aboutsummaryrefslogtreecommitdiff
path: root/arch/parisc/lib
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/parisc/lib
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/parisc/lib')
-rw-r--r--arch/parisc/lib/Makefile9
-rw-r--r--arch/parisc/lib/bitops.c84
-rw-r--r--arch/parisc/lib/checksum.c148
-rw-r--r--arch/parisc/lib/debuglocks.c277
-rw-r--r--arch/parisc/lib/fixup.S89
-rw-r--r--arch/parisc/lib/io.c488
-rw-r--r--arch/parisc/lib/iomap.c422
-rw-r--r--arch/parisc/lib/lusercopy.S193
-rw-r--r--arch/parisc/lib/memcpy.c522
-rw-r--r--arch/parisc/lib/memset.c91
10 files changed, 2323 insertions, 0 deletions
diff --git a/arch/parisc/lib/Makefile b/arch/parisc/lib/Makefile
new file mode 100644
index 00000000000..7bf70567629
--- /dev/null
+++ b/arch/parisc/lib/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for parisc-specific library files
+#
+
+lib-y := lusercopy.o bitops.o checksum.o io.o memset.o fixup.o memcpy.o
+
+obj-y := iomap.o
+
+lib-$(CONFIG_SMP) += debuglocks.o
diff --git a/arch/parisc/lib/bitops.c b/arch/parisc/lib/bitops.c
new file mode 100644
index 00000000000..2de182f6fe8
--- /dev/null
+++ b/arch/parisc/lib/bitops.c
@@ -0,0 +1,84 @@
+/*
+ * bitops.c: atomic operations which got too long to be inlined all over
+ * the place.
+ *
+ * Copyright 1999 Philipp Rumpf (prumpf@tux.org)
+ * Copyright 2000 Grant Grundler (grundler@cup.hp.com)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <asm/system.h>
+#include <asm/atomic.h>
+
+#ifdef CONFIG_SMP
+spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] __lock_aligned = {
+ [0 ... (ATOMIC_HASH_SIZE-1)] = SPIN_LOCK_UNLOCKED
+};
+#endif
+
+#ifdef __LP64__
+unsigned long __xchg64(unsigned long x, unsigned long *ptr)
+{
+ unsigned long temp, flags;
+
+ _atomic_spin_lock_irqsave(ptr, flags);
+ temp = *ptr;
+ *ptr = x;
+ _atomic_spin_unlock_irqrestore(ptr, flags);
+ return temp;
+}
+#endif
+
+unsigned long __xchg32(int x, int *ptr)
+{
+ unsigned long flags;
+ long temp;
+
+ _atomic_spin_lock_irqsave(ptr, flags);
+ temp = (long) *ptr; /* XXX - sign extension wanted? */
+ *ptr = x;
+ _atomic_spin_unlock_irqrestore(ptr, flags);
+ return (unsigned long)temp;
+}
+
+
+unsigned long __xchg8(char x, char *ptr)
+{
+ unsigned long flags;
+ long temp;
+
+ _atomic_spin_lock_irqsave(ptr, flags);
+ temp = (long) *ptr; /* XXX - sign extension wanted? */
+ *ptr = x;
+ _atomic_spin_unlock_irqrestore(ptr, flags);
+ return (unsigned long)temp;
+}
+
+
+#ifdef __LP64__
+unsigned long __cmpxchg_u64(volatile unsigned long *ptr, unsigned long old, unsigned long new)
+{
+ unsigned long flags;
+ unsigned long prev;
+
+ _atomic_spin_lock_irqsave(ptr, flags);
+ if ((prev = *ptr) == old)
+ *ptr = new;
+ _atomic_spin_unlock_irqrestore(ptr, flags);
+ return prev;
+}
+#endif
+
+unsigned long __cmpxchg_u32(volatile unsigned int *ptr, unsigned int old, unsigned int new)
+{
+ unsigned long flags;
+ unsigned int prev;
+
+ _atomic_spin_lock_irqsave(ptr, flags);
+ if ((prev = *ptr) == old)
+ *ptr = new;
+ _atomic_spin_unlock_irqrestore(ptr, flags);
+ return (unsigned long)prev;
+}
diff --git a/arch/parisc/lib/checksum.c b/arch/parisc/lib/checksum.c
new file mode 100644
index 00000000000..8a1e08068e7
--- /dev/null
+++ b/arch/parisc/lib/checksum.c
@@ -0,0 +1,148 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * MIPS specific IP/TCP/UDP checksumming routines
+ *
+ * Authors: Ralf Baechle, <ralf@waldorf-gmbh.de>
+ * Lots of code moved from tcp.c and ip.c; see those files
+ * for more names.
+ *
+ * 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.
+ *
+ * $Id: checksum.c,v 1.3 1997/12/01 17:57:34 ralf Exp $
+ */
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <net/checksum.h>
+#include <asm/byteorder.h>
+#include <asm/string.h>
+#include <asm/uaccess.h>
+
+#define addc(_t,_r) \
+ __asm__ __volatile__ ( \
+" add %0, %1, %0\n" \
+" addc %0, %%r0, %0\n" \
+ : "=r"(_t) \
+ : "r"(_r), "0"(_t));
+
+static inline unsigned short from32to16(unsigned int x)
+{
+ /* 32 bits --> 16 bits + carry */
+ x = (x & 0xffff) + (x >> 16);
+ /* 16 bits + carry --> 16 bits including carry */
+ x = (x & 0xffff) + (x >> 16);
+ return (unsigned short)x;
+}
+
+static inline unsigned int do_csum(const unsigned char * buff, int len)
+{
+ int odd, count;
+ unsigned int result = 0;
+
+ if (len <= 0)
+ goto out;
+ odd = 1 & (unsigned long) buff;
+ if (odd) {
+ result = be16_to_cpu(*buff);
+ len--;
+ buff++;
+ }
+ count = len >> 1; /* nr of 16-bit words.. */
+ if (count) {
+ if (2 & (unsigned long) buff) {
+ result += *(unsigned short *) buff;
+ count--;
+ len -= 2;
+ buff += 2;
+ }
+ count >>= 1; /* nr of 32-bit words.. */
+ if (count) {
+ while (count >= 4) {
+ unsigned int r1, r2, r3, r4;
+ r1 = *(unsigned int *)(buff + 0);
+ r2 = *(unsigned int *)(buff + 4);
+ r3 = *(unsigned int *)(buff + 8);
+ r4 = *(unsigned int *)(buff + 12);
+ addc(result, r1);
+ addc(result, r2);
+ addc(result, r3);
+ addc(result, r4);
+ count -= 4;
+ buff += 16;
+ }
+ while (count) {
+ unsigned int w = *(unsigned int *) buff;
+ count--;
+ buff += 4;
+ addc(result, w);
+ }
+ result = (result & 0xffff) + (result >> 16);
+ }
+ if (len & 2) {
+ result += *(unsigned short *) buff;
+ buff += 2;
+ }
+ }
+ if (len & 1)
+ result += le16_to_cpu(*buff);
+ result = from32to16(result);
+ if (odd)
+ result = swab16(result);
+out:
+ return result;
+}
+
+/*
+ * computes a partial checksum, e.g. for TCP/UDP fragments
+ */
+unsigned int csum_partial(const unsigned char *buff, int len, unsigned int sum)
+{
+ unsigned int result = do_csum(buff, len);
+ addc(result, sum);
+ return from32to16(result);
+}
+
+EXPORT_SYMBOL(csum_partial);
+
+/*
+ * copy while checksumming, otherwise like csum_partial
+ */
+unsigned int csum_partial_copy_nocheck(const unsigned char *src, unsigned char *dst,
+ int len, unsigned int sum)
+{
+ /*
+ * It's 2:30 am and I don't feel like doing it real ...
+ * This is lots slower than the real thing (tm)
+ */
+ sum = csum_partial(src, len, sum);
+ memcpy(dst, src, len);
+
+ return sum;
+}
+EXPORT_SYMBOL(csum_partial_copy_nocheck);
+
+/*
+ * Copy from userspace and compute checksum. If we catch an exception
+ * then zero the rest of the buffer.
+ */
+unsigned int csum_partial_copy_from_user(const unsigned char __user *src,
+ unsigned char *dst, int len,
+ unsigned int sum, int *err_ptr)
+{
+ int missing;
+
+ missing = copy_from_user(dst, src, len);
+ if (missing) {
+ memset(dst + len - missing, 0, missing);
+ *err_ptr = -EFAULT;
+ }
+
+ return csum_partial(dst, len, sum);
+}
+EXPORT_SYMBOL(csum_partial_copy_from_user);
diff --git a/arch/parisc/lib/debuglocks.c b/arch/parisc/lib/debuglocks.c
new file mode 100644
index 00000000000..1b33fe6e5b7
--- /dev/null
+++ b/arch/parisc/lib/debuglocks.c
@@ -0,0 +1,277 @@
+/*
+ * Debugging versions of SMP locking primitives.
+ *
+ * Copyright (C) 2004 Thibaut VARENE <varenet@parisc-linux.org>
+ *
+ * Some code stollen from alpha & sparc64 ;)
+ *
+ * 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
+ *
+ * We use pdc_printf() throughout the file for all output messages, to avoid
+ * losing messages because of disabled interrupts. Since we're using these
+ * messages for debugging purposes, it makes sense not to send them to the
+ * linux console.
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/hardirq.h> /* in_interrupt() */
+#include <asm/system.h>
+#include <asm/hardirq.h> /* in_interrupt() */
+#include <asm/pdc.h>
+
+#undef INIT_STUCK
+#define INIT_STUCK 1L << 30
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+
+
+void _dbg_spin_lock(spinlock_t * lock, const char *base_file, int line_no)
+{
+ volatile unsigned int *a;
+ long stuck = INIT_STUCK;
+ void *inline_pc = __builtin_return_address(0);
+ unsigned long started = jiffies;
+ int printed = 0;
+ int cpu = smp_processor_id();
+
+try_again:
+
+ /* Do the actual locking */
+ /* <T-Bone> ggg: we can't get stuck on the outter loop?
+ * <ggg> T-Bone: We can hit the outer loop
+ * alot if multiple CPUs are constantly racing for a lock
+ * and the backplane is NOT fair about which CPU sees
+ * the update first. But it won't hang since every failed
+ * attempt will drop us back into the inner loop and
+ * decrement `stuck'.
+ * <ggg> K-class and some of the others are NOT fair in the HW
+ * implementation so we could see false positives.
+ * But fixing the lock contention is easier than
+ * fixing the HW to be fair.
+ * <tausq> __ldcw() returns 1 if we get the lock; otherwise we
+ * spin until the value of the lock changes, or we time out.
+ */
+ mb();
+ a = __ldcw_align(lock);
+ while (stuck && (__ldcw(a) == 0))
+ while ((*a == 0) && --stuck);
+ mb();
+
+ if (unlikely(stuck <= 0)) {
+ pdc_printf(
+ "%s:%d: spin_lock(%s/%p) stuck in %s at %p(%d)"
+ " owned by %s:%d in %s at %p(%d)\n",
+ base_file, line_no, lock->module, lock,
+ current->comm, inline_pc, cpu,
+ lock->bfile, lock->bline, lock->task->comm,
+ lock->previous, lock->oncpu);
+ stuck = INIT_STUCK;
+ printed = 1;
+ goto try_again;
+ }
+
+ /* Exiting. Got the lock. */
+ lock->oncpu = cpu;
+ lock->previous = inline_pc;
+ lock->task = current;
+ lock->bfile = (char *)base_file;
+ lock->bline = line_no;
+
+ if (unlikely(printed)) {
+ pdc_printf(
+ "%s:%d: spin_lock grabbed in %s at %p(%d) %ld ticks\n",
+ base_file, line_no, current->comm, inline_pc,
+ cpu, jiffies - started);
+ }
+}
+
+void _dbg_spin_unlock(spinlock_t * lock, const char *base_file, int line_no)
+{
+ CHECK_LOCK(lock);
+ volatile unsigned int *a;
+ mb();
+ a = __ldcw_align(lock);
+ if (unlikely((*a != 0) && lock->babble)) {
+ lock->babble--;
+ pdc_printf(
+ "%s:%d: spin_unlock(%s:%p) not locked\n",
+ base_file, line_no, lock->module, lock);
+ }
+ *a = 1;
+ mb();
+}
+
+int _dbg_spin_trylock(spinlock_t * lock, const char *base_file, int line_no)
+{
+ int ret;
+ volatile unsigned int *a;
+ mb();
+ a = __ldcw_align(lock);
+ ret = (__ldcw(a) != 0);
+ mb();
+ if (ret) {
+ lock->oncpu = smp_processor_id();
+ lock->previous = __builtin_return_address(0);
+ lock->task = current;
+ } else {
+ lock->bfile = (char *)base_file;
+ lock->bline = line_no;
+ }
+ return ret;
+}
+
+#endif /* CONFIG_DEBUG_SPINLOCK */
+
+#ifdef CONFIG_DEBUG_RWLOCK
+
+/* Interrupts trouble detailed explanation, thx Grant:
+ *
+ * o writer (wants to modify data) attempts to acquire the rwlock
+ * o He gets the write lock.
+ * o Interupts are still enabled, we take an interrupt with the
+ * write still holding the lock.
+ * o interrupt handler tries to acquire the rwlock for read.
+ * o deadlock since the writer can't release it at this point.
+ *
+ * In general, any use of spinlocks that competes between "base"
+ * level and interrupt level code will risk deadlock. Interrupts
+ * need to be disabled in the base level routines to avoid it.
+ * Or more precisely, only the IRQ the base level routine
+ * is competing with for the lock. But it's more efficient/faster
+ * to just disable all interrupts on that CPU to guarantee
+ * once it gets the lock it can release it quickly too.
+ */
+
+void _dbg_write_lock(rwlock_t *rw, const char *bfile, int bline)
+{
+ void *inline_pc = __builtin_return_address(0);
+ unsigned long started = jiffies;
+ long stuck = INIT_STUCK;
+ int printed = 0;
+ int cpu = smp_processor_id();
+
+ if(unlikely(in_interrupt())) { /* acquiring write lock in interrupt context, bad idea */
+ pdc_printf("write_lock caller: %s:%d, IRQs enabled,\n", bfile, bline);
+ BUG();
+ }
+
+ /* Note: if interrupts are disabled (which is most likely), the printk
+ will never show on the console. We might need a polling method to flush
+ the dmesg buffer anyhow. */
+
+retry:
+ _raw_spin_lock(&rw->lock);
+
+ if(rw->counter != 0) {
+ /* this basically never happens */
+ _raw_spin_unlock(&rw->lock);
+
+ stuck--;
+ if ((unlikely(stuck <= 0)) && (rw->counter < 0)) {
+ pdc_printf(
+ "%s:%d: write_lock stuck on writer"
+ " in %s at %p(%d) %ld ticks\n",
+ bfile, bline, current->comm, inline_pc,
+ cpu, jiffies - started);
+ stuck = INIT_STUCK;
+ printed = 1;
+ }
+ else if (unlikely(stuck <= 0)) {
+ pdc_printf(
+ "%s:%d: write_lock stuck on reader"
+ " in %s at %p(%d) %ld ticks\n",
+ bfile, bline, current->comm, inline_pc,
+ cpu, jiffies - started);
+ stuck = INIT_STUCK;
+ printed = 1;
+ }
+
+ while(rw->counter != 0);
+
+ goto retry;
+ }
+
+ /* got it. now leave without unlocking */
+ rw->counter = -1; /* remember we are locked */
+
+ if (unlikely(printed)) {
+ pdc_printf(
+ "%s:%d: write_lock grabbed in %s at %p(%d) %ld ticks\n",
+ bfile, bline, current->comm, inline_pc,
+ cpu, jiffies - started);
+ }
+}
+
+int _dbg_write_trylock(rwlock_t *rw, const char *bfile, int bline)
+{
+#if 0
+ void *inline_pc = __builtin_return_address(0);
+ int cpu = smp_processor_id();
+#endif
+
+ if(unlikely(in_interrupt())) { /* acquiring write lock in interrupt context, bad idea */
+ pdc_printf("write_lock caller: %s:%d, IRQs enabled,\n", bfile, bline);
+ BUG();
+ }
+
+ /* Note: if interrupts are disabled (which is most likely), the printk
+ will never show on the console. We might need a polling method to flush
+ the dmesg buffer anyhow. */
+
+ _raw_spin_lock(&rw->lock);
+
+ if(rw->counter != 0) {
+ /* this basically never happens */
+ _raw_spin_unlock(&rw->lock);
+ return 0;
+ }
+
+ /* got it. now leave without unlocking */
+ rw->counter = -1; /* remember we are locked */
+#if 0
+ pdc_printf("%s:%d: try write_lock grabbed in %s at %p(%d)\n",
+ bfile, bline, current->comm, inline_pc, cpu);
+#endif
+ return 1;
+}
+
+void _dbg_read_lock(rwlock_t * rw, const char *bfile, int bline)
+{
+#if 0
+ void *inline_pc = __builtin_return_address(0);
+ unsigned long started = jiffies;
+ int cpu = smp_processor_id();
+#endif
+ unsigned long flags;
+
+ local_irq_save(flags);
+ _raw_spin_lock(&rw->lock);
+
+ rw->counter++;
+#if 0
+ pdc_printf(
+ "%s:%d: read_lock grabbed in %s at %p(%d) %ld ticks\n",
+ bfile, bline, current->comm, inline_pc,
+ cpu, jiffies - started);
+#endif
+ _raw_spin_unlock(&rw->lock);
+ local_irq_restore(flags);
+}
+
+#endif /* CONFIG_DEBUG_RWLOCK */
diff --git a/arch/parisc/lib/fixup.S b/arch/parisc/lib/fixup.S
new file mode 100644
index 00000000000..134f0cd240f
--- /dev/null
+++ b/arch/parisc/lib/fixup.S
@@ -0,0 +1,89 @@
+/*
+ * Linux/PA-RISC Project (http://www.parisc-linux.org/)
+ *
+ * Copyright (C) 2004 Randolph Chung <tausq@debian.org>
+ *
+ * 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; 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.
+ *
+ * Fixup routines for kernel exception handling.
+ */
+#include <linux/config.h>
+#include <asm/offsets.h>
+#include <asm/assembly.h>
+#include <asm/errno.h>
+
+#ifdef CONFIG_SMP
+ .macro get_fault_ip t1 t2
+ addil LT%__per_cpu_offset,%r27
+ LDREG RT%__per_cpu_offset(%r1),\t1
+ /* t2 = smp_processor_id() */
+ mfctl 30,\t2
+ ldw TI_CPU(\t2),\t2
+#ifdef __LP64__
+ extrd,u \t2,63,32,\t2
+#endif
+ /* t2 = &__per_cpu_offset[smp_processor_id()]; */
+ LDREG,s \t2(\t1),\t2
+ addil LT%per_cpu__exception_data,%r27
+ LDREG RT%per_cpu__exception_data(%r1),\t1
+ /* t1 = &__get_cpu_var(exception_data) */
+ add,l \t1,\t2,\t1
+ /* t1 = t1->fault_ip */
+ LDREG EXCDATA_IP(\t1), \t1
+ .endm
+#else
+ .macro get_fault_ip t1 t2
+ /* t1 = &__get_cpu_var(exception_data) */
+ addil LT%per_cpu__exception_data,%r27
+ LDREG RT%per_cpu__exception_data(%r1),\t2
+ /* t1 = t2->fault_ip */
+ LDREG EXCDATA_IP(\t2), \t1
+ .endm
+#endif
+
+ .text
+ .section .fixup, "ax"
+
+ /* get_user() fixups, store -EFAULT in r8, and 0 in r9 */
+ .export fixup_get_user_skip_1
+fixup_get_user_skip_1:
+ get_fault_ip %r1,%r8
+ ldo 4(%r1), %r1
+ ldi -EFAULT, %r8
+ bv %r0(%r1)
+ copy %r0, %r9
+
+ .export fixup_get_user_skip_2
+fixup_get_user_skip_2:
+ get_fault_ip %r1,%r8
+ ldo 8(%r1), %r1
+ ldi -EFAULT, %r8
+ bv %r0(%r1)
+ copy %r0, %r9
+
+ /* put_user() fixups, store -EFAULT in r8 */
+ .export fixup_put_user_skip_1
+fixup_put_user_skip_1:
+ get_fault_ip %r1,%r8
+ ldo 4(%r1), %r1
+ bv %r0(%r1)
+ ldi -EFAULT, %r8
+
+ .export fixup_put_user_skip_2
+fixup_put_user_skip_2:
+ get_fault_ip %r1,%r8
+ ldo 8(%r1), %r1
+ bv %r0(%r1)
+ ldi -EFAULT, %r8
diff --git a/arch/parisc/lib/io.c b/arch/parisc/lib/io.c
new file mode 100644
index 00000000000..7c1406ff825
--- /dev/null
+++ b/arch/parisc/lib/io.c
@@ -0,0 +1,488 @@
+/*
+ * arch/parisc/lib/io.c
+ *
+ * Copyright (c) Matthew Wilcox 2001 for Hewlett-Packard
+ * Copyright (c) Randolph Chung 2001 <tausq@debian.org>
+ *
+ * IO accessing functions which shouldn't be inlined because they're too big
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/io.h>
+
+/* Copies a block of memory to a device in an efficient manner.
+ * Assumes the device can cope with 32-bit transfers. If it can't,
+ * don't use this function.
+ */
+void memcpy_toio(volatile void __iomem *dst, const void *src, int count)
+{
+ if (((unsigned long)dst & 3) != ((unsigned long)src & 3))
+ goto bytecopy;
+ while ((unsigned long)dst & 3) {
+ writeb(*(char *)src, dst++);
+ src++;
+ count--;
+ }
+ while (count > 3) {
+ __raw_writel(*(u32 *)src, dst);
+ src += 4;
+ dst += 4;
+ count -= 4;
+ }
+ bytecopy:
+ while (count--) {
+ writeb(*(char *)src, dst++);
+ src++;
+ }
+}
+
+/*
+** Copies a block of memory from a device in an efficient manner.
+** Assumes the device can cope with 32-bit transfers. If it can't,
+** don't use this function.
+**
+** CR16 counts on C3000 reading 256 bytes from Symbios 896 RAM:
+** 27341/64 = 427 cyc per int
+** 61311/128 = 478 cyc per short
+** 122637/256 = 479 cyc per byte
+** Ergo bus latencies dominant (not transfer size).
+** Minimize total number of transfers at cost of CPU cycles.
+** TODO: only look at src alignment and adjust the stores to dest.
+*/
+void memcpy_fromio(void *dst, const volatile void __iomem *src, int count)
+{
+ /* first compare alignment of src/dst */
+ if ( (((unsigned long)dst ^ (unsigned long)src) & 1) || (count < 2) )
+ goto bytecopy;
+
+ if ( (((unsigned long)dst ^ (unsigned long)src) & 2) || (count < 4) )
+ goto shortcopy;
+
+ /* Then check for misaligned start address */
+ if ((unsigned long)src & 1) {
+ *(u8 *)dst = readb(src);
+ src++;
+ dst++;
+ count--;
+ if (count < 2) goto bytecopy;
+ }
+
+ if ((unsigned long)src & 2) {
+ *(u16 *)dst = __raw_readw(src);
+ src += 2;
+ dst += 2;
+ count -= 2;
+ }
+
+ while (count > 3) {
+ *(u32 *)dst = __raw_readl(src);
+ dst += 4;
+ src += 4;
+ count -= 4;
+ }
+
+ shortcopy:
+ while (count > 1) {
+ *(u16 *)dst = __raw_readw(src);
+ src += 2;
+ dst += 2;
+ count -= 2;
+ }
+
+ bytecopy:
+ while (count--) {
+ *(char *)dst = readb(src);
+ src++;
+ dst++;
+ }
+}
+
+/* Sets a block of memory on a device to a given value.
+ * Assumes the device can cope with 32-bit transfers. If it can't,
+ * don't use this function.
+ */
+void memset_io(volatile void __iomem *addr, unsigned char val, int count)
+{
+ u32 val32 = (val << 24) | (val << 16) | (val << 8) | val;
+ while ((unsigned long)addr & 3) {
+ writeb(val, addr++);
+ count--;
+ }
+ while (count > 3) {
+ __raw_writel(val32, addr);
+ addr += 4;
+ count -= 4;
+ }
+ while (count--) {
+ writeb(val, addr++);
+ }
+}
+
+/*
+ * Read COUNT 8-bit bytes from port PORT into memory starting at
+ * SRC.
+ */
+void insb (unsigned long port, void *dst, unsigned long count)
+{
+ unsigned char *p;
+
+ p = (unsigned char *)dst;
+
+ while (((unsigned long)p) & 0x3) {
+ if (!count)
+ return;
+ count--;
+ *p = inb(port);
+ p++;
+ }
+
+ while (count >= 4) {
+ unsigned int w;
+ count -= 4;
+ w = inb(port) << 24;
+ w |= inb(port) << 16;
+ w |= inb(port) << 8;
+ w |= inb(port);
+ *(unsigned int *) p = w;
+ p += 4;
+ }
+
+ while (count) {
+ --count;
+ *p = inb(port);
+ p++;
+ }
+}
+
+
+/*
+ * Read COUNT 16-bit words from port PORT into memory starting at
+ * SRC. SRC must be at least short aligned. This is used by the
+ * IDE driver to read disk sectors. Performance is important, but
+ * the interfaces seems to be slow: just using the inlined version
+ * of the inw() breaks things.
+ */
+void insw (unsigned long port, void *dst, unsigned long count)
+{
+ unsigned int l = 0, l2;
+ unsigned char *p;
+
+ p = (unsigned char *)dst;
+
+ if (!count)
+ return;
+
+ switch (((unsigned long)p) & 0x3)
+ {
+ case 0x00: /* Buffer 32-bit aligned */
+ while (count>=2) {
+
+ count -= 2;
+ l = cpu_to_le16(inw(port)) << 16;
+ l |= cpu_to_le16(inw(port));
+ *(unsigned int *)p = l;
+ p += 4;
+ }
+ if (count) {
+ *(unsigned short *)p = cpu_to_le16(inw(port));
+ }
+ break;
+
+ case 0x02: /* Buffer 16-bit aligned */
+ *(unsigned short *)p = cpu_to_le16(inw(port));
+ p += 2;
+ count--;
+ while (count>=2) {
+
+ count -= 2;
+ l = cpu_to_le16(inw(port)) << 16;
+ l |= cpu_to_le16(inw(port));
+ *(unsigned int *)p = l;
+ p += 4;
+ }
+ if (count) {
+ *(unsigned short *)p = cpu_to_le16(inw(port));
+ }
+ break;
+
+ case 0x01: /* Buffer 8-bit aligned */
+ case 0x03:
+ /* I don't bother with 32bit transfers
+ * in this case, 16bit will have to do -- DE */
+ --count;
+
+ l = cpu_to_le16(inw(port));
+ *p = l >> 8;
+ p++;
+ while (count--)
+ {
+ l2 = cpu_to_le16(inw(port));
+ *(unsigned short *)p = (l & 0xff) << 8 | (l2 >> 8);
+ p += 2;
+ l = l2;
+ }
+ *p = l & 0xff;
+ break;
+ }
+}
+
+
+
+/*
+ * Read COUNT 32-bit words from port PORT into memory starting at
+ * SRC. Now works with any alignment in SRC. Performance is important,
+ * but the interfaces seems to be slow: just using the inlined version
+ * of the inl() breaks things.
+ */
+void insl (unsigned long port, void *dst, unsigned long count)
+{
+ unsigned int l = 0, l2;
+ unsigned char *p;
+
+ p = (unsigned char *)dst;
+
+ if (!count)
+ return;
+
+ switch (((unsigned long) dst) & 0x3)
+ {
+ case 0x00: /* Buffer 32-bit aligned */
+ while (count--)
+ {
+ *(unsigned int *)p = cpu_to_le32(inl(port));
+ p += 4;
+ }
+ break;
+
+ case 0x02: /* Buffer 16-bit aligned */
+ --count;
+
+ l = cpu_to_le32(inl(port));
+ *(unsigned short *)p = l >> 16;
+ p += 2;
+
+ while (count--)
+ {
+ l2 = cpu_to_le32(inl(port));
+ *(unsigned int *)p = (l & 0xffff) << 16 | (l2 >> 16);
+ p += 4;
+ l = l2;
+ }
+ *(unsigned short *)p = l & 0xffff;
+ break;
+ case 0x01: /* Buffer 8-bit aligned */
+ --count;
+
+ l = cpu_to_le32(inl(port));
+ *(unsigned char *)p = l >> 24;
+ p++;
+ *(unsigned short *)p = (l >> 8) & 0xffff;
+ p += 2;
+ while (count--)
+ {
+ l2 = cpu_to_le32(inl(port));
+ *(unsigned int *)p = (l & 0xff) << 24 | (l2 >> 8);
+ p += 4;
+ l = l2;
+ }
+ *p = l & 0xff;
+ break;
+ case 0x03: /* Buffer 8-bit aligned */
+ --count;
+
+ l = cpu_to_le32(inl(port));
+ *p = l >> 24;
+ p++;
+ while (count--)
+ {
+ l2 = cpu_to_le32(inl(port));
+ *(unsigned int *)p = (l & 0xffffff) << 8 | l2 >> 24;
+ p += 4;
+ l = l2;
+ }
+ *(unsigned short *)p = (l >> 8) & 0xffff;
+ p += 2;
+ *p = l & 0xff;
+ break;
+ }
+}
+
+
+/*
+ * Like insb but in the opposite direction.
+ * Don't worry as much about doing aligned memory transfers:
+ * doing byte reads the "slow" way isn't nearly as slow as
+ * doing byte writes the slow way (no r-m-w cycle).
+ */
+void outsb(unsigned long port, const void * src, unsigned long count)
+{
+ const unsigned char *p;
+
+ p = (const unsigned char *)src;
+ while (count) {
+ count--;
+ outb(*p, port);
+ p++;
+ }
+}
+
+/*
+ * Like insw but in the opposite direction. This is used by the IDE
+ * driver to write disk sectors. Performance is important, but the
+ * interfaces seems to be slow: just using the inlined version of the
+ * outw() breaks things.
+ */
+void outsw (unsigned long port, const void *src, unsigned long count)
+{
+ unsigned int l = 0, l2;
+ const unsigned char *p;
+
+ p = (const unsigned char *)src;
+
+ if (!count)
+ return;
+
+ switch (((unsigned long)p) & 0x3)
+ {
+ case 0x00: /* Buffer 32-bit aligned */
+ while (count>=2) {
+ count -= 2;
+ l = *(unsigned int *)p;
+ p += 4;
+ outw(le16_to_cpu(l >> 16), port);
+ outw(le16_to_cpu(l & 0xffff), port);
+ }
+ if (count) {
+ outw(le16_to_cpu(*(unsigned short*)p), port);
+ }
+ break;
+
+ case 0x02: /* Buffer 16-bit aligned */
+
+ outw(le16_to_cpu(*(unsigned short*)p), port);
+ p += 2;
+ count--;
+
+ while (count>=2) {
+ count -= 2;
+ l = *(unsigned int *)p;
+ p += 4;
+ outw(le16_to_cpu(l >> 16), port);
+ outw(le16_to_cpu(l & 0xffff), port);
+ }
+ if (count) {
+ outw(le16_to_cpu(*(unsigned short *)p), port);
+ }
+ break;
+
+ case 0x01: /* Buffer 8-bit aligned */
+ /* I don't bother with 32bit transfers
+ * in this case, 16bit will have to do -- DE */
+
+ l = *p << 8;
+ p++;
+ count--;
+ while (count)
+ {
+ count--;
+ l2 = *(unsigned short *)p;
+ p += 2;
+ outw(le16_to_cpu(l | l2 >> 8), port);
+ l = l2 << 8;
+ }
+ l2 = *(unsigned char *)p;
+ outw (le16_to_cpu(l | l2>>8), port);
+ break;
+
+ }
+}
+
+
+/*
+ * Like insl but in the opposite direction. This is used by the IDE
+ * driver to write disk sectors. Works with any alignment in SRC.
+ * Performance is important, but the interfaces seems to be slow:
+ * just using the inlined version of the outl() breaks things.
+ */
+void outsl (unsigned long port, const void *src, unsigned long count)
+{
+ unsigned int l = 0, l2;
+ const unsigned char *p;
+
+ p = (const unsigned char *)src;
+
+ if (!count)
+ return;
+
+ switch (((unsigned long)p) & 0x3)
+ {
+ case 0x00: /* Buffer 32-bit aligned */
+ while (count--)
+ {
+ outl(le32_to_cpu(*(unsigned int *)p), port);
+ p += 4;
+ }
+ break;
+
+ case 0x02: /* Buffer 16-bit aligned */
+ --count;
+
+ l = *(unsigned short *)p;
+ p += 2;
+
+ while (count--)
+ {
+ l2 = *(unsigned int *)p;
+ p += 4;
+ outl (le32_to_cpu(l << 16 | l2 >> 16), port);
+ l = l2;
+ }
+ l2 = *(unsigned short *)p;
+ outl (le32_to_cpu(l << 16 | l2), port);
+ break;
+ case 0x01: /* Buffer 8-bit aligned */
+ --count;
+
+ l = *p << 24;
+ p++;
+ l |= *(unsigned short *)p << 8;
+ p += 2;
+
+ while (count--)
+ {
+ l2 = *(unsigned int *)p;
+ p += 4;
+ outl (le32_to_cpu(l | l2 >> 24), port);
+ l = l2 << 8;
+ }
+ l2 = *p;
+ outl (le32_to_cpu(l | l2), port);
+ break;
+ case 0x03: /* Buffer 8-bit aligned */
+ --count;
+
+ l = *p << 24;
+ p++;
+
+ while (count--)
+ {
+ l2 = *(unsigned int *)p;
+ p += 4;
+ outl (le32_to_cpu(l | l2 >> 8), port);
+ l = l2 << 24;
+ }
+ l2 = *(unsigned short *)p << 16;
+ p += 2;
+ l2 |= *p;
+ outl (le32_to_cpu(l | l2), port);
+ break;
+ }
+}
+
+EXPORT_SYMBOL(insb);
+EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(insl);
+EXPORT_SYMBOL(outsb);
+EXPORT_SYMBOL(outsw);
+EXPORT_SYMBOL(outsl);
diff --git a/arch/parisc/lib/iomap.c b/arch/parisc/lib/iomap.c
new file mode 100644
index 00000000000..290a62e7120
--- /dev/null
+++ b/arch/parisc/lib/iomap.c
@@ -0,0 +1,422 @@
+/*
+ * iomap.c - Implement iomap interface for PA-RISC
+ * Copyright (c) 2004 Matthew Wilcox
+ */
+
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+/*
+ * The iomap space on 32-bit PA-RISC is intended to look like this:
+ * 00000000-7fffffff virtual mapped IO
+ * 80000000-8fffffff ISA/EISA port space that can't be virtually mapped
+ * 90000000-9fffffff Dino port space