Преглед на файлове

Merge with /shiny/git/linux-2.6/.git

David Woodhouse преди 20 години
родител
ревизия
c973b112c7
променени са 100 файла, в които са добавени 11620 реда и са изтрити 1453 реда
  1. 5 5
      CREDITS
  2. 1 0
      Documentation/Changes
  3. 5 0
      Documentation/SubmittingPatches
  4. 1 0
      Documentation/dontdiff
  5. 15 1
      Documentation/fb/vesafb.txt
  6. 114 0
      Documentation/infiniband/core_locking.txt
  7. 40 13
      Documentation/infiniband/user_mad.txt
  8. 588 0
      Documentation/kprobes.txt
  9. 568 242
      Documentation/networking/bonding.txt
  10. 9 0
      Documentation/pcmcia/driver-changes.txt
  11. 20 24
      Documentation/sound/alsa/ALSA-Configuration.txt
  12. 1 1
      Documentation/stable_api_nonsense.txt
  13. 58 0
      Documentation/stable_kernel_rules.txt
  14. 1 1
      Documentation/usb/usbmon.txt
  15. 1 0
      Documentation/video4linux/CARDLIST.cx88
  16. 2 0
      Documentation/video4linux/CARDLIST.tuner
  17. 3 0
      Documentation/video4linux/bttv/Insmod-options
  18. 11 4
      Documentation/x86_64/boot-options.txt
  19. 8 2
      MAINTAINERS
  20. 1 1
      Makefile
  21. 10 0
      README
  22. 11 10
      REPORTING-BUGS
  23. 16 0
      arch/alpha/kernel/pci.c
  24. 5 0
      arch/alpha/kernel/systbls.S
  25. 17 0
      arch/arm/kernel/bios32.c
  26. 1 0
      arch/arm/kernel/smp.c
  27. 4 4
      arch/arm/lib/bitops.h
  28. 2 0
      arch/arm/mach-integrator/platsmp.c
  29. 1 1
      arch/arm/mach-ixp4xx/coyote-setup.c
  30. 1 1
      arch/arm/mach-ixp4xx/gtwx5715-setup.c
  31. 2 2
      arch/arm/mach-ixp4xx/ixdp425-setup.c
  32. 15 1
      arch/arm/mach-s3c2410/mach-bast.c
  33. 1 0
      arch/arm/mach-sa1100/jornada720.c
  34. 3 3
      arch/arm/mm/fault.c
  35. 0 136
      arch/arm/mm/proc-xscale.S
  36. 12 12
      arch/arm/nwfpe/double_cpdo.c
  37. 12 12
      arch/arm/nwfpe/extended_cpdo.c
  38. 11 19
      arch/arm/nwfpe/fpa11.c
  39. 9 2
      arch/arm/nwfpe/fpa11.h
  40. 17 11
      arch/arm/nwfpe/fpa11_cpdo.c
  41. 14 8
      arch/arm/nwfpe/fpa11_cpdt.c
  42. 19 9
      arch/arm/nwfpe/fpa11_cprt.c
  43. 6 9
      arch/arm/nwfpe/fpmodule.c
  44. 12 12
      arch/arm/nwfpe/single_cpdo.c
  45. 123 132
      arch/arm/nwfpe/softfloat.c
  46. 29 39
      arch/arm/nwfpe/softfloat.h
  47. 1 1
      arch/arm/oprofile/backtrace.c
  48. 3 0
      arch/arm/vfp/vfpdouble.c
  49. 7 10
      arch/arm26/mm/fault.c
  50. 5 0
      arch/cris/Kconfig.debug
  51. 45 21
      arch/cris/Makefile
  52. 31 0
      arch/cris/arch-v10/Kconfig
  53. 5 4
      arch/cris/arch-v10/boot/Makefile
  54. 24 19
      arch/cris/arch-v10/boot/compressed/Makefile
  55. 19 3
      arch/cris/arch-v10/boot/compressed/head.S
  56. 23 22
      arch/cris/arch-v10/boot/rescue/Makefile
  57. 27 6
      arch/cris/arch-v10/boot/rescue/head.S
  58. 67 183
      arch/cris/arch-v10/drivers/Kconfig
  59. 4 1
      arch/cris/arch-v10/drivers/axisflashmap.c
  60. 49 22
      arch/cris/arch-v10/drivers/ds1302.c
  61. 14 15
      arch/cris/arch-v10/drivers/eeprom.c
  62. 106 95
      arch/cris/arch-v10/drivers/gpio.c
  63. 55 7
      arch/cris/arch-v10/drivers/i2c.c
  64. 16 4
      arch/cris/arch-v10/drivers/pcf8563.c
  65. 3 2
      arch/cris/arch-v10/kernel/Makefile
  66. 196 77
      arch/cris/arch-v10/kernel/debugport.c
  67. 287 0
      arch/cris/arch-v10/kernel/dma.c
  68. 36 18
      arch/cris/arch-v10/kernel/entry.S
  69. 29 26
      arch/cris/arch-v10/kernel/fasttimer.c
  70. 55 71
      arch/cris/arch-v10/kernel/head.S
  71. 879 0
      arch/cris/arch-v10/kernel/io_interface_mux.c
  72. 43 33
      arch/cris/arch-v10/kernel/irq.c
  73. 13 4
      arch/cris/arch-v10/kernel/kgdb.c
  74. 2 1
      arch/cris/arch-v10/kernel/process.c
  75. 7 2
      arch/cris/arch-v10/kernel/ptrace.c
  76. 2 1
      arch/cris/arch-v10/kernel/shadows.c
  77. 22 15
      arch/cris/arch-v10/kernel/traps.c
  78. 2 24
      arch/cris/arch-v10/mm/fault.c
  79. 1 1
      arch/cris/arch-v10/mm/init.c
  80. 1 48
      arch/cris/arch-v10/mm/tlb.c
  81. 296 0
      arch/cris/arch-v32/Kconfig
  82. 14 0
      arch/cris/arch-v32/boot/Makefile
  83. 41 0
      arch/cris/arch-v32/boot/compressed/Makefile
  84. 25 0
      arch/cris/arch-v32/boot/compressed/README
  85. 30 0
      arch/cris/arch-v32/boot/compressed/decompress.ld
  86. 193 0
      arch/cris/arch-v32/boot/compressed/head.S
  87. 318 0
      arch/cris/arch-v32/boot/compressed/misc.c
  88. 36 0
      arch/cris/arch-v32/boot/rescue/Makefile
  89. 39 0
      arch/cris/arch-v32/boot/rescue/head.S
  90. 20 0
      arch/cris/arch-v32/boot/rescue/rescue.ld
  91. 625 0
      arch/cris/arch-v32/drivers/Kconfig
  92. 13 0
      arch/cris/arch-v32/drivers/Makefile
  93. 455 0
      arch/cris/arch-v32/drivers/axisflashmap.c
  94. 3522 0
      arch/cris/arch-v32/drivers/cryptocop.c
  95. 766 0
      arch/cris/arch-v32/drivers/gpio.c
  96. 611 0
      arch/cris/arch-v32/drivers/i2c.c
  97. 15 0
      arch/cris/arch-v32/drivers/i2c.h
  98. 219 0
      arch/cris/arch-v32/drivers/iop_fw_load.c
  99. 157 0
      arch/cris/arch-v32/drivers/nandflash.c
  100. 341 0
      arch/cris/arch-v32/drivers/pcf8563.c

+ 5 - 5
CREDITS

@@ -1624,10 +1624,10 @@ E: ajoshi@shell.unixbox.com
 D: fbdev hacking
 
 N: Jesper Juhl
-E: juhl-lkml@dif.dk
-D: Various small janitor fixes, cleanups etc.
+E: jesper.juhl@gmail.com
+D: Various fixes, cleanups and minor features.
 S: Lemnosvej 1, 3.tv
-S: 2300 Copenhagen S
+S: 2300 Copenhagen S.
 S: Denmark
 
 N: Jozsef Kadlecsik
@@ -2380,8 +2380,8 @@ E: tmolina@cablespeed.com
 D: bug fixes, documentation, minor hackery
 
 N: James Morris
-E: jmorris@redhat.com
-W: http://www.intercode.com.au/jmorris/
+E: jmorris@namei.org
+W: http://namei.org/
 D: Netfilter, Linux Security Modules (LSM), SELinux, IPSec,
 D: Crypto API, general networking, miscellaneous.
 S: PO Box 707

+ 1 - 0
Documentation/Changes

@@ -65,6 +65,7 @@ o  isdn4k-utils           3.1pre1                 # isdnctrl 2>&1|grep version
 o  nfs-utils              1.0.5                   # showmount --version
 o  procps                 3.2.0                   # ps --version
 o  oprofile               0.9                     # oprofiled --version
+o  udev                   058                     # udevinfo -V
 
 Kernel compilation
 ==================

+ 5 - 0
Documentation/SubmittingPatches

@@ -149,6 +149,11 @@ USB, framebuffer devices, the VFS, the SCSI subsystem, etc.  See the
 MAINTAINERS file for a mailing list that relates specifically to
 your change.
 
+If changes affect userland-kernel interfaces, please send
+the MAN-PAGES maintainer (as listed in the MAINTAINERS file)
+a man-pages patch, or at least a notification of the change,
+so that some information makes its way into the manual pages.
+
 Even if the maintainer did not respond in step #4, make sure to ALWAYS
 copy the maintainer when you change their code.
 

+ 1 - 0
Documentation/dontdiff

@@ -104,6 +104,7 @@ logo_*.c
 logo_*_clut224.c
 logo_*_mono.c
 lxdialog
+mach-types
 mach-types.h
 make_times_h
 map

+ 15 - 1
Documentation/fb/vesafb.txt

@@ -144,7 +144,21 @@ vgapal	Use the standard vga registers for palette changes.
 	This is the default.
 pmipal	Use the protected mode interface for palette changes.
 
-mtrr	setup memory type range registers for the vesafb framebuffer.
+mtrr:n	setup memory type range registers for the vesafb framebuffer
+	where n:
+	      0 - disabled (equivalent to nomtrr)
+	      1 - uncachable
+	      2 - write-back
+	      3 - write-combining (default)
+	      4 - write-through
+
+	If you see the following in dmesg, choose the type that matches the
+	old one. In this example, use "mtrr:2".
+...
+mtrr: type mismatch for e0000000,8000000 old: write-back new: write-combining
+...
+
+nomtrr  disable mtrr
 
 vremap:n
         remap 'n' MiB of video RAM. If 0 or not specified, remap memory

+ 114 - 0
Documentation/infiniband/core_locking.txt

@@ -0,0 +1,114 @@
+INFINIBAND MIDLAYER LOCKING
+
+  This guide is an attempt to make explicit the locking assumptions
+  made by the InfiniBand midlayer.  It describes the requirements on
+  both low-level drivers that sit below the midlayer and upper level
+  protocols that use the midlayer.
+
+Sleeping and interrupt context
+
+  With the following exceptions, a low-level driver implementation of
+  all of the methods in struct ib_device may sleep.  The exceptions
+  are any methods from the list:
+
+    create_ah
+    modify_ah
+    query_ah
+    destroy_ah
+    bind_mw
+    post_send
+    post_recv
+    poll_cq
+    req_notify_cq
+    map_phys_fmr
+
+  which may not sleep and must be callable from any context.
+
+  The corresponding functions exported to upper level protocol
+  consumers:
+
+    ib_create_ah
+    ib_modify_ah
+    ib_query_ah
+    ib_destroy_ah
+    ib_bind_mw
+    ib_post_send
+    ib_post_recv
+    ib_req_notify_cq
+    ib_map_phys_fmr
+
+  are therefore safe to call from any context.
+
+  In addition, the function
+
+    ib_dispatch_event
+
+  used by low-level drivers to dispatch asynchronous events through
+  the midlayer is also safe to call from any context.
+
+Reentrancy
+
+  All of the methods in struct ib_device exported by a low-level
+  driver must be fully reentrant.  The low-level driver is required to
+  perform all synchronization necessary to maintain consistency, even
+  if multiple function calls using the same object are run
+  simultaneously.
+
+  The IB midlayer does not perform any serialization of function calls.
+
+  Because low-level drivers are reentrant, upper level protocol
+  consumers are not required to perform any serialization.  However,
+  some serialization may be required to get sensible results.  For
+  example, a consumer may safely call ib_poll_cq() on multiple CPUs
+  simultaneously.  However, the ordering of the work completion
+  information between different calls of ib_poll_cq() is not defined.
+
+Callbacks
+
+  A low-level driver must not perform a callback directly from the
+  same callchain as an ib_device method call.  For example, it is not
+  allowed for a low-level driver to call a consumer's completion event
+  handler directly from its post_send method.  Instead, the low-level
+  driver should defer this callback by, for example, scheduling a
+  tasklet to perform the callback.
+
+  The low-level driver is responsible for ensuring that multiple
+  completion event handlers for the same CQ are not called
+  simultaneously.  The driver must guarantee that only one CQ event
+  handler for a given CQ is running at a time.  In other words, the
+  following situation is not allowed:
+
+        CPU1                                    CPU2
+
+  low-level driver ->
+    consumer CQ event callback:
+      /* ... */
+      ib_req_notify_cq(cq, ...);
+                                        low-level driver ->
+      /* ... */                           consumer CQ event callback:
+                                            /* ... */
+      return from CQ event handler
+
+  The context in which completion event and asynchronous event
+  callbacks run is not defined.  Depending on the low-level driver, it
+  may be process context, softirq context, or interrupt context.
+  Upper level protocol consumers may not sleep in a callback.
+
+Hot-plug
+
+  A low-level driver announces that a device is ready for use by
+  consumers when it calls ib_register_device(), all initialization
+  must be complete before this call.  The device must remain usable
+  until the driver's call to ib_unregister_device() has returned.
+
+  A low-level driver must call ib_register_device() and
+  ib_unregister_device() from process context.  It must not hold any
+  semaphores that could cause deadlock if a consumer calls back into
+  the driver across these calls.
+
+  An upper level protocol consumer may begin using an IB device as
+  soon as the add method of its struct ib_client is called for that
+  device.  A consumer must finish all cleanup and free all resources
+  relating to a device before returning from the remove method.
+
+  A consumer is permitted to sleep in its add and remove methods.

+ 40 - 13
Documentation/infiniband/user_mad.txt

@@ -28,13 +28,37 @@ Creating MAD agents
 
 Receiving MADs
 
-  MADs are received using read().  The buffer passed to read() must be
-  large enough to hold at least one struct ib_user_mad.  For example:
-
-	struct ib_user_mad mad;
-	ret = read(fd, &mad, sizeof mad);
-	if (ret != sizeof mad)
+  MADs are received using read().  The receive side now supports
+  RMPP. The buffer passed to read() must be at least one
+  struct ib_user_mad + 256 bytes. For example:
+
+  If the buffer passed is not large enough to hold the received
+  MAD (RMPP), the errno is set to ENOSPC and the length of the
+  buffer needed is set in mad.length.
+
+  Example for normal MAD (non RMPP) reads:
+	struct ib_user_mad *mad;
+	mad = malloc(sizeof *mad + 256);
+	ret = read(fd, mad, sizeof *mad + 256);
+	if (ret != sizeof mad + 256) {
+		perror("read");
+		free(mad);
+	}
+
+  Example for RMPP reads:
+	struct ib_user_mad *mad;
+	mad = malloc(sizeof *mad + 256);
+	ret = read(fd, mad, sizeof *mad + 256);
+	if (ret == -ENOSPC)) {
+		length = mad.length;
+		free(mad);
+		mad = malloc(sizeof *mad + length);
+		ret = read(fd, mad, sizeof *mad + length);
+	}
+	if (ret < 0) {
 		perror("read");
+		free(mad);
+	}
 
   In addition to the actual MAD contents, the other struct ib_user_mad
   fields will be filled in with information on the received MAD.  For
@@ -50,18 +74,21 @@ Sending MADs
 
   MADs are sent using write().  The agent ID for sending should be
   filled into the id field of the MAD, the destination LID should be
-  filled into the lid field, and so on.  For example:
+  filled into the lid field, and so on.  The send side does support
+  RMPP so arbitrary length MAD can be sent. For example:
+
+	struct ib_user_mad *mad;
 
-	struct ib_user_mad mad;
+	mad = malloc(sizeof *mad + mad_length);
 
-	/* fill in mad.data */
+	/* fill in mad->data */
 
-	mad.id  = my_agent;	/* req.id from agent registration */
-	mad.lid = my_dest;	/* in network byte order... */
+	mad->hdr.id  = my_agent;	/* req.id from agent registration */
+	mad->hdr.lid = my_dest;		/* in network byte order... */
 	/* etc. */
 
-	ret = write(fd, &mad, sizeof mad);
-	if (ret != sizeof mad)
+	ret = write(fd, &mad, sizeof *mad + mad_length);
+	if (ret != sizeof *mad + mad_length)
 		perror("write");
 
 Setting IsSM Capability Bit

+ 588 - 0
Documentation/kprobes.txt

@@ -0,0 +1,588 @@
+Title	: Kernel Probes (Kprobes)
+Authors	: Jim Keniston <jkenisto@us.ibm.com>
+	: Prasanna S Panchamukhi <prasanna@in.ibm.com>
+
+CONTENTS
+
+1. Concepts: Kprobes, Jprobes, Return Probes
+2. Architectures Supported
+3. Configuring Kprobes
+4. API Reference
+5. Kprobes Features and Limitations
+6. Probe Overhead
+7. TODO
+8. Kprobes Example
+9. Jprobes Example
+10. Kretprobes Example
+
+1. Concepts: Kprobes, Jprobes, Return Probes
+
+Kprobes enables you to dynamically break into any kernel routine and
+collect debugging and performance information non-disruptively. You
+can trap at almost any kernel code address, specifying a handler
+routine to be invoked when the breakpoint is hit.
+
+There are currently three types of probes: kprobes, jprobes, and
+kretprobes (also called return probes).  A kprobe can be inserted
+on virtually any instruction in the kernel.  A jprobe is inserted at
+the entry to a kernel function, and provides convenient access to the
+function's arguments.  A return probe fires when a specified function
+returns.
+
+In the typical case, Kprobes-based instrumentation is packaged as
+a kernel module.  The module's init function installs ("registers")
+one or more probes, and the exit function unregisters them.  A
+registration function such as register_kprobe() specifies where
+the probe is to be inserted and what handler is to be called when
+the probe is hit.
+
+The next three subsections explain how the different types of
+probes work.  They explain certain things that you'll need to
+know in order to make the best use of Kprobes -- e.g., the
+difference between a pre_handler and a post_handler, and how
+to use the maxactive and nmissed fields of a kretprobe.  But
+if you're in a hurry to start using Kprobes, you can skip ahead
+to section 2.
+
+1.1 How Does a Kprobe Work?
+
+When a kprobe is registered, Kprobes makes a copy of the probed
+instruction and replaces the first byte(s) of the probed instruction
+with a breakpoint instruction (e.g., int3 on i386 and x86_64).
+
+When a CPU hits the breakpoint instruction, a trap occurs, the CPU's
+registers are saved, and control passes to Kprobes via the
+notifier_call_chain mechanism.  Kprobes executes the "pre_handler"
+associated with the kprobe, passing the handler the addresses of the
+kprobe struct and the saved registers.
+
+Next, Kprobes single-steps its copy of the probed instruction.
+(It would be simpler to single-step the actual instruction in place,
+but then Kprobes would have to temporarily remove the breakpoint
+instruction.  This would open a small time window when another CPU
+could sail right past the probepoint.)
+
+After the instruction is single-stepped, Kprobes executes the
+"post_handler," if any, that is associated with the kprobe.
+Execution then continues with the instruction following the probepoint.
+
+1.2 How Does a Jprobe Work?
+
+A jprobe is implemented using a kprobe that is placed on a function's
+entry point.  It employs a simple mirroring principle to allow
+seamless access to the probed function's arguments.  The jprobe
+handler routine should have the same signature (arg list and return
+type) as the function being probed, and must always end by calling
+the Kprobes function jprobe_return().
+
+Here's how it works.  When the probe is hit, Kprobes makes a copy of
+the saved registers and a generous portion of the stack (see below).
+Kprobes then points the saved instruction pointer at the jprobe's
+handler routine, and returns from the trap.  As a result, control
+passes to the handler, which is presented with the same register and
+stack contents as the probed function.  When it is done, the handler
+calls jprobe_return(), which traps again to restore the original stack
+contents and processor state and switch to the probed function.
+
+By convention, the callee owns its arguments, so gcc may produce code
+that unexpectedly modifies that portion of the stack.  This is why
+Kprobes saves a copy of the stack and restores it after the jprobe
+handler has run.  Up to MAX_STACK_SIZE bytes are copied -- e.g.,
+64 bytes on i386.
+
+Note that the probed function's args may be passed on the stack
+or in registers (e.g., for x86_64 or for an i386 fastcall function).
+The jprobe will work in either case, so long as the handler's
+prototype matches that of the probed function.
+
+1.3 How Does a Return Probe Work?
+
+When you call register_kretprobe(), Kprobes establishes a kprobe at
+the entry to the function.  When the probed function is called and this
+probe is hit, Kprobes saves a copy of the return address, and replaces
+the return address with the address of a "trampoline."  The trampoline
+is an arbitrary piece of code -- typically just a nop instruction.
+At boot time, Kprobes registers a kprobe at the trampoline.
+
+When the probed function executes its return instruction, control
+passes to the trampoline and that probe is hit.  Kprobes' trampoline
+handler calls the user-specified handler associated with the kretprobe,
+then sets the saved instruction pointer to the saved return address,
+and that's where execution resumes upon return from the trap.
+
+While the probed function is executing, its return address is
+stored in an object of type kretprobe_instance.  Before calling
+register_kretprobe(), the user sets the maxactive field of the
+kretprobe struct to specify how many instances of the specified
+function can be probed simultaneously.  register_kretprobe()
+pre-allocates the indicated number of kretprobe_instance objects.
+
+For example, if the function is non-recursive and is called with a
+spinlock held, maxactive = 1 should be enough.  If the function is
+non-recursive and can never relinquish the CPU (e.g., via a semaphore
+or preemption), NR_CPUS should be enough.  If maxactive <= 0, it is
+set to a default value.  If CONFIG_PREEMPT is enabled, the default
+is max(10, 2*NR_CPUS).  Otherwise, the default is NR_CPUS.
+
+It's not a disaster if you set maxactive too low; you'll just miss
+some probes.  In the kretprobe struct, the nmissed field is set to
+zero when the return probe is registered, and is incremented every
+time the probed function is entered but there is no kretprobe_instance
+object available for establishing the return probe.
+
+2. Architectures Supported
+
+Kprobes, jprobes, and return probes are implemented on the following
+architectures:
+
+- i386
+- x86_64 (AMD-64, E64MT)
+- ppc64
+- ia64 (Support for probes on certain instruction types is still in progress.)
+- sparc64 (Return probes not yet implemented.)
+
+3. Configuring Kprobes
+
+When configuring the kernel using make menuconfig/xconfig/oldconfig,
+ensure that CONFIG_KPROBES is set to "y".  Under "Kernel hacking",
+look for "Kprobes".  You may have to enable "Kernel debugging"
+(CONFIG_DEBUG_KERNEL) before you can enable Kprobes.
+
+You may also want to ensure that CONFIG_KALLSYMS and perhaps even
+CONFIG_KALLSYMS_ALL are set to "y", since kallsyms_lookup_name()
+is a handy, version-independent way to find a function's address.
+
+If you need to insert a probe in the middle of a function, you may find
+it useful to "Compile the kernel with debug info" (CONFIG_DEBUG_INFO),
+so you can use "objdump -d -l vmlinux" to see the source-to-object
+code mapping.
+
+4. API Reference
+
+The Kprobes API includes a "register" function and an "unregister"
+function for each type of probe.  Here are terse, mini-man-page
+specifications for these functions and the associated probe handlers
+that you'll write.  See the latter half of this document for examples.
+
+4.1 register_kprobe
+
+#include <linux/kprobes.h>
+int register_kprobe(struct kprobe *kp);
+
+Sets a breakpoint at the address kp->addr.  When the breakpoint is
+hit, Kprobes calls kp->pre_handler.  After the probed instruction
+is single-stepped, Kprobe calls kp->post_handler.  If a fault
+occurs during execution of kp->pre_handler or kp->post_handler,
+or during single-stepping of the probed instruction, Kprobes calls
+kp->fault_handler.  Any or all handlers can be NULL.
+
+register_kprobe() returns 0 on success, or a negative errno otherwise.
+
+User's pre-handler (kp->pre_handler):
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+int pre_handler(struct kprobe *p, struct pt_regs *regs);
+
+Called with p pointing to the kprobe associated with the breakpoint,
+and regs pointing to the struct containing the registers saved when
+the breakpoint was hit.  Return 0 here unless you're a Kprobes geek.
+
+User's post-handler (kp->post_handler):
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+void post_handler(struct kprobe *p, struct pt_regs *regs,
+	unsigned long flags);
+
+p and regs are as described for the pre_handler.  flags always seems
+to be zero.
+
+User's fault-handler (kp->fault_handler):
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+int fault_handler(struct kprobe *p, struct pt_regs *regs, int trapnr);
+
+p and regs are as described for the pre_handler.  trapnr is the
+architecture-specific trap number associated with the fault (e.g.,
+on i386, 13 for a general protection fault or 14 for a page fault).
+Returns 1 if it successfully handled the exception.
+
+4.2 register_jprobe
+
+#include <linux/kprobes.h>
+int register_jprobe(struct jprobe *jp)
+
+Sets a breakpoint at the address jp->kp.addr, which must be the address
+of the first instruction of a function.  When the breakpoint is hit,
+Kprobes runs the handler whose address is jp->entry.
+
+The handler should have the same arg list and return type as the probed
+function; and just before it returns, it must call jprobe_return().
+(The handler never actually returns, since jprobe_return() returns
+control to Kprobes.)  If the probed function is declared asmlinkage,
+fastcall, or anything else that affects how args are passed, the
+handler's declaration must match.
+
+register_jprobe() returns 0 on success, or a negative errno otherwise.
+
+4.3 register_kretprobe
+
+#include <linux/kprobes.h>
+int register_kretprobe(struct kretprobe *rp);
+
+Establishes a return probe for the function whose address is
+rp->kp.addr.  When that function returns, Kprobes calls rp->handler.
+You must set rp->maxactive appropriately before you call
+register_kretprobe(); see "How Does a Return Probe Work?" for details.
+
+register_kretprobe() returns 0 on success, or a negative errno
+otherwise.
+
+User's return-probe handler (rp->handler):
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+int kretprobe_handler(struct kretprobe_instance *ri, struct pt_regs *regs);
+
+regs is as described for kprobe.pre_handler.  ri points to the
+kretprobe_instance object, of which the following fields may be
+of interest:
+- ret_addr: the return address
+- rp: points to the corresponding kretprobe object
+- task: points to the corresponding task struct
+The handler's return value is currently ignored.
+
+4.4 unregister_*probe
+
+#include <linux/kprobes.h>
+void unregister_kprobe(struct kprobe *kp);
+void unregister_jprobe(struct jprobe *jp);
+void unregister_kretprobe(struct kretprobe *rp);
+
+Removes the specified probe.  The unregister function can be called
+at any time after the probe has been registered.
+
+5. Kprobes Features and Limitations
+
+As of Linux v2.6.12, Kprobes allows multiple probes at the same
+address.  Currently, however, there cannot be multiple jprobes on
+the same function at the same time.
+
+In general, you can install a probe anywhere in the kernel.
+In particular, you can probe interrupt handlers.  Known exceptions
+are discussed in this section.
+
+For obvious reasons, it's a bad idea to install a probe in
+the code that implements Kprobes (mostly kernel/kprobes.c and
+arch/*/kernel/kprobes.c).  A patch in the v2.6.13 timeframe instructs
+Kprobes to reject such requests.
+
+If you install a probe in an inline-able function, Kprobes makes
+no attempt to chase down all inline instances of the function and
+install probes there.  gcc may inline a function without being asked,
+so keep this in mind if you're not seeing the probe hits you expect.
+
+A probe handler can modify the environment of the probed function
+-- e.g., by modifying kernel data structures, or by modifying the
+contents of the pt_regs struct (which are restored to the registers
+upon return from the breakpoint).  So Kprobes can be used, for example,
+to install a bug fix or to inject faults for testing.  Kprobes, of
+course, has no way to distinguish the deliberately injected faults
+from the accidental ones.  Don't drink and probe.
+
+Kprobes makes no attempt to prevent probe handlers from stepping on
+each other -- e.g., probing printk() and then calling printk() from a
+probe handler.  As of Linux v2.6.12, if a probe handler hits a probe,
+that second probe's handlers won't be run in that instance.
+
+In Linux v2.6.12 and previous versions, Kprobes' data structures are
+protected by a single lock that is held during probe registration and
+unregistration and while handlers are run.  Thus, no two handlers
+can run simultaneously.  To improve scalability on SMP systems,
+this restriction will probably be removed soon, in which case
+multiple handlers (or multiple instances of the same handler) may
+run concurrently on different CPUs.  Code your handlers accordingly.
+
+Kprobes does not use semaphores or allocate memory except during
+registration and unregistration.
+
+Probe handlers are run with preemption disabled.  Depending on the
+architecture, handlers may also run with interrupts disabled.  In any
+case, your handler should not yield the CPU (e.g., by attempting to
+acquire a semaphore).
+
+Since a return probe is implemented by replacing the return
+address with the trampoline's address, stack backtraces and calls
+to __builtin_return_address() will typically yield the trampoline's
+address instead of the real return address for kretprobed functions.
+(As far as we can tell, __builtin_return_address() is used only
+for instrumentation and error reporting.)
+
+If the number of times a function is called does not match the
+number of times it returns, registering a return probe on that
+function may produce undesirable results.  We have the do_exit()
+and do_execve() cases covered.  do_fork() is not an issue.  We're
+unaware of other specific cases where this could be a problem.
+
+6. Probe Overhead
+
+On a typical CPU in use in 2005, a kprobe hit takes 0.5 to 1.0
+microseconds to process.  Specifically, a benchmark that hits the same
+probepoint repeatedly, firing a simple handler each time, reports 1-2
+million hits per second, depending on the architecture.  A jprobe or
+return-probe hit typically takes 50-75% longer than a kprobe hit.
+When you have a return probe set on a function, adding a kprobe at
+the entry to that function adds essentially no overhead.
+
+Here are sample overhead figures (in usec) for different architectures.
+k = kprobe; j = jprobe; r = return probe; kr = kprobe + return probe
+on same function; jr = jprobe + return probe on same function
+
+i386: Intel Pentium M, 1495 MHz, 2957.31 bogomips
+k = 0.57 usec; j = 1.00; r = 0.92; kr = 0.99; jr = 1.40
+
+x86_64: AMD Opteron 246, 1994 MHz, 3971.48 bogomips
+k = 0.49 usec; j = 0.76; r = 0.80; kr = 0.82; jr = 1.07
+
+ppc64: POWER5 (gr), 1656 MHz (SMT disabled, 1 virtual CPU per physical CPU)
+k = 0.77 usec; j = 1.31; r = 1.26; kr = 1.45; jr = 1.99
+
+7. TODO
+
+a. SystemTap (http://sourceware.org/systemtap): Work in progress
+to provide a simplified programming interface for probe-based
+instrumentation.
+b. Improved SMP scalability: Currently, work is in progress to handle
+multiple kprobes in parallel.
+c. Kernel return probes for sparc64.
+d. Support for other architectures.
+e. User-space probes.
+
+8. Kprobes Example
+
+Here's a sample kernel module showing the use of kprobes to dump a
+stack trace and selected i386 registers when do_fork() is called.
+----- cut here -----
+/*kprobe_example.c*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kprobes.h>
+#include <linux/kallsyms.h>
+#include <linux/sched.h>
+
+/*For each probe you need to allocate a kprobe structure*/
+static struct kprobe kp;
+
+/*kprobe pre_handler: called just before the probed instruction is executed*/
+int handler_pre(struct kprobe *p, struct pt_regs *regs)
+{
+	printk("pre_handler: p->addr=0x%p, eip=%lx, eflags=0x%lx\n",
+		p->addr, regs->eip, regs->eflags);
+	dump_stack();
+	return 0;
+}
+
+/*kprobe post_handler: called after the probed instruction is executed*/
+void handler_post(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
+{
+	printk("post_handler: p->addr=0x%p, eflags=0x%lx\n",
+		p->addr, regs->eflags);
+}
+
+/* fault_handler: this is called if an exception is generated for any
+ * instruction within the pre- or post-handler, or when Kprobes
+ * single-steps the probed instruction.
+ */
+int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
+{
+	printk("fault_handler: p->addr=0x%p, trap #%dn",
+		p->addr, trapnr);
+	/* Return 0 because we don't handle the fault. */
+	return 0;
+}
+
+int init_module(void)
+{
+	int ret;
+	kp.pre_handler = handler_pre;
+	kp.post_handler = handler_post;
+	kp.fault_handler = handler_fault;
+	kp.addr = (kprobe_opcode_t*) kallsyms_lookup_name("do_fork");
+	/* register the kprobe now */
+	if (!kp.addr) {
+		printk("Couldn't find %s to plant kprobe\n", "do_fork");
+		return -1;
+	}
+	if ((ret = register_kprobe(&kp) < 0)) {
+		printk("register_kprobe failed, returned %d\n", ret);
+		return -1;
+	}
+	printk("kprobe registered\n");
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	unregister_kprobe(&kp);
+	printk("kprobe unregistered\n");
+}
+
+MODULE_LICENSE("GPL");
+----- cut here -----
+
+You can build the kernel module, kprobe-example.ko, using the following
+Makefile:
+----- cut here -----
+obj-m := kprobe-example.o
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+default:
+	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
+clean:
+	rm -f *.mod.c *.ko *.o
+----- cut here -----
+
+$ make
+$ su -
+...
+# insmod kprobe-example.ko
+
+You will see the trace data in /var/log/messages and on the console
+whenever do_fork() is invoked to create a new process.
+
+9. Jprobes Example
+
+Here's a sample kernel module showing the use of jprobes to dump
+the arguments of do_fork().
+----- cut here -----
+/*jprobe-example.c */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uio.h>
+#include <linux/kprobes.h>
+#include <linux/kallsyms.h>
+
+/*
+ * Jumper probe for do_fork.
+ * Mirror principle enables access to arguments of the probed routine
+ * from the probe handler.
+ */
+
+/* Proxy routine having the same arguments as actual do_fork() routine */
+long jdo_fork(unsigned long clone_flags, unsigned long stack_start,
+	      struct pt_regs *regs, unsigned long stack_size,
+	      int __user * parent_tidptr, int __user * child_tidptr)
+{
+	printk("jprobe: clone_flags=0x%lx, stack_size=0x%lx, regs=0x%p\n",
+	       clone_flags, stack_size, regs);
+	/* Always end with a call to jprobe_return(). */
+	jprobe_return();
+	/*NOTREACHED*/
+	return 0;
+}
+
+static struct jprobe my_jprobe = {
+	.entry = (kprobe_opcode_t *) jdo_fork
+};
+
+int init_module(void)
+{
+	int ret;
+	my_jprobe.kp.addr = (kprobe_opcode_t *) kallsyms_lookup_name("do_fork");
+	if (!my_jprobe.kp.addr) {
+		printk("Couldn't find %s to plant jprobe\n", "do_fork");
+		return -1;
+	}
+
+	if ((ret = register_jprobe(&my_jprobe)) <0) {
+		printk("register_jprobe failed, returned %d\n", ret);
+		return -1;
+	}
+	printk("Planted jprobe at %p, handler addr %p\n",
+	       my_jprobe.kp.addr, my_jprobe.entry);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	unregister_jprobe(&my_jprobe);
+	printk("jprobe unregistered\n");
+}
+
+MODULE_LICENSE("GPL");
+----- cut here -----
+
+Build and insert the kernel module as shown in the above kprobe
+example.  You will see the trace data in /var/log/messages and on
+the console whenever do_fork() is invoked to create a new process.
+(Some messages may be suppressed if syslogd is configured to
+eliminate duplicate messages.)
+
+10. Kretprobes Example
+
+Here's a sample kernel module showing the use of return probes to
+report failed calls to sys_open().
+----- cut here -----
+/*kretprobe-example.c*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kprobes.h>
+#include <linux/kallsyms.h>
+
+static const char *probed_func = "sys_open";
+
+/* Return-probe handler: If the probed function fails, log the return value. */
+static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
+{
+	// Substitute the appropriate register name for your architecture --
+	// e.g., regs->rax for x86_64, regs->gpr[3] for ppc64.
+	int retval = (int) regs->eax;
+	if (retval < 0) {
+		printk("%s returns %d\n", probed_func, retval);
+	}
+	return 0;
+}
+
+static struct kretprobe my_kretprobe = {
+	.handler = ret_handler,
+	/* Probe up to 20 instances concurrently. */
+	.maxactive = 20
+};
+
+int init_module(void)
+{
+	int ret;
+	my_kretprobe.kp.addr =
+		(kprobe_opcode_t *) kallsyms_lookup_name(probed_func);
+	if (!my_kretprobe.kp.addr) {
+		printk("Couldn't find %s to plant return probe\n", probed_func);
+		return -1;
+	}
+	if ((ret = register_kretprobe(&my_kretprobe)) < 0) {
+		printk("register_kretprobe failed, returned %d\n", ret);
+		return -1;
+	}
+	printk("Planted return probe at %p\n", my_kretprobe.kp.addr);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	unregister_kretprobe(&my_kretprobe);
+	printk("kretprobe unregistered\n");
+	/* nmissed > 0 suggests that maxactive was set too low. */
+	printk("Missed probing %d instances of %s\n",
+		my_kretprobe.nmissed, probed_func);
+}
+
+MODULE_LICENSE("GPL");
+----- cut here -----
+
+Build and insert the kernel module as shown in the above kprobe
+example.  You will see the trace data in /var/log/messages and on the
+console whenever sys_open() returns a negative value.  (Some messages
+may be suppressed if syslogd is configured to eliminate duplicate
+messages.)
+
+For additional information on Kprobes, refer to the following URLs:
+http://www-106.ibm.com/developerworks/library/l-kprobes.html?ca=dgr-lnxw42Kprobe
+http://www.redhat.com/magazine/005mar05/features/kprobes/

Файловите разлики са ограничени, защото са твърде много
+ 568 - 242
Documentation/networking/bonding.txt


+ 9 - 0
Documentation/pcmcia/driver-changes.txt

@@ -56,3 +56,12 @@ This file details changes in 2.6 which affect PCMCIA card driver authors:
    memory regions in-use. The name argument should be a pointer to
    your driver name. Eg, for pcnet_cs, name should point to the
    string "pcnet_cs".
+
+* CardServices is gone
+  CardServices() in 2.4 is just a big switch statement to call various
+  services.  In 2.6, all of those entry points are exported and called
+  directly (except for pcmcia_report_error(), just use cs_error() instead).
+
+* struct pcmcia_driver
+  You need to use struct pcmcia_driver and pcmcia_{un,}register_driver
+  instead of {un,}register_pccard_driver

+ 20 - 24
Documentation/sound/alsa/ALSA-Configuration.txt

@@ -636,11 +636,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 	  3stack-digout	3-jack in back, a HP out and a SPDIF out
 	  5stack	5-jack in back, 2-jack in front
 	  5stack-digout	5-jack in back, 2-jack in front, a SPDIF out
+	  6stack	6-jack in back, 2-jack in front
+	  6stack-digout	6-jack with a SPDIF out
 	  w810		3-jack
 	  z71v		3-jack (HP shared SPDIF)
 	  asus		3-jack
 	  uniwill	3-jack
 	  F1734		2-jack
+	  test		for testing/debugging purpose, almost all controls can be
+			adjusted.  Appearing only when compiled with
+			$CONFIG_SND_DEBUG=y
 
 	CMI9880
 	  minimal	3-jack in back
@@ -1054,6 +1059,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     The power-management is supported.
 
+  Module snd-pxa2xx-ac97 (on arm only)
+  ------------------------------------
+
+    Module for AC97 driver for the Intel PXA2xx chip
+
+    For ARM architecture only.
+
   Module snd-rme32
   ----------------
 
@@ -1173,6 +1185,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module supports up to 8 cards.
 
+  Module snd-sun-dbri (on sparc only)
+  -----------------------------------
+
+    Module for DBRI sound chips found on Sparcs.
+
+    Module supports up to 8 cards.
+
   Module snd-wavefront
   --------------------
 
@@ -1371,7 +1390,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
   Module snd-vxpocket
   -------------------
 
-    Module for Digigram VX-Pocket VX2 PCMCIA card.
+    Module for Digigram VX-Pocket VX2 and 440 PCMCIA cards.
 
     ibl      - Capture IBL size. (default = 0, minimum size)
 
@@ -1391,29 +1410,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Note: the driver is build only when CONFIG_ISA is set.
     
-  Module snd-vxp440
-  -----------------
-
-    Module for Digigram VX-Pocket 440 PCMCIA card.
-
-    ibl      - Capture IBL size. (default = 0, minimum size)
-
-    Module supports up to 8 cards.  The module is compiled only when
-    PCMCIA is supported on kernel.
-
-    To activate the driver via the card manager, you'll need to set
-    up /etc/pcmcia/vxp440.conf.  See the sound/pcmcia/vx/vxp440.c.
-
-    When the driver is compiled as a module and the hotplug firmware
-    is supported, the firmware data is loaded via hotplug automatically.
-    Install the necessary firmware files in alsa-firmware package.
-    When no hotplug fw loader is available, you need to load the
-    firmware via vxloader utility in alsa-tools package.
-
-    About capture IBL, see the description of snd-vx222 module.
-
-    Note: the driver is build only when CONFIG_ISA is set.
-    
   Module snd-ymfpci
   -----------------
 

+ 1 - 1
Documentation/stable_api_nonsense.txt

@@ -132,7 +132,7 @@ to extra work for the USB developers.  Since all Linux USB developers do
 their work on their own time, asking programmers to do extra work for no
 gain, for free, is not a possibility.
 
-Security issues are also a very important for Linux.  When a
+Security issues are also very important for Linux.  When a
 security issue is found, it is fixed in a very short amount of time.  A
 number of times this has caused internal kernel interfaces to be
 reworked to prevent the security problem from occurring.  When this

+ 58 - 0
Documentation/stable_kernel_rules.txt

@@ -0,0 +1,58 @@
+Everything you ever wanted to know about Linux 2.6 -stable releases.
+
+Rules on what kind of patches are accepted, and what ones are not, into
+the "-stable" tree:
+
+ - It must be obviously correct and tested.
+ - It can not bigger than 100 lines, with context.
+ - It must fix only one thing.
+ - It must fix a real bug that bothers people (not a, "This could be a
+   problem..." type thing.)
+ - It must fix a problem that causes a build error (but not for things
+   marked CONFIG_BROKEN), an oops, a hang, data corruption, a real
+   security issue, or some "oh, that's not good" issue.  In short,
+   something critical.
+ - No "theoretical race condition" issues, unless an explanation of how
+   the race can be exploited.
+ - It can not contain any "trivial" fixes in it (spelling changes,
+   whitespace cleanups, etc.)
+ - It must be accepted by the relevant subsystem maintainer.
+ - It must follow Documentation/SubmittingPatches rules.
+
+
+Procedure for submitting patches to the -stable tree:
+
+ - Send the patch, after verifying that it follows the above rules, to
+   stable@kernel.org.
+ - The sender will receive an ack when the patch has been accepted into
+   the queue, or a nak if the patch is rejected.  This response might
+   take a few days, according to the developer's schedules.
+ - If accepted, the patch will be added to the -stable queue, for review
+   by other developers.
+ - Security patches should not be sent to this alias, but instead to the
+   documented security@kernel.org.
+
+
+Review cycle:
+
+ - When the -stable maintainers decide for a review cycle, the patches
+   will be sent to the review committee, and the maintainer of the
+   affected area of the patch (unless the submitter is the maintainer of
+   the area) and CC: to the linux-kernel mailing list.
+ - The review committee has 48 hours in which to ack or nak the patch.
+ - If the patch is rejected by a member of the committee, or linux-kernel
+   members object to the patch, bringing up issues that the maintainers
+   and members did not realize, the patch will be dropped from the
+   queue.
+ - At the end of the review cycle, the acked patches will be added to
+   the latest -stable release, and a new -stable release will happen.
+ - Security patches will be accepted into the -stable tree directly from
+   the security kernel team, and not go through the normal review cycle.
+   Contact the kernel security team for more details on this procedure.
+
+
+Review committe:
+
+ - This will be made up of a number of kernel developers who have
+   volunteered for this task, and a few that haven't.
+

+ 1 - 1
Documentation/usb/usbmon.txt

@@ -102,7 +102,7 @@ Here is the list of words, from left to right:
 - URB Status. This field makes no sense for submissions, but is present
   to help scripts with parsing. In error case, it contains the error code.
   In case of a setup packet, it contains a Setup Tag. If scripts read a number
-  in this field, the proceed to read Data Length. Otherwise, they read
+  in this field, they proceed to read Data Length. Otherwise, they read
   the setup packet before reading the Data Length.
 - Setup packet, if present, consists of 5 words: one of each for bmRequestType,
   bRequest, wValue, wIndex, wLength, as specified by the USB Specification 2.0.

+ 1 - 0
Documentation/video4linux/CARDLIST.cx88

@@ -29,3 +29,4 @@ card=27 - PixelView PlayTV Ultra Pro (Stereo)
 card=28 - DViCO FusionHDTV 3 Gold-T
 card=29 - ADS Tech Instant TV DVB-T PCI
 card=30 - TerraTec Cinergy 1400 DVB-T
+card=31 - DViCO FusionHDTV 5 Gold

+ 2 - 0
Documentation/video4linux/CARDLIST.tuner

@@ -62,3 +62,5 @@ tuner=60 - Thomson DDT 7611 (ATSC/NTSC)
 tuner=61 - Tena TNF9533-D/IF/TNF9533-B/DF
 tuner=62 - Philips TEA5767HN FM Radio
 tuner=63 - Philips FMD1216ME MK3 Hybrid Tuner
+tuner=64 - LG TDVS-H062F/TUA6034
+tuner=65 - Ymec TVF66T5-B/DFF

+ 3 - 0
Documentation/video4linux/bttv/Insmod-options

@@ -44,6 +44,9 @@ bttv.o
 				push used by bttv.  bttv will disable overlay
 				by default on this hardware to avoid crashes.
 				With this insmod option you can override this.
+		no_overlay=1	Disable overlay. It should be used by broken
+				hardware that doesn't support PCI2PCI direct
+				transfers.
 		automute=0/1	Automatically mutes the sound if there is
 				no TV signal, on by default.  You might try
 				to disable this if you have bad input signal

+ 11 - 4
Documentation/x86_64/boot-options.txt

@@ -6,6 +6,11 @@ only the AMD64 specific ones are listed here.
 Machine check
 
    mce=off disable machine check
+   mce=bootlog Enable logging of machine checks left over from booting.
+               Disabled by default because some BIOS leave bogus ones.
+               If your BIOS doesn't do that it's a good idea to enable though
+               to make sure you log even machine check events that result
+               in a reboot.
 
    nomce (for compatibility with i386): same as mce=off
 
@@ -47,7 +52,7 @@ Timing
   notsc
   Don't use the CPU time stamp counter to read the wall time.
   This can be used to work around timing problems on multiprocessor systems
-  with not properly synchronized CPUs. Only useful with a SMP kernel
+  with not properly synchronized CPUs.
 
   report_lost_ticks
   Report when timer interrupts are lost because some code turned off
@@ -74,6 +79,9 @@ Idle loop
   event. This will make the CPUs eat a lot more power, but may be useful
   to get slightly better performance in multiprocessor benchmarks. It also
   makes some profiling using performance counters more accurate.
+  Please note that on systems with MONITOR/MWAIT support (like Intel EM64T
+  CPUs) this option has no performance advantage over the normal idle loop.
+  It may also interact badly with hyperthreading.
 
 Rebooting
 
@@ -178,6 +186,5 @@ Debugging
 Misc
 
   noreplacement  Don't replace instructions with more appropiate ones
-  				 for the CPU. This may be useful on asymmetric MP systems
-				 where some CPU have less capabilities than the others.
-
+		 for the CPU. This may be useful on asymmetric MP systems
+		 where some CPU have less capabilities than the others.

+ 8 - 2
MAINTAINERS

@@ -1524,6 +1524,12 @@ P:	Zach Brown
 M:	zab@zabbo.net
 S:	Odd Fixes
 
+MAN-PAGES: MANUAL PAGES FOR LINUX -- Sections 2, 3, 4, 5, and 7
+P: Michael Kerrisk
+M: mtk-manpages@gmx.net
+W: ftp://ftp.kernel.org/pub/linux/docs/manpages
+S: Maintained
+
 MARVELL MV64340 ETHERNET DRIVER
 P:	Manish Lachwani
 M:	Manish_Lachwani@pmc-sierra.com
@@ -1655,7 +1661,7 @@ M:	kuznet@ms2.inr.ac.ru
 P:	Pekka Savola (ipv6)
 M:	pekkas@netcore.fi
 P:	James Morris
-M:	jmorris@redhat.com
+M:	jmorris@namei.org
 P:	Hideaki YOSHIFUJI
 M:	yoshfuji@linux-ipv6.org
 P:	Patrick McHardy
@@ -2044,7 +2050,7 @@ SELINUX SECURITY MODULE
 P:	Stephen Smalley
 M:	sds@epoch.ncsc.mil
 P:	James Morris
-M:	jmorris@redhat.com
+M:	jmorris@namei.org
 L:	linux-kernel@vger.kernel.org (kernel issues)
 L: 	selinux@tycho.nsa.gov (general discussion)
 W:	http://www.nsa.gov/selinux

+ 1 - 1
Makefile

@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 13
-EXTRAVERSION =-rc3
+EXTRAVERSION =-rc6
 NAME=Woozy Numbat
 
 # *DOCUMENTATION*

+ 10 - 0
README

@@ -87,6 +87,16 @@ INSTALLING the kernel:
    kernel source.  Patches are applied from the current directory, but
    an alternative directory can be specified as the second argument.
 
+ - If you are upgrading between releases using the stable series patches
+   (for example, patch-2.6.xx.y), note that these "dot-releases" are
+   not incremental and must be applied to the 2.6.xx base tree. For
+   example, if your base kernel is 2.6.12 and you want to apply the
+   2.6.12.3 patch, you do not and indeed must not first apply the
+   2.6.12.1 and 2.6.12.2 patches. Similarly, if you are running kernel
+   version 2.6.12.2 and want to jump to 2.6.12.3, you must first
+   reverse the 2.6.12.2 patch (that is, patch -R) _before_ applying
+   the 2.6.12.3 patch.
+
  - Make sure you have no stale .o files and dependencies lying around:
 
 		cd linux

+ 11 - 10
REPORTING-BUGS

@@ -41,18 +41,19 @@ summary from [1.]>" for easy identification by the developers
 [2.] Full description of the problem/report:
 [3.] Keywords (i.e., modules, networking, kernel):
 [4.] Kernel version (from /proc/version):
-[5.] Output of Oops.. message (if applicable) with symbolic information 
+[5.] Most recent kernel version which did not have the bug:
+[6.] Output of Oops.. message (if applicable) with symbolic information
      resolved (see Documentation/oops-tracing.txt)
-[6.] A small shell script or example program which triggers the
+[7.] A small shell script or example program which triggers the
      problem (if possible)
-[7.] Environment
-[7.1.] Software (add the output of the ver_linux script here)
-[7.2.] Processor information (from /proc/cpuinfo):
-[7.3.] Module information (from /proc/modules):
-[7.4.] Loaded driver and hardware information (/proc/ioports, /proc/iomem)
-[7.5.] PCI information ('lspci -vvv' as root)
-[7.6.] SCSI information (from /proc/scsi/scsi)
-[7.7.] Other information that might be relevant to the problem
+[8.] Environment
+[8.1.] Software (add the output of the ver_linux script here)
+[8.2.] Processor information (from /proc/cpuinfo):
+[8.3.] Module information (from /proc/modules):
+[8.4.] Loaded driver and hardware information (/proc/ioports, /proc/iomem)
+[8.5.] PCI information ('lspci -vvv' as root)
+[8.6.] SCSI information (from /proc/scsi/scsi)
+[8.7.] Other information that might be relevant to the problem
        (please look in /proc and include all information that you
        think to be relevant):
 [X.] Other notes, patches, fixes, workarounds:

+ 16 - 0
arch/alpha/kernel/pci.c

@@ -350,8 +350,24 @@ pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
 	region->end = res->end - offset;
 }
 
+void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res,
+			     struct pci_bus_region *region)
+{
+	struct pci_controller *hose = (struct pci_controller *)dev->sysdata;
+	unsigned long offset = 0;
+
+	if (res->flags & IORESOURCE_IO)
+		offset = hose->io_space->start;
+	else if (res->flags & IORESOURCE_MEM)
+		offset = hose->mem_space->start;
+
+	res->start = region->start + offset;
+	res->end = region->end + offset;
+}
+
 #ifdef CONFIG_HOTPLUG
 EXPORT_SYMBOL(pcibios_resource_to_bus);
+EXPORT_SYMBOL(pcibios_bus_to_resource);
 #endif
 
 int

+ 5 - 0
arch/alpha/kernel/systbls.S

@@ -461,6 +461,11 @@ sys_call_table:
 	.quad sys_add_key
 	.quad sys_request_key			/* 440 */
 	.quad sys_keyctl
+	.quad sys_ioprio_set
+	.quad sys_ioprio_get
+	.quad sys_inotify_init
+	.quad sys_inotify_add_watch		/* 445 */
+	.quad sys_inotify_rm_watch
 
 	.size sys_call_table, . - sys_call_table
 	.type sys_call_table, @object

+ 17 - 0
arch/arm/kernel/bios32.c

@@ -447,9 +447,26 @@ pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
 	region->end   = res->end - offset;
 }
 
+void __devinit
+pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res,
+			struct pci_bus_region *region)
+{
+	struct pci_sys_data *root = dev->sysdata;
+	unsigned long offset = 0;
+
+	if (res->flags & IORESOURCE_IO)
+		offset = root->io_offset;
+	if (res->flags & IORESOURCE_MEM)
+		offset = root->mem_offset;
+
+	res->start = region->start + offset;
+	res->end   = region->end + offset;
+}
+
 #ifdef CONFIG_HOTPLUG
 EXPORT_SYMBOL(pcibios_fixup_bus);
 EXPORT_SYMBOL(pcibios_resource_to_bus);
+EXPORT_SYMBOL(pcibios_bus_to_resource);
 #endif
 
 /*

+ 1 - 0
arch/arm/kernel/smp.c

@@ -176,6 +176,7 @@ asmlinkage void __cpuinit secondary_start_kernel(void)
 	cpu_set(cpu, mm->cpu_vm_mask);
 	cpu_switch_mm(mm->pgd, mm);
 	enter_lazy_tlb(mm, current);
+	local_flush_tlb_all();
 
 	cpu_init();
 

+ 4 - 4
arch/arm/lib/bitops.h

@@ -7,7 +7,7 @@
 1:	ldrexb	r2, [r1]
 	\instr	r2, r2, r3
 	strexb	r0, r2, [r1]
-	cmpne	r0, #0
+	cmp	r0, #0
 	bne	1b
 	mov	pc, lr
 	.endm
@@ -19,9 +19,9 @@
 	mov	r3, r2, lsl r3		@ create mask
 1:	ldrexb	r2, [r1]
 	ands	r0, r2, r3		@ save old value of bit
-	\instr	ip, r2, r3			@ toggle bit
-	strexb	r2, ip, [r1]
-	cmp	r2, #0
+	\instr	r2, r2, r3			@ toggle bit
+	strexb	ip, r2, [r1]
+	cmp	ip, #0
 	bne	1b
 	cmp	r0, #0
 	movne	r0, #1

+ 2 - 0
arch/arm/mach-integrator/platsmp.c

@@ -15,6 +15,7 @@
 #include <linux/mm.h>
 
 #include <asm/atomic.h>
+#include <asm/cacheflush.h>
 #include <asm/delay.h>
 #include <asm/mmu_context.h>
 #include <asm/procinfo.h>
@@ -80,6 +81,7 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
 	 * "cpu" is Linux's internal ID.
 	 */
 	pen_release = cpu;
+	flush_cache_all();
 
 	/*
 	 * XXX

+ 1 - 1
arch/arm/mach-ixp4xx/coyote-setup.c

@@ -61,7 +61,7 @@ static struct plat_serial8250_port coyote_uart_data[] = {
 		.mapbase	= IXP4XX_UART2_BASE_PHYS,
 		.membase	= (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET,
 		.irq		= IRQ_IXP4XX_UART2,
-		.flags		= UPF_BOOT_AUTOCONF,
+		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
 		.iotype		= UPIO_MEM,
 		.regshift	= 2,
 		.uartclk	= IXP4XX_UART_XTAL,

+ 1 - 1
arch/arm/mach-ixp4xx/gtwx5715-setup.c

@@ -83,7 +83,7 @@ static struct plat_serial8250_port gtwx5715_uart_platform_data[] = {
 	.mapbase	= IXP4XX_UART2_BASE_PHYS,
 	.membase	= (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET,
 	.irq		= IRQ_IXP4XX_UART2,
-	.flags		= UPF_BOOT_AUTOCONF,
+	.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
 	.iotype		= UPIO_MEM,
 	.regshift	= 2,
 	.uartclk	= IXP4XX_UART_XTAL,

+ 2 - 2
arch/arm/mach-ixp4xx/ixdp425-setup.c

@@ -82,7 +82,7 @@ static struct plat_serial8250_port ixdp425_uart_data[] = {
 		.mapbase	= IXP4XX_UART1_BASE_PHYS,
 		.membase	= (char *)IXP4XX_UART1_BASE_VIRT + REG_OFFSET,
 		.irq		= IRQ_IXP4XX_UART1,
-		.flags		= UPF_BOOT_AUTOCONF,
+		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
 		.iotype		= UPIO_MEM,
 		.regshift	= 2,
 		.uartclk	= IXP4XX_UART_XTAL,
@@ -91,7 +91,7 @@ static struct plat_serial8250_port ixdp425_uart_data[] = {
 		.mapbase	= IXP4XX_UART2_BASE_PHYS,
 		.membase	= (char *)IXP4XX_UART2_BASE_VIRT + REG_OFFSET,
 		.irq		= IRQ_IXP4XX_UART1,
-		.flags		= UPF_BOOT_AUTOCONF,
+		.flags		= UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
 		.iotype		= UPIO_MEM,
 		.regshift	= 2,
 		.uartclk	= IXP4XX_UART_XTAL,

+ 15 - 1
arch/arm/mach-s3c2410/mach-bast.c

@@ -30,6 +30,7 @@
  *     28-Jun-2005 BJD  Moved pm functionality out to common code
  *     17-Jul-2005 BJD  Changed to platform device for SuperIO 16550s
  *     25-Jul-2005 BJD  Removed ASIX static mappings
+ *     27-Jul-2005 BJD  Ensure maximum frequency of i2c bus
 */
 
 #include <linux/kernel.h>
@@ -60,6 +61,7 @@
 #include <asm/arch/regs-mem.h>
 #include <asm/arch/regs-lcd.h>
 #include <asm/arch/nand.h>
+#include <asm/arch/iic.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -304,7 +306,7 @@ static void bast_nand_select(struct s3c2410_nand_set *set, int slot)
 }
 
 static struct s3c2410_platform_nand bast_nand_info = {
-	.tacls		= 80,
+	.tacls		= 40,
 	.twrph0		= 80,
 	.twrph1		= 80,
 	.nr_sets	= ARRAY_SIZE(bast_nand_sets),
@@ -385,6 +387,17 @@ static struct platform_device bast_sio = {
 	},
 };
 
+/* we have devices on the bus which cannot work much over the
+ * standard 100KHz i2c bus frequency
+*/
+
+static struct s3c2410_platform_i2c bast_i2c_info = {
+	.flags		= 0,
+	.slave_addr	= 0x10,
+	.bus_freq	= 100*1000,
+	.max_freq	= 130*1000,
+};
+
 /* Standard BAST devices */
 
 static struct platform_device *bast_devices[] __initdata = {
@@ -431,6 +444,7 @@ void __init bast_map_io(void)
 	s3c24xx_uclk.parent  = &s3c24xx_clkout1;
 
 	s3c_device_nand.dev.platform_data = &bast_nand_info;
+	s3c_device_i2c.dev.platform_data = &bast_i2c_info;
 
 	s3c24xx_init_io(bast_iodesc, ARRAY_SIZE(bast_iodesc));
 	s3c24xx_init_clocks(0);

+ 1 - 0
arch/arm/mach-sa1100/jornada720.c

@@ -97,6 +97,7 @@ static void __init jornada720_map_io(void)
 }
 
 MACHINE_START(JORNADA720, "HP Jornada 720")
+	/* Maintainer: Michael Gernoth <michael@gernoth.net> */
 	.phys_ram	= 0xc0000000,
 	.phys_io	= 0x80000000,
 	.io_pg_offst	= ((0xf8000000) >> 18) & 0xfffc,

+ 3 - 3
arch/arm/mm/fault.c

@@ -238,9 +238,9 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 	up_read(&mm->mmap_sem);
 
 	/*
-	 * Handle the "normal" case first
+	 * Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR
 	 */
-	if (fault > 0)
+	if (fault >= VM_FAULT_MINOR)
 		return 0;
 
 	/*
@@ -261,7 +261,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 		do_exit(SIGKILL);
 		return 0;
 
-	case 0:
+	case VM_FAULT_SIGBUS:
 		/*
 		 * We had some memory, but were unable to
 		 * successfully fix up this page fault.

+ 0 - 136
arch/arm/mm/proc-xscale.S

@@ -370,142 +370,6 @@ ENTRY(cpu_xscale_dcache_clean_area)
 	bhi	1b
 	mov	pc, lr
 
-/* ================================ CACHE LOCKING============================
- *
- * The XScale MicroArchitecture implements support for locking entries into
- * the data and instruction cache.  The following functions implement the core
- * low level instructions needed to accomplish the locking.  The developer's
- * manual states that the code that performs the locking must be in non-cached
- * memory.  To accomplish this, the code in xscale-cache-lock.c copies the
- * following functions from the cache into a non-cached memory region that
- * is allocated through consistent_alloc().
- *
- */
-	.align	5
-/*
- * xscale_icache_lock
- *
- * r0: starting address to lock
- * r1: end address to lock
- */
-ENTRY(xscale_icache_lock)
-
-iLockLoop:
-	bic	r0, r0, #CACHELINESIZE - 1
-	mcr	p15, 0, r0, c9, c1, 0	@ lock into cache
-	cmp	r0, r1			@ are we done?
-	add	r0, r0, #CACHELINESIZE	@ advance to next cache line
-	bls	iLockLoop
-	mov	pc, lr
-
-/*
- * xscale_icache_unlock
- */
-ENTRY(xscale_icache_unlock)
-	mcr	p15, 0, r0, c9, c1, 1	@ Unlock icache
-	mov	pc, lr
-
-/*
- * xscale_dcache_lock
- *
- * r0: starting address to lock
- * r1: end address to lock
- */
-ENTRY(xscale_dcache_lock)
-	mcr	p15, 0, ip, c7, c10, 4		@ Drain Write (& Fill) Buffer
-	mov	r2, #1
-	mcr	p15, 0, r2, c9, c2, 0	@ Put dcache in lock mode
-	cpwait	ip			@ Wait for completion
-
-	mrs	r2, cpsr
-	orr	r3, r2, #PSR_F_BIT | PSR_I_BIT
-dLockLoop:
-	msr	cpsr_c, r3
-	mcr	p15, 0, r0, c7, c10, 1	@ Write back line if it is dirty
-	mcr	p15, 0, r0, c7, c6, 1	@ Flush/invalidate line
-	msr	cpsr_c, r2
-	ldr	ip, [r0], #CACHELINESIZE @ Preload 32 bytes into cache from
-					@ location [r0]. Post-increment
-					@ r3 to next cache line
-	cmp	r0, r1			@ Are we done?
-	bls	dLockLoop
-
-	mcr	p15, 0, ip, c7, c10, 4		@ Drain Write (& Fill) Buffer
-	mov	r2, #0
-	mcr	p15, 0, r2, c9, c2, 0	@ Get out of lock mode
-	cpwait_ret lr, ip
-
-/*
- * xscale_dcache_unlock
- */
-ENTRY(xscale_dcache_unlock)
-	mcr	p15, 0, ip, c7, c10, 4		@ Drain Write (& Fill) Buffer
-	mcr	p15, 0, ip, c9, c2, 1	@ Unlock cache
-	mov	pc, lr
-
-/*
- * Needed to determine the length of the code that needs to be copied.
- */
-	.align	5
-ENTRY(xscale_cache_dummy)
-	mov	pc, lr
-
-/* ================================ TLB LOCKING==============================
- *
- * The XScale MicroArchitecture implements support for locking entries into
- * the Instruction and Data TLBs.  The following functions provide the
- * low level support for supporting these under Linux.  xscale-lock.c
- * implements some higher level management code.  Most of the following
- * is taken straight out of the Developer's Manual.
- */
-
-/*
- * Lock I-TLB entry
- *
- * r0: Virtual address to translate and lock
- */
-	.align	5
-ENTRY(xscale_itlb_lock)
-	mrs	r2, cpsr
-	orr	r3, r2, #PSR_F_BIT | PSR_I_BIT
-	msr	cpsr_c, r3			@ Disable interrupts
-	mcr	p15, 0, r0, c8, c5, 1		@ Invalidate I-TLB entry
-	mcr	p15, 0, r0, c10, c4, 0		@ Translate and lock
-	msr	cpsr_c, r2			@ Restore interrupts
-	cpwait_ret lr, ip
-
-/*
- * Lock D-TLB entry
- *
- * r0: Virtual address to translate and lock
- */
-	.align	5
-ENTRY(xscale_dtlb_lock)
-	mrs	r2, cpsr
-	orr	r3, r2, #PSR_F_BIT | PSR_I_BIT
-	msr	cpsr_c, r3			@ Disable interrupts
-	mcr	p15, 0, r0, c8, c6, 1		@ Invalidate D-TLB entry
-	mcr	p15, 0, r0, c10, c8, 0		@ Translate and lock
-	msr	cpsr_c, r2			@ Restore interrupts
-	cpwait_ret lr, ip
-
-/*
- * Unlock all I-TLB entries
- */
-	.align	5
-ENTRY(xscale_itlb_unlock)
-	mcr	p15, 0, ip, c10, c4, 1		@ Unlock I-TLB
-	mcr	p15, 0, ip, c8, c5, 0		@ Invalidate I-TLB
-	cpwait_ret lr, ip
-
-/*
- * Unlock all D-TLB entries
- */
-ENTRY(xscale_dtlb_unlock)
-	mcr	p15, 0, ip, c10, c8, 1		@ Unlock D-TBL
-	mcr	p15, 0, ip, c8, c6, 0		@ Invalidate D-TLB
-	cpwait_ret lr, ip
-
 /* =============================== PageTable ============================== */
 
 #define PTE_CACHE_WRITE_ALLOCATE 0

+ 12 - 12
arch/arm/nwfpe/double_cpdo.c

@@ -40,17 +40,17 @@ float64 float64_arccos(float64 rFm);
 float64 float64_pow(float64 rFn, float64 rFm);
 float64 float64_pol(float64 rFn, float64 rFm);
 
-static float64 float64_rsf(float64 rFn, float64 rFm)
+static float64 float64_rsf(struct roundingData *roundData, float64 rFn, float64 rFm)
 {
-	return float64_sub(rFm, rFn);
+	return float64_sub(roundData, rFm, rFn);
 }
 
-static float64 float64_rdv(float64 rFn, float64 rFm)
+static float64 float64_rdv(struct roundingData *roundData, float64 rFn, float64 rFm)
 {
-	return float64_div(rFm, rFn);
+	return float64_div(roundData, rFm, rFn);
 }
 
-static float64 (*const dyadic_double[16])(float64 rFn, float64 rFm) = {
+static float64 (*const dyadic_double[16])(struct roundingData*, float64 rFn, float64 rFm) = {
 	[ADF_CODE >> 20] = float64_add,
 	[MUF_CODE >> 20] = float64_mul,
 	[SUF_CODE >> 20] = float64_sub,
@@ -65,12 +65,12 @@ static float64 (*const dyadic_double[16])(float64 rFn, float64 rFm) = {
 	[FRD_CODE >> 20] = float64_rdv,
 };
 
-static float64 float64_mvf(float64 rFm)
+static float64 float64_mvf(struct roundingData *roundData,float64 rFm)
 {
 	return rFm;
 }
 
-static float64 float64_mnf(float64 rFm)
+static float64 float64_mnf(struct roundingData *roundData,float64 rFm)
 {
 	union float64_components u;
 
@@ -84,7 +84,7 @@ static float64 float64_mnf(float64 rFm)
 	return u.f64;
 }
 
-static float64 float64_abs(float64 rFm)
+static float64 float64_abs(struct roundingData *roundData,float64 rFm)
 {
 	union float64_components u;
 
@@ -98,7 +98,7 @@ static float64 float64_abs(float64 rFm)
 	return u.f64;
 }
 
-static float64 (*const monadic_double[16])(float64 rFm) = {
+static float64 (*const monadic_double[16])(struct roundingData *, float64 rFm) = {
 	[MVF_CODE >> 20] = float64_mvf,
 	[MNF_CODE >> 20] = float64_mnf,
 	[ABS_CODE >> 20] = float64_abs,
@@ -108,7 +108,7 @@ static float64 (*const monadic_double[16])(float64 rFm) = {
 	[NRM_CODE >> 20] = float64_mvf,
 };
 
-unsigned int DoubleCPDO(const unsigned int opcode, FPREG * rFd)
+unsigned int DoubleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd)
 {
 	FPA11 *fpa11 = GET_FPA11();
 	float64 rFm;
@@ -151,13 +151,13 @@ unsigned int DoubleCPDO(const unsigned int opcode, FPREG * rFd)
 		}
 
 		if (dyadic_double[opc_mask_shift]) {
-			rFd->fDouble = dyadic_double[opc_mask_shift](rFn, rFm);
+			rFd->fDouble = dyadic_double[opc_mask_shift](roundData, rFn, rFm);
 		} else {
 			return 0;
 		}
 	} else {
 		if (monadic_double[opc_mask_shift]) {
-			rFd->fDouble = monadic_double[opc_mask_shift](rFm);
+			rFd->fDouble = monadic_double[opc_mask_shift](roundData, rFm);
 		} else {
 			return 0;
 		}

+ 12 - 12
arch/arm/nwfpe/extended_cpdo.c

@@ -35,17 +35,17 @@ floatx80 floatx80_arccos(floatx80 rFm);
 floatx80 floatx80_pow(floatx80 rFn, floatx80 rFm);
 floatx80 floatx80_pol(floatx80 rFn, floatx80 rFm);
 
-static floatx80 floatx80_rsf(floatx80 rFn, floatx80 rFm)
+static floatx80 floatx80_rsf(struct roundingData *roundData, floatx80 rFn, floatx80 rFm)
 {
-	return floatx80_sub(rFm, rFn);
+	return floatx80_sub(roundData, rFm, rFn);
 }
 
-static floatx80 floatx80_rdv(floatx80 rFn, floatx80 rFm)
+static floatx80 floatx80_rdv(struct roundingData *roundData, floatx80 rFn, floatx80 rFm)
 {
-	return floatx80_div(rFm, rFn);
+	return floatx80_div(roundData, rFm, rFn);
 }
 
-static floatx80 (*const dyadic_extended[16])(floatx80 rFn, floatx80 rFm) = {
+static floatx80 (*const dyadic_extended[16])(struct roundingData*, floatx80 rFn, floatx80 rFm) = {
 	[ADF_CODE >> 20] = floatx80_add,
 	[MUF_CODE >> 20] = floatx80_mul,
 	[SUF_CODE >> 20] = floatx80_sub,
@@ -60,24 +60,24 @@ static floatx80 (*const dyadic_extended[16])(floatx80 rFn, floatx80 rFm) = {
 	[FRD_CODE >> 20] = floatx80_rdv,
 };
 
-static floatx80 floatx80_mvf(floatx80 rFm)
+static floatx80 floatx80_mvf(struct roundingData *roundData, floatx80 rFm)
 {
 	return rFm;
 }
 
-static floatx80 floatx80_mnf(floatx80 rFm)
+static floatx80 floatx80_mnf(struct roundingData *roundData, floatx80 rFm)
 {
 	rFm.high ^= 0x8000;
 	return rFm;
 }
 
-static floatx80 floatx80_abs(floatx80 rFm)
+static floatx80 floatx80_abs(struct roundingData *roundData, floatx80 rFm)
 {
 	rFm.high &= 0x7fff;
 	return rFm;
 }
 
-static floatx80 (*const monadic_extended[16])(floatx80 rFm) = {
+static floatx80 (*const monadic_extended[16])(struct roundingData*, floatx80 rFm) = {
 	[MVF_CODE >> 20] = floatx80_mvf,
 	[MNF_CODE >> 20] = floatx80_mnf,
 	[ABS_CODE >> 20] = floatx80_abs,
@@ -87,7 +87,7 @@ static floatx80 (*const monadic_extended[16])(floatx80 rFm) = {
 	[NRM_CODE >> 20] = floatx80_mvf,
 };
 
-unsigned int ExtendedCPDO(const unsigned int opcode, FPREG * rFd)
+unsigned int ExtendedCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd)
 {
 	FPA11 *fpa11 = GET_FPA11();
 	floatx80 rFm;
@@ -138,13 +138,13 @@ unsigned int ExtendedCPDO(const unsigned int opcode, FPREG * rFd)
 		}
 
 		if (dyadic_extended[opc_mask_shift]) {
-			rFd->fExtended = dyadic_extended[opc_mask_shift](rFn, rFm);
+			rFd->fExtended = dyadic_extended[opc_mask_shift](roundData, rFn, rFm);
 		} else {
 			return 0;
 		}
 	} else {
 		if (monadic_extended[opc_mask_shift]) {
-			rFd->fExtended = monadic_extended[opc_mask_shift](rFm);
+			rFd->fExtended = monadic_extended[opc_mask_shift](roundData, rFm);
 		} else {
 			return 0;
 		}

+ 11 - 19
arch/arm/nwfpe/fpa11.c

@@ -51,48 +51,42 @@ static void resetFPA11(void)
 	fpa11->fpsr = FP_EMULATOR | BIT_AC;
 }
 
-void SetRoundingMode(const unsigned int opcode)
+int8 SetRoundingMode(const unsigned int opcode)
 {
 	switch (opcode & MASK_ROUNDING_MODE) {
 	default:
 	case ROUND_TO_NEAREST:
-		float_rounding_mode = float_round_nearest_even;
-		break;
+		return float_round_nearest_even;
 
 	case ROUND_TO_PLUS_INFINITY:
-		float_rounding_mode = float_round_up;
-		break;
+		return float_round_up;
 
 	case ROUND_TO_MINUS_INFINITY:
-		float_rounding_mode = float_round_down;
-		break;
+		return float_round_down;
 
 	case ROUND_TO_ZERO:
-		float_rounding_mode = float_round_to_zero;
-		break;
+		return float_round_to_zero;
 	}
 }
 
-void SetRoundingPrecision(const unsigned int opcode)
+int8 SetRoundingPrecision(const unsigned int opcode)
 {
 #ifdef CONFIG_FPE_NWFPE_XP
 	switch (opcode & MASK_ROUNDING_PRECISION) {
 	case ROUND_SINGLE:
-		floatx80_rounding_precision = 32;
-		break;
+		return 32;
 
 	case ROUND_DOUBLE:
-		floatx80_rounding_precision = 64;
-		break;
+		return 64;
 
 	case ROUND_EXTENDED:
-		floatx80_rounding_precision = 80;
-		break;
+		return 80;
 
 	default:
-		floatx80_rounding_precision = 80;
+		return 80;
 	}
 #endif
+	return 80;
 }
 
 void nwfpe_init_fpa(union fp_state *fp)
@@ -103,8 +97,6 @@ void nwfpe_init_fpa(union fp_state *fp)
 #endif
  	memset(fpa11, 0, sizeof(FPA11));
 	resetFPA11();
-	SetRoundingMode(ROUND_TO_NEAREST);
-	SetRoundingPrecision(ROUND_EXTENDED);
 	fpa11->initflag = 1;
 }
 

+ 9 - 2
arch/arm/nwfpe/fpa11.h

@@ -37,6 +37,13 @@
 /* includes */
 #include "fpsr.h"		/* FP control and status register definitions */
 #include "milieu.h"
+
+struct roundingData {
+    int8 mode;
+    int8 precision;
+    signed char exception;
+};
+
 #include "softfloat.h"
 
 #define		typeNone		0x00
@@ -84,8 +91,8 @@ typedef struct tagFPA11 {
 				   initialised. */
 } FPA11;
 
-extern void SetRoundingMode(const unsigned int);
-extern void SetRoundingPrecision(const unsigned int);
+extern int8 SetRoundingMode(const unsigned int);
+extern int8 SetRoundingPrecision(const unsigned int);
 extern void nwfpe_init_fpa(union fp_state *fp);
 
 #endif

+ 17 - 11
arch/arm/nwfpe/fpa11_cpdo.c

@@ -24,15 +24,16 @@
 #include "fpa11.h"
 #include "fpopcode.h"
 
-unsigned int SingleCPDO(const unsigned int opcode, FPREG * rFd);
-unsigned int DoubleCPDO(const unsigned int opcode, FPREG * rFd);
-unsigned int ExtendedCPDO(const unsigned int opcode, FPREG * rFd);
+unsigned int SingleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
+unsigned int DoubleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
+unsigned int ExtendedCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd);
 
 unsigned int EmulateCPDO(const unsigned int opcode)
 {
 	FPA11 *fpa11 = GET_FPA11();
 	FPREG *rFd;
 	unsigned int nType, nDest, nRc;
+	struct roundingData roundData;
 
 	/* Get the destination size.  If not valid let Linux perform
 	   an invalid instruction trap. */
@@ -40,7 +41,9 @@ unsigned int EmulateCPDO(const unsigned int opcode)
 	if (typeNone == nDest)
 		return 0;
 
-	SetRoundingMode(opcode);
+	roundData.mode = SetRoundingMode(opcode);
+	roundData.precision = SetRoundingPrecision(opcode);
+	roundData.exception = 0;
 
 	/* Compare the size of the operands in Fn and Fm.
 	   Choose the largest size and perform operations in that size,
@@ -63,14 +66,14 @@ unsigned int EmulateCPDO(const unsigned int opcode)
 
 	switch (nType) {
 	case typeSingle:
-		nRc = SingleCPDO(opcode, rFd);
+		nRc = SingleCPDO(&roundData, opcode, rFd);
 		break;
 	case typeDouble:
-		nRc = DoubleCPDO(opcode, rFd);
+		nRc = DoubleCPDO(&roundData, opcode, rFd);
 		break;
 #ifdef CONFIG_FPE_NWFPE_XP
 	case typeExtended:
-		nRc = ExtendedCPDO(opcode, rFd);
+		nRc = ExtendedCPDO(&roundData, opcode, rFd);
 		break;
 #endif
 	default:
@@ -93,9 +96,9 @@ unsigned int EmulateCPDO(const unsigned int opcode)
 			case typeSingle:
 				{
 					if (typeDouble == nType)
-						rFd->fSingle = float64_to_float32(rFd->fDouble);
+						rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble);
 					else
-						rFd->fSingle = floatx80_to_float32(rFd->fExtended);
+						rFd->fSingle = floatx80_to_float32(&roundData, rFd->fExtended);
 				}
 				break;
 
@@ -104,7 +107,7 @@ unsigned int EmulateCPDO(const unsigned int opcode)
 					if (typeSingle == nType)
 						rFd->fDouble = float32_to_float64(rFd->fSingle);
 					else
-						rFd->fDouble = floatx80_to_float64(rFd->fExtended);
+						rFd->fDouble = floatx80_to_float64(&roundData, rFd->fExtended);
 				}
 				break;
 
@@ -121,12 +124,15 @@ unsigned int EmulateCPDO(const unsigned int opcode)
 #else
 		if (nDest != nType) {
 			if (nDest == typeSingle)
-				rFd->fSingle = float64_to_float32(rFd->fDouble);
+				rFd->fSingle = float64_to_float32(&roundData, rFd->fDouble);
 			else
 				rFd->fDouble = float32_to_float64(rFd->fSingle);
 		}
 #endif
 	}
 
+	if (roundData.exception)
+		float_raise(roundData.exception);
+
 	return nRc;
 }

+ 14 - 8
arch/arm/nwfpe/fpa11_cpdt.c

@@ -96,7 +96,7 @@ static inline void loadMultiple(const unsigned int Fn, const unsigned int __user
 	}
 }
 
-static inline void storeSingle(const unsigned int Fn, unsigned int __user *pMem)
+static inline void storeSingle(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
 {
 	FPA11 *fpa11 = GET_FPA11();
 	union {
@@ -106,12 +106,12 @@ static inline void storeSingle(const unsigned int Fn, unsigned int __user *pMem)
 
 	switch (fpa11->fType[Fn]) {
 	case typeDouble:
-		val.f = float64_to_float32(fpa11->fpreg[Fn].fDouble);
+		val.f = float64_to_float32(roundData, fpa11->fpreg[Fn].fDouble);
 		break;
 
 #ifdef CONFIG_FPE_NWFPE_XP
 	case typeExtended:
-		val.f = floatx80_to_float32(fpa11->fpreg[Fn].fExtended);
+		val.f = floatx80_to_float32(roundData, fpa11->fpreg[Fn].fExtended);
 		break;
 #endif
 
@@ -122,7 +122,7 @@ static inline void storeSingle(const unsigned int Fn, unsigned int __user *pMem)
 	put_user(val.i[0], pMem);
 }
 
-static inline void storeDouble(const unsigned int Fn, unsigned int __user *pMem)
+static inline void storeDouble(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem)
 {
 	FPA11 *fpa11 = GET_FPA11();
 	union {
@@ -137,7 +137,7 @@ static inline void storeDouble(const unsigned int Fn, unsigned int __user *pMem)
 
 #ifdef CONFIG_FPE_NWFPE_XP
 	case typeExtended:
-		val.f = floatx80_to_float64(fpa11->fpreg[Fn].fExtended);
+		val.f = floatx80_to_float64(roundData, fpa11->fpreg[Fn].fExtended);
 		break;
 #endif
 
@@ -259,8 +259,11 @@ unsigned int PerformSTF(const unsigned int opcode)
 {
 	unsigned int __user *pBase, *pAddress, *pFinal;
 	unsigned int nRc = 1, write_back = WRITE_BACK(opcode);
+	struct roundingData roundData;
 
-	SetRoundingMode(ROUND_TO_NEAREST);
+	roundData.mode = SetRoundingMode(opcode);
+	roundData.precision = SetRoundingPrecision(opcode);
+	roundData.exception = 0;
 
 	pBase = (unsigned int __user *) readRegister(getRn(opcode));
 	if (REG_PC == getRn(opcode)) {
@@ -281,10 +284,10 @@ unsigned int PerformSTF(const unsigned int opcode)
 
 	switch (opcode & MASK_TRANSFER_LENGTH) {
 	case TRANSFER_SINGLE:
-		storeSingle(getFd(opcode), pAddress);
+		storeSingle(&roundData, getFd(opcode), pAddress);
 		break;
 	case TRANSFER_DOUBLE:
-		storeDouble(getFd(opcode), pAddress);
+		storeDouble(&roundData, getFd(opcode), pAddress);
 		break;
 #ifdef CONFIG_FPE_NWFPE_XP
 	case TRANSFER_EXTENDED:
@@ -295,6 +298,9 @@ unsigned int PerformSTF(const unsigned int opcode)
 		nRc = 0;
 	}
 
+	if (roundData.exception)
+		float_raise(roundData.exception);
+
 	if (write_back)
 		writeRegister(getRn(opcode), (unsigned long) pFinal);
 	return nRc;

+ 19 - 9
arch/arm/nwfpe/fpa11_cprt.c

@@ -33,8 +33,6 @@ extern flag floatx80_is_nan(floatx80);
 extern flag float64_is_nan(float64);
 extern flag float32_is_nan(float32);
 
-void SetRoundingMode(const unsigned int opcode);
-
 unsigned int PerformFLT(const unsigned int opcode);
 unsigned int PerformFIX(const unsigned int opcode);
 
@@ -77,14 +75,17 @@ unsigned int EmulateCPRT(const unsigned int opcode)
 unsigned int PerformFLT(const unsigned int opcode)
 {
 	FPA11 *fpa11 = GET_FPA11();
-	SetRoundingMode(opcode);
-	SetRoundingPrecision(opcode);
+	struct roundingData roundData;
+
+	roundData.mode = SetRoundingMode(opcode);
+	roundData.precision = SetRoundingPrecision(opcode);
+	roundData.exception = 0;
 
 	switch (opcode & MASK_ROUNDING_PRECISION) {
 	case ROUND_SINGLE:
 		{
 			fpa11->fType[getFn(opcode)] = typeSingle;
-			fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(readRegister(getRd(opcode)));
+			fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(&roundData, readRegister(getRd(opcode)));
 		}
 		break;
 
@@ -108,6 +109,9 @@ unsigned int PerformFLT(const unsigned int opcode)
 		return 0;
 	}
 
+	if (roundData.exception)
+		float_raise(roundData.exception);
+
 	return 1;
 }
 
@@ -115,26 +119,29 @@ unsigned int PerformFIX(const unsigned int opcode)
 {
 	FPA11 *fpa11 = GET_FPA11();
 	unsigned int Fn = getFm(opcode);
+	struct roundingData roundData;
 
-	SetRoundingMode(opcode);
+	roundData.mode = SetRoundingMode(opcode);
+	roundData.precision = SetRoundingPrecision(opcode);
+	roundData.exception = 0;
 
 	switch (fpa11->fType[Fn]) {
 	case typeSingle:
 		{
-			writeRegister(getRd(opcode), float32_to_int32(fpa11->fpreg[Fn].fSingle));
+			writeRegister(getRd(opcode), float32_to_int32(&roundData, fpa11->fpreg[Fn].fSingle));
 		}
 		break;
 
 	case typeDouble:
 		{
-			writeRegister(getRd(opcode), float64_to_int32(fpa11->fpreg[Fn].fDouble));
+			writeRegister(getRd(opcode), float64_to_int32(&roundData, fpa11->fpreg[Fn].fDouble));
 		}
 		break;
 
 #ifdef CONFIG_FPE_NWFPE_XP
 	case typeExtended:
 		{
-			writeRegister(getRd(opcode), floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
+			writeRegister(getRd(opcode), floatx80_to_int32(&roundData, fpa11->fpreg[Fn].fExtended));
 		}
 		break;
 #endif
@@ -143,6 +150,9 @@ unsigned int PerformFIX(const unsigned int opcode)
 		return 0;
 	}
 
+	if (roundData.exception)
+		float_raise(roundData.exception);
+
 	return 1;
 }
 

+ 6 - 9
arch/arm/nwfpe/fpmodule.c

@@ -116,8 +116,6 @@ fpmodule.c to integrate with the NetBSD kernel (I hope!).
 code to access data in user space in some other source files at the 
 moment (grep for get_user / put_user calls).  --philb]
 
-float_exception_flags is a global variable in SoftFloat.
-
 This function is called by the SoftFloat routines to raise a floating
 point exception.  We check the trap enable byte in the FPSR, and raise
 a SIGFPE exception if necessary.  If not the relevant bits in the 
@@ -129,15 +127,14 @@ void float_raise(signed char flags)
 	register unsigned int fpsr, cumulativeTraps;
 
 #ifdef CONFIG_DEBUG_USER
-	printk(KERN_DEBUG
-	       "NWFPE: %s[%d] takes exception %08x at %p from %08lx\n",
-	       current->comm, current->pid, flags,
-	       __builtin_return_address(0), GET_USERREG()->ARM_pc);
+ 	/* Ignore inexact errors as there are far too many of them to log */
+ 	if (flags & ~BIT_IXC)
+ 		printk(KERN_DEBUG
+		       "NWFPE: %s[%d] takes exception %08x at %p from %08lx\n",
+		       current->comm, current->pid, flags,
+		       __builtin_return_address(0), GET_USERREG()->ARM_pc);
 #endif
 
-	/* Keep SoftFloat exception flags up to date.  */
-	float_exception_flags |= flags;
-
 	/* Read fpsr and initialize the cumulativeTraps.  */
 	fpsr = readFPSR();
 	cumulativeTraps = 0;

+ 12 - 12
arch/arm/nwfpe/single_cpdo.c

@@ -36,17 +36,17 @@ float32 float32_arccos(float32 rFm);
 float32 float32_pow(float32 rFn, float32 rFm);
 float32 float32_pol(float32 rFn, float32 rFm);
 
-static float32 float32_rsf(float32 rFn, float32 rFm)
+static float32 float32_rsf(struct roundingData *roundData, float32 rFn, float32 rFm)
 {
-	return float32_sub(rFm, rFn);
+	return float32_sub(roundData, rFm, rFn);
 }
 
-static float32 float32_rdv(float32 rFn, float32 rFm)
+static float32 float32_rdv(struct roundingData *roundData, float32 rFn, float32 rFm)
 {
-	return float32_div(rFm, rFn);
+	return float32_div(roundData, rFm, rFn);
 }
 
-static float32 (*const dyadic_single[16])(float32 rFn, float32 rFm) = {
+static float32 (*const dyadic_single[16])(struct roundingData *, float32 rFn, float32 rFm) = {
 	[ADF_CODE >> 20] = float32_add,
 	[MUF_CODE >> 20] = float32_mul,
 	[SUF_CODE >> 20] = float32_sub,
@@ -60,22 +60,22 @@ static float32 (*const dyadic_single[16])(float32 rFn, float32 rFm) = {
 	[FRD_CODE >> 20] = float32_rdv,
 };
 
-static float32 float32_mvf(float32 rFm)
+static float32 float32_mvf(struct roundingData *roundData, float32 rFm)
 {
 	return rFm;
 }
 
-static float32 float32_mnf(float32 rFm)
+static float32 float32_mnf(struct roundingData *roundData, float32 rFm)
 {
 	return rFm ^ 0x80000000;
 }
 
-static float32 float32_abs(float32 rFm)
+static float32 float32_abs(struct roundingData *roundData, float32 rFm)
 {
 	return rFm & 0x7fffffff;
 }
 
-static float32 (*const monadic_single[16])(float32 rFm) = {
+static float32 (*const monadic_single[16])(struct roundingData*, float32 rFm) = {
 	[MVF_CODE >> 20] = float32_mvf,
 	[MNF_CODE >> 20] = float32_mnf,
 	[ABS_CODE >> 20] = float32_abs,
@@ -85,7 +85,7 @@ static float32 (*const monadic_single[16])(float32 rFm) = {
 	[NRM_CODE >> 20] = float32_mvf,
 };
 
-unsigned int SingleCPDO(const unsigned int opcode, FPREG * rFd)
+unsigned int SingleCPDO(struct roundingData *roundData, const unsigned int opcode, FPREG * rFd)
 {
 	FPA11 *fpa11 = GET_FPA11();
 	float32 rFm;
@@ -108,13 +108,13 @@ unsigned int SingleCPDO(const unsigned int opcode, FPREG * rFd)
 		if (fpa11->fType[Fn] == typeSingle &&
 		    dyadic_single[opc_mask_shift]) {
 			rFn = fpa11->fpreg[Fn].fSingle;
-			rFd->fSingle = dyadic_single[opc_mask_shift](rFn, rFm);
+			rFd->fSingle = dyadic_single[opc_mask_shift](roundData, rFn, rFm);
 		} else {
 			return 0;
 		}
 	} else {
 		if (monadic_single[opc_mask_shift]) {
-			rFd->fSingle = monadic_single[opc_mask_shift](rFm);
+			rFd->fSingle = monadic_single[opc_mask_shift](roundData, rFm);
 		} else {
 			return 0;
 		}

Файловите разлики са ограничени, защото са твърде много
+ 123 - 132
arch/arm/nwfpe/softfloat.c


+ 29 - 39
arch/arm/nwfpe/softfloat.h

@@ -74,7 +74,7 @@ enum {
 Software IEC/IEEE floating-point rounding mode.
 -------------------------------------------------------------------------------
 */
-extern signed char float_rounding_mode;
+//extern int8 float_rounding_mode;
 enum {
     float_round_nearest_even = 0,
     float_round_to_zero      = 1,
@@ -86,7 +86,6 @@ enum {
 -------------------------------------------------------------------------------
 Software IEC/IEEE floating-point exception flags.
 -------------------------------------------------------------------------------
-extern signed char float_exception_flags;
 enum {
     float_flag_inexact   =  1,
     float_flag_underflow =  2,
@@ -99,7 +98,6 @@ ScottB: November 4, 1998
 Changed the enumeration to match the bit order in the FPA11.
 */
 
-extern signed char float_exception_flags;
 enum {
     float_flag_invalid   =  1,
     float_flag_divbyzero =  2,
@@ -121,7 +119,7 @@ void float_raise( signed char );
 Software IEC/IEEE integer-to-floating-point conversion routines.
 -------------------------------------------------------------------------------
 */
-float32 int32_to_float32( signed int );
+float32 int32_to_float32( struct roundingData *, signed int );
 float64 int32_to_float64( signed int );
 #ifdef FLOATX80
 floatx80 int32_to_floatx80( signed int );
@@ -132,7 +130,7 @@ floatx80 int32_to_floatx80( signed int );
 Software IEC/IEEE single-precision conversion routines.
 -------------------------------------------------------------------------------
 */
-signed int float32_to_int32( float32 );
+signed int float32_to_int32( struct roundingData *, float32 );
 signed int float32_to_int32_round_to_zero( float32 );
 float64 float32_to_float64( float32 );
 #ifdef FLOATX80
@@ -144,13 +142,13 @@ floatx80 float32_to_floatx80( float32 );
 Software IEC/IEEE single-precision operations.
 -------------------------------------------------------------------------------
 */
-float32 float32_round_to_int( float32 );
-float32 float32_add( float32, float32 );
-float32 float32_sub( float32, float32 );
-float32 float32_mul( float32, float32 );
-float32 float32_div( float32, float32 );
-float32 float32_rem( float32, float32 );
-float32 float32_sqrt( float32 );
+float32 float32_round_to_int( struct roundingData*, float32 );
+float32 float32_add( struct roundingData *, float32, float32 );
+float32 float32_sub( struct roundingData *, float32, float32 );
+float32 float32_mul( struct roundingData *, float32, float32 );
+float32 float32_div( struct roundingData *, float32, float32 );
+float32 float32_rem( struct roundingData *, float32, float32 );
+float32 float32_sqrt( struct roundingData*, float32 );
 char float32_eq( float32, float32 );
 char float32_le( float32, float32 );
 char float32_lt( float32, float32 );
@@ -164,9 +162,9 @@ char float32_is_signaling_nan( float32 );
 Software IEC/IEEE double-precision conversion routines.
 -------------------------------------------------------------------------------
 */
-signed int float64_to_int32( float64 );
+signed int float64_to_int32( struct roundingData *, float64 );
 signed int float64_to_int32_round_to_zero( float64 );
-float32 float64_to_float32( float64 );
+float32 float64_to_float32( struct roundingData *, float64 );
 #ifdef FLOATX80
 floatx80 float64_to_floatx80( float64 );
 #endif
@@ -176,13 +174,13 @@ floatx80 float64_to_floatx80( float64 );
 Software IEC/IEEE double-precision operations.
 -------------------------------------------------------------------------------
 */
-float64 float64_round_to_int( float64 );
-float64 float64_add( float64, float64 );
-float64 float64_sub( float64, float64 );
-float64 float64_mul( float64, float64 );
-float64 float64_div( float64, float64 );
-float64 float64_rem( float64, float64 );
-float64 float64_sqrt( float64 );
+float64 float64_round_to_int( struct roundingData *, float64 );
+float64 float64_add( struct roundingData *, float64, float64 );
+float64 float64_sub( struct roundingData *, float64, float64 );
+float64 float64_mul( struct roundingData *, float64, float64 );
+float64 float64_div( struct roundingData *, float64, float64 );
+float64 float64_rem( struct roundingData *, float64, float64 );
+float64 float64_sqrt( struct roundingData *, float64 );
 char float64_eq( float64, float64 );
 char float64_le( float64, float64 );
 char float64_lt( float64, float64 );
@@ -198,31 +196,23 @@ char float64_is_signaling_nan( float64 );
 Software IEC/IEEE extended double-precision conversion routines.
 -------------------------------------------------------------------------------
 */
-signed int floatx80_to_int32( floatx80 );
+signed int floatx80_to_int32( struct roundingData *, floatx80 );
 signed int floatx80_to_int32_round_to_zero( floatx80 );
-float32 floatx80_to_float32( floatx80 );
-float64 floatx80_to_float64( floatx80 );
-
-/*
--------------------------------------------------------------------------------
-Software IEC/IEEE extended double-precision rounding precision.  Valid
-values are 32, 64, and 80.
--------------------------------------------------------------------------------
-*/
-extern signed char floatx80_rounding_precision;
+float32 floatx80_to_float32( struct roundingData *, floatx80 );
+float64 floatx80_to_float64( struct roundingData *, floatx80 );
 
 /*
 -------------------------------------------------------------------------------
 Software IEC/IEEE extended double-precision operations.
 -------------------------------------------------------------------------------
 */
-floatx80 floatx80_round_to_int( floatx80 );
-floatx80 floatx80_add( floatx80, floatx80 );
-floatx80 floatx80_sub( floatx80, floatx80 );
-floatx80 floatx80_mul( floatx80, floatx80 );
-floatx80 floatx80_div( floatx80, floatx80 );
-floatx80 floatx80_rem( floatx80, floatx80 );
-floatx80 floatx80_sqrt( floatx80 );
+floatx80 floatx80_round_to_int( struct roundingData *, floatx80 );
+floatx80 floatx80_add( struct roundingData *, floatx80, floatx80 );
+floatx80 floatx80_sub( struct roundingData *, floatx80, floatx80 );
+floatx80 floatx80_mul( struct roundingData *, floatx80, floatx80 );
+floatx80 floatx80_div( struct roundingData *, floatx80, floatx80 );
+floatx80 floatx80_rem( struct roundingData *, floatx80, floatx80 );
+floatx80 floatx80_sqrt( struct roundingData *, floatx80 );
 char floatx80_eq( floatx80, floatx80 );
 char floatx80_le( floatx80, floatx80 );
 char floatx80_lt( floatx80, floatx80 );

+ 1 - 1
arch/arm/oprofile/backtrace.c

@@ -115,7 +115,7 @@ static int valid_kernel_stack(struct frame_tail *tail, struct pt_regs *regs)
 	return (tailaddr > stack) && (tailaddr < stack_base);
 }
 
-void arm_backtrace(struct pt_regs const *regs, unsigned int depth)
+void arm_backtrace(struct pt_regs * const regs, unsigned int depth)
 {
 	struct frame_tail *tail;
 	unsigned long last_address = 0;

+ 3 - 0
arch/arm/vfp/vfpdouble.c

@@ -770,6 +770,9 @@ vfp_double_add(struct vfp_double *vdd, struct vfp_double *vdn,
 		if ((s64)m_sig < 0) {
 			vdd->sign = vfp_sign_negate(vdd->sign);
 			m_sig = -m_sig;
+		} else if (m_sig == 0) {
+			vdd->sign = (fpscr & FPSCR_RMODE_MASK) ==
+				      FPSCR_ROUND_MINUSINF ? 0x8000 : 0;
 		}
 	} else {
 		m_sig += vdn->significand;

+ 7 - 10
arch/arm26/mm/fault.c

@@ -176,12 +176,12 @@ survive:
 	 * Handle the "normal" cases first - successful and sigbus
 	 */
 	switch (fault) {
-	case 2:
+	case VM_FAULT_MAJOR:
 		tsk->maj_flt++;
 		return fault;
-	case 1:
+	case VM_FAULT_MINOR:
 		tsk->min_flt++;
-	case 0:
+	case VM_FAULT_SIGBUS:
 		return fault;
 	}
 
@@ -226,14 +226,11 @@ int do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 	/*
 	 * Handle the "normal" case first
 	 */
-	if (fault > 0)
+	switch (fault) {
+	case VM_FAULT_MINOR:
+	case VM_FAULT_MAJOR:
 		return 0;
-
-	/*
-	 * We had some memory, but were unable to
-	 * successfully fix up this page fault.
-	 */
-	if (fault == 0){
+	case VM_FAULT_SIGBUS:
 		goto do_sigbus;
 	}
 

+ 5 - 0
arch/cris/Kconfig.debug

@@ -38,4 +38,9 @@ config FRAME_POINTER
           If you don't debug the kernel, you can say N, but we may not be able
           to solve problems without frame pointers.
 
+config DEBUG_NMI_OOPS
+       bool "NMI causes oops printout"
+       help
+         If the system locks up without any debug information you can say Y
+         here to make it possible to dump an OOPS with an external NMI.
 endmenu

+ 45 - 21
arch/cris/Makefile

@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.23 2004/10/19 13:07:34 starvik Exp $
+# $Id: Makefile,v 1.28 2005/03/17 10:44:37 larsv Exp $
 # cris/Makefile
 #
 # This file is included by the global makefile so that you can add your own
@@ -15,6 +15,7 @@
 
 arch-y := v10
 arch-$(CONFIG_ETRAX_ARCH_V10) := v10
+arch-$(CONFIG_ETRAX_ARCH_V32) := v32
 
 # No config avaiable for make clean etc
 ifneq ($(arch-y),)
@@ -46,6 +47,21 @@ core-y		+= arch/$(ARCH)/$(SARCH)/kernel/ arch/$(ARCH)/$(SARCH)/mm/
 drivers-y	+= arch/$(ARCH)/$(SARCH)/drivers/
 libs-y		+= arch/$(ARCH)/$(SARCH)/lib/ $(LIBGCC)
 
+# cris source path
+SRC_ARCH              = $(srctree)/arch/$(ARCH)
+# cris object files path
+OBJ_ARCH              = $(objtree)/arch/$(ARCH)
+
+target_boot_arch_dir  = $(OBJ_ARCH)/$(SARCH)/boot
+target_boot_dir       = $(OBJ_ARCH)/boot
+src_boot_dir          = $(SRC_ARCH)/boot
+target_compressed_dir = $(OBJ_ARCH)/boot/compressed
+src_compressed_dir    = $(SRC_ARCH)/boot/compressed
+target_rescue_dir     = $(OBJ_ARCH)/boot/rescue
+src_rescue_dir        = $(SRC_ARCH)/boot/rescue
+
+export target_boot_arch_dir target_boot_dir src_boot_dir target_compressed_dir src_compressed_dir target_rescue_dir src_rescue_dir
+
 vmlinux.bin: vmlinux
 	$(OBJCOPY) $(OBJCOPYFLAGS) vmlinux vmlinux.bin
 
@@ -65,44 +81,52 @@ cramfs:
 
 clinux: vmlinux.bin decompress.bin rescue.bin
 
-decompress.bin: FORCE
-	@make -C arch/$(ARCH)/boot/compressed decompress.bin
+decompress.bin: $(target_boot_dir)
+	@$(MAKE) -f $(src_compressed_dir)/Makefile $(target_compressed_dir)/decompress.bin
 
-rescue.bin: FORCE
-	@make -C arch/$(ARCH)/boot/rescue rescue.bin
+$(target_rescue_dir)/rescue.bin: $(target_boot_dir)
+	@$(MAKE) -f $(src_rescue_dir)/Makefile $(target_rescue_dir)/rescue.bin
 
-zImage: vmlinux.bin rescue.bin
+zImage: $(target_boot_dir) vmlinux.bin $(target_rescue_dir)/rescue.bin
 ## zImage     - Compressed kernel (gzip)
-	@make -C arch/$(ARCH)/boot/ zImage
+	@$(MAKE) -f $(src_boot_dir)/Makefile zImage
+
+$(target_boot_dir): $(target_boot_arch_dir)
+	ln -sfn $< $@
+
+$(target_boot_arch_dir):
+	mkdir -p $@
 
 compressed: zImage
 
 archmrproper:
 archclean:
-	$(Q)$(MAKE) $(clean)=arch/$(ARCH)/boot
+	@if [ -d arch/$(ARCH)/boot ]; then \
+		$(MAKE) $(clean)=arch/$(ARCH)/boot ; \
+	fi
 	rm -f timage vmlinux.bin decompress.bin rescue.bin cramfs.img
 	rm -rf $(LD_SCRIPT).tmp
 
-prepare: arch/$(ARCH)/.links include/asm-$(ARCH)/.arch \
+prepare: $(SRC_ARCH)/.links $(srctree)/include/asm-$(ARCH)/.arch \
 	 include/asm-$(ARCH)/$(SARCH)/offset.h
 
 # Create some links to make all tools happy
-arch/$(ARCH)/.links:
-	@rm -rf arch/$(ARCH)/drivers
-	@ln -sfn $(SARCH)/drivers arch/$(ARCH)/drivers
-	@rm -rf arch/$(ARCH)/boot
-	@ln -sfn $(SARCH)/boot arch/$(ARCH)/boot
-	@rm -rf arch/$(ARCH)/lib
-	@ln -sfn $(SARCH)/lib arch/$(ARCH)/lib
-	@ln -sfn $(SARCH) arch/$(ARCH)/arch
-	@ln -sfn ../$(SARCH)/vmlinux.lds.S arch/$(ARCH)/kernel/vmlinux.lds.S
+$(SRC_ARCH)/.links:
+	@rm -rf $(SRC_ARCH)/drivers
+	@ln -sfn $(SRC_ARCH)/$(SARCH)/drivers $(SRC_ARCH)/drivers
+	@rm -rf $(SRC_ARCH)/boot
+	@ln -sfn $(SRC_ARCH)/$(SARCH)/boot $(SRC_ARCH)/boot
+	@rm -rf $(SRC_ARCH)/lib
+	@ln -sfn $(SRC_ARCH)/$(SARCH)/lib $(SRC_ARCH)/lib
+	@ln -sfn $(SRC_ARCH)/$(SARCH) $(SRC_ARCH)/arch
+	@ln -sfn $(SRC_ARCH)/$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S
 	@touch $@
 
 # Create link to sub arch includes
-include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h)
-	@echo '  Making asm-$(ARCH)/arch -> asm-$(ARCH)/$(SARCH) symlink'
+$(srctree)/include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h)
+	@echo '  Making $(srctree)/include/asm-$(ARCH)/arch -> $(srctree)/include/asm-$(ARCH)/$(SARCH) symlink'
 	@rm -f include/asm-$(ARCH)/arch
-	@ln -sf $(SARCH) include/asm-$(ARCH)/arch
+	@ln -sf $(srctree)/include/asm-$(ARCH)/$(SARCH) $(srctree)/include/asm-$(ARCH)/arch
 	@touch $@
 
 arch/$(ARCH)/$(SARCH)/kernel/asm-offsets.s: include/asm include/linux/version.h \

+ 31 - 0
arch/cris/arch-v10/Kconfig

@@ -259,6 +259,37 @@ config ETRAX_DEBUG_PORT_NULL
 
 endchoice
 
+choice
+	prompt "Kernel GDB port"
+	depends on ETRAX_KGDB
+	default ETRAX_KGDB_PORT0
+	help
+	  Choose a serial port for kernel debugging.  NOTE: This port should
+	  not be enabled under Drivers for built-in interfaces (as it has its
+	  own initialization code) and should not be the same as the debug port.
+
+config ETRAX_KGDB_PORT0
+	bool "Serial-0"
+	help
+	  Use serial port 0 for kernel debugging.
+
+config ETRAX_KGDB_PORT1
+	bool "Serial-1"
+	help
+	  Use serial port 1 for kernel debugging.
+
+config ETRAX_KGDB_PORT2
+	bool "Serial-2"
+	help
+	  Use serial port 2 for kernel debugging.
+
+config ETRAX_KGDB_PORT3
+	bool "Serial-3"
+	help
+	  Use serial port 3 for kernel debugging.
+
+endchoice
+
 choice
 	prompt "Product rescue-port"
 	depends on ETRAX_ARCH_V10

+ 5 - 4
arch/cris/arch-v10/boot/Makefile

@@ -1,12 +1,13 @@
 #
 # arch/cris/boot/Makefile
 #
+target = $(target_boot_dir)
+src    = $(src_boot_dir)
 
 zImage: compressed/vmlinuz
 
-compressed/vmlinuz: $(TOPDIR)/vmlinux
-	@$(MAKE) -C compressed vmlinuz
+compressed/vmlinuz:
+	@$(MAKE) -f $(src)/compressed/Makefile $(target_compressed_dir)/vmlinuz
 
 clean:
-	rm -f zImage tools/build compressed/vmlinux.out
-	@$(MAKE) -C compressed clean
+	@$(MAKE) -f $(src)/compressed/Makefile clean

+ 24 - 19
arch/cris/arch-v10/boot/compressed/Makefile

@@ -1,40 +1,45 @@
 #
-# linux/arch/etrax100/boot/compressed/Makefile
-#
-# create a compressed vmlinux image from the original vmlinux files and romfs
+# create a compressed vmlinuz image from the binary vmlinux.bin file
 #
+target = $(target_compressed_dir)
+src    = $(src_compressed_dir)
 
-CC = gcc-cris -melf -I $(TOPDIR)/include
+CC = gcc-cris -melf $(LINUXINCLUDE)
 CFLAGS = -O2
 LD = ld-cris
 OBJCOPY = objcopy-cris
 OBJCOPYFLAGS = -O binary --remove-section=.bss
-OBJECTS = head.o misc.o
+OBJECTS = $(target)/head.o $(target)/misc.o
 
 # files to compress
-SYSTEM = $(TOPDIR)/vmlinux.bin
+SYSTEM = $(objtree)/vmlinux.bin
 
-all: vmlinuz
+all: $(target_compressed_dir)/vmlinuz
 
-decompress.bin: $(OBJECTS)
-	$(LD) -T decompress.ld -o decompress.o $(OBJECTS)
-	$(OBJCOPY) $(OBJCOPYFLAGS) decompress.o decompress.bin
-# save it for mkprod in the topdir.
-	cp decompress.bin $(TOPDIR)
+$(target)/decompress.bin: $(OBJECTS)
+	$(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin
 
+# Create vmlinuz image in top-level build directory
+$(target_compressed_dir)/vmlinuz: $(target) piggy.img $(target)/decompress.bin
+	@echo "  COMPR   vmlinux.bin --> vmlinuz"
+	@cat $(target)/decompress.bin piggy.img > $(target_compressed_dir)/vmlinuz
+	@rm -f piggy.img
 
-vmlinuz: piggy.img decompress.bin
-	cat decompress.bin piggy.img > vmlinuz
-	rm -f piggy.img
+$(target)/head.o: $(src)/head.S
+	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $@
 
-head.o: head.S
-	$(CC) -D__ASSEMBLY__ -traditional -c head.S -o head.o
+$(target)/misc.o: $(src)/misc.c
+	$(CC) -D__KERNEL__ -c $< -o $@
 
 # gzip the kernel image
 
 piggy.img: $(SYSTEM)
-	cat $(SYSTEM) | gzip -f -9 > piggy.img
+	@cat $(SYSTEM) | gzip -f -9 > piggy.img
+
+$(target):
+	mkdir -p $(target)
 
 clean:
-	rm -f piggy.img vmlinuz vmlinuz.o
+	rm -f piggy.img $(objtree)/vmlinuz
 

+ 19 - 3
arch/cris/arch-v10/boot/compressed/head.S

@@ -13,7 +13,8 @@
 #include <asm/arch/sv_addr_ag.h>
 
 #define RAM_INIT_MAGIC 0x56902387
-	
+#define COMMAND_LINE_MAGIC 0x87109563
+
 	;; Exported symbols
 	
 	.globl	_input_data
@@ -88,6 +89,12 @@ basse:	move.d	pc, r5
 	cmp.d	r2, r1
 	bcs	1b
 	nop
+
+	;;  Save command line magic and address.
+	move.d	_cmd_line_magic, $r12
+	move.d  $r10, [$r12]
+	move.d	_cmd_line_addr, $r12
+	move.d  $r11, [$r12]
 	
 	;; Do the decompression and save compressed size in _inptr
 
@@ -98,7 +105,13 @@ basse:	move.d	pc, r5
 
 	move.d	[_input_data], r9	; flash address of compressed kernel
 	add.d	[_inptr], r9		; size of compressed kernel
-	 
+
+	;; Restore command line magic and address.
+	move.d  _cmd_line_magic, $r10
+	move.d  [$r10], $r10
+	move.d  _cmd_line_addr, $r11
+	move.d  [$r11], $r11
+
 	;; Enter the decompressed kernel
 	move.d	RAM_INIT_MAGIC, r8	; Tell kernel that DRAM is initialized
 	jump	0x40004000	; kernel is linked to this address
@@ -107,5 +120,8 @@ basse:	move.d	pc, r5
 
 _input_data:
 	.dword	0		; used by the decompressor
-
+_cmd_line_magic:
+	.dword 0
+_cmd_line_addr:
+	.dword 0
 #include "../../lib/hw_settings.S"

+ 23 - 22
arch/cris/arch-v10/boot/rescue/Makefile

@@ -1,52 +1,53 @@
 #
 # Makefile for rescue code
 #
-ifndef TOPDIR
-TOPDIR = ../../../..
-endif
-CC = gcc-cris -mlinux -I $(TOPDIR)/include
+target = $(target_rescue_dir)
+src    = $(src_rescue_dir)
+
+CC = gcc-cris -mlinux $(LINUXINCLUDE)
 CFLAGS = -O2
 LD = gcc-cris -mlinux -nostdlib
 OBJCOPY = objcopy-cris
 OBJCOPYFLAGS = -O binary --remove-section=.bss
 
-all: rescue.bin testrescue.bin kimagerescue.bin
-
-rescue: rescue.bin
-	# do nothing
+all: $(target)/rescue.bin $(target)/testrescue.bin $(target)/kimagerescue.bin
 
-rescue.bin: head.o
-	$(LD) -T rescue.ld -o rescue.o head.o
-	$(OBJCOPY) $(OBJCOPYFLAGS) rescue.o rescue.bin
-	cp rescue.bin $(TOPDIR)
+$(target)/rescue.bin: $(target) $(target)/head.o
+	$(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o
+	$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin
+# Place a copy in top-level build directory
+	cp -p $(target)/rescue.bin $(objtree)
 
-testrescue.bin: testrescue.o
-	$(OBJCOPY) $(OBJCOPYFLAGS) testrescue.o tr.bin
+$(target)/testrescue.bin: $(target) $(target)/testrescue.o
+	$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/testrescue.o tr.bin
 # Pad it to 784 bytes
 	dd if=/dev/zero of=tmp2423 bs=1 count=784
 	cat tr.bin tmp2423 >testrescue_tmp.bin
-	dd if=testrescue_tmp.bin of=testrescue.bin bs=1 count=784
+	dd if=testrescue_tmp.bin of=$(target)/testrescue.bin bs=1 count=784
 	rm tr.bin tmp2423 testrescue_tmp.bin
 
-kimagerescue.bin: kimagerescue.o
-	$(OBJCOPY) $(OBJCOPYFLAGS) kimagerescue.o ktr.bin
+$(target)/kimagerescue.bin: $(target) $(target)/kimagerescue.o
+	$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/kimagerescue.o ktr.bin
 # Pad it to 784 bytes, that's what the rescue loader expects
 	dd if=/dev/zero of=tmp2423 bs=1 count=784
 	cat ktr.bin tmp2423 >kimagerescue_tmp.bin
-	dd if=kimagerescue_tmp.bin of=kimagerescue.bin bs=1 count=784
+	dd if=kimagerescue_tmp.bin of=$(target)/kimagerescue.bin bs=1 count=784
 	rm ktr.bin tmp2423 kimagerescue_tmp.bin
 
-head.o: head.S
+$(target):
+	mkdir -p $(target)
+
+$(target)/head.o: $(src)/head.S
 	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
 
-testrescue.o: testrescue.S
+$(target)/testrescue.o: $(src)/testrescue.S
 	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
 
-kimagerescue.o: kimagerescue.S
+$(target)/kimagerescue.o: $(src)/kimagerescue.S
 	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
 
 clean:
-	rm -f *.o *.bin
+	rm -f $(target)/*.o $(target)/*.bin
 
 fastdep:
 

+ 27 - 6
arch/cris/arch-v10/boot/rescue/head.S

@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.6 2003/04/09 08:12:43 pkj Exp $
+/* $Id: head.S,v 1.7 2005/03/07 12:11:06 starvik Exp $
  * 
  * Rescue code, made to reside at the beginning of the
  * flash-memory. when it starts, it checks a partition
@@ -121,12 +121,13 @@
 	;; 0x80000000 if loaded in flash (as it should be)
 	;; since etrax actually starts at address 2 when booting from flash, we
 	;; put a nop (2 bytes) here first so we dont accidentally skip the di
-	
+
 	nop	
 	di
 
 	jump	in_cache	; enter cached area instead
-in_cache:	
+in_cache:
+
 
 	;; first put a jump test to give a possibility of upgrading the rescue code
 	;; without erasing/reflashing the sector. we put a longword of -1 here and if
@@ -325,9 +326,29 @@ flash_ok:
 	;; result will be in r0
 checksum:
 	moveq	0, $r0
-1:	addu.b	[$r1+], $r0
-	subq	1, $r2
-	bne	1b
+	moveq   CONFIG_ETRAX_FLASH1_SIZE, $r6
+
+	;; If the first physical flash memory is exceeded wrap to the second one.
+	btstq	26, $r1		; Are we addressing first flash?
+	bpl	1f
+	nop
+	clear.d	$r6
+
+1:	test.d  $r6		; 0 = no wrapping
+	beq	2f
+	nop
+	lslq	20, $r6		; Convert MB to bytes
+	sub.d	$r1, $r6
+
+2:	addu.b	[$r1+], $r0
+	subq	1, $r6		; Flash memory left
+	beq	3f
+	subq	1, $r2		; Length left
+	bne	2b
 	nop
 	ret
 	nop
+
+3:	move.d	MEM_CSE1_START, $r1 ; wrap to second flash
+	ba	2b
+	nop

+ 67 - 183
arch/cris/arch-v10/drivers/Kconfig

@@ -1,17 +1,11 @@
 config ETRAX_ETHERNET
 	bool "Ethernet support"
 	depends on ETRAX_ARCH_V10
+	select NET_ETHERNET
 	help
 	  This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet
 	  controller.
 
-# this is just so that the user does not have to go into the
-# normal ethernet driver section just to enable ethernetworking
-config NET_ETHERNET
-	bool
-	depends on ETRAX_ETHERNET
-	default y
-
 choice
 	prompt "Network LED behavior"
 	depends on ETRAX_ETHERNET
@@ -20,26 +14,26 @@ choice
 config ETRAX_NETWORK_LED_ON_WHEN_LINK
 	bool "LED_on_when_link"
 	help
-	  Selecting LED_on_when_link will light the LED when there is a 
-	  connection and will flash off when there is activity. 
+	  Selecting LED_on_when_link will light the LED when there is a
+	  connection and will flash off when there is activity.
 
-	  Selecting LED_on_when_activity will light the LED only when 
+	  Selecting LED_on_when_activity will light the LED only when
 	  there is activity.
 
-	  This setting will also affect the behaviour of other activity LEDs 
-	  e.g. Bluetooth. 
+	  This setting will also affect the behaviour of other activity LEDs
+	  e.g. Bluetooth.
 
 config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
 	bool "LED_on_when_activity"
 	help
-	  Selecting LED_on_when_link will light the LED when there is a 
-	  connection and will flash off when there is activity. 
+	  Selecting LED_on_when_link will light the LED when there is a
+	  connection and will flash off when there is activity.
 
-	  Selecting LED_on_when_activity will light the LED only when 
+	  Selecting LED_on_when_activity will light the LED only when
 	  there is activity.
 
-	  This setting will also affect the behaviour of other activity LEDs 
-	  e.g. Bluetooth. 
+	  This setting will also affect the behaviour of other activity LEDs
+	  e.g. Bluetooth.
 
 endchoice
 
@@ -91,11 +85,11 @@ choice
 	depends on ETRAX_SERIAL_PORT0
 	default ETRAX_SERIAL_PORT0_DMA6_OUT
 
-config CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_OUT
-       bool "No DMA out"
+config ETRAX_SERIAL_PORT0_NO_DMA_OUT
+	bool "No DMA out"
 
-config CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
-       bool "DMA 6"
+config ETRAX_SERIAL_PORT0_DMA6_OUT
+	bool "DMA 6"
 
 endchoice
 
@@ -104,11 +98,11 @@ choice
 	depends on ETRAX_SERIAL_PORT0
 	default ETRAX_SERIAL_PORT0_DMA7_IN
 
-config CONFIG_ETRAX_SERIAL_PORT0_NO_DMA_IN
-       bool "No DMA in"
+config ETRAX_SERIAL_PORT0_NO_DMA_IN
+	bool "No DMA in"
 
-config CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
-       bool "DMA 7"
+config ETRAX_SERIAL_PORT0_DMA7_IN
+	bool "DMA 7"
 
 endchoice
 
@@ -205,11 +199,11 @@ choice
 	depends on ETRAX_SERIAL_PORT1
 	default ETRAX_SERIAL_PORT1_DMA8_OUT
 
-config CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_OUT
-       bool "No DMA out"
+config ETRAX_SERIAL_PORT1_NO_DMA_OUT
+	bool "No DMA out"
 
-config CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
-       bool "DMA 8"
+config ETRAX_SERIAL_PORT1_DMA8_OUT
+	bool "DMA 8"
 
 endchoice
 
@@ -218,11 +212,11 @@ choice
 	depends on ETRAX_SERIAL_PORT1
 	default ETRAX_SERIAL_PORT1_DMA9_IN
 
-config CONFIG_ETRAX_SERIAL_PORT1_NO_DMA_IN
-       bool "No DMA in"
+config ETRAX_SERIAL_PORT1_NO_DMA_IN
+	bool "No DMA in"
 
-config CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
-       bool "DMA 9"
+config ETRAX_SERIAL_PORT1_DMA9_IN
+	bool "DMA 9"
 
 endchoice
 
@@ -308,7 +302,7 @@ config ETRAX_SER1_CD_ON_PB_BIT
 	  Specify the pin of the PB port to carry the CD signal for serial
 	  port 1.
 
-comment "Make sure you dont have the same PB bits more than once!"
+comment "Make sure you do not have the same PB bits more than once!"
 	depends on ETRAX_SERIAL && ETRAX_SER0_DTR_RI_DSR_CD_ON_PB && ETRAX_SER1_DTR_RI_DSR_CD_ON_PB
 
 config ETRAX_SERIAL_PORT2
@@ -322,11 +316,11 @@ choice
 	depends on ETRAX_SERIAL_PORT2
 	default ETRAX_SERIAL_PORT2_DMA2_OUT
 
-config CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_OUT
-       bool "No DMA out"
+config ETRAX_SERIAL_PORT2_NO_DMA_OUT
+	bool "No DMA out"
 
-config CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
-       bool "DMA 2"
+config ETRAX_SERIAL_PORT2_DMA2_OUT
+	bool "DMA 2"
 
 endchoice
 
@@ -335,11 +329,11 @@ choice
 	depends on ETRAX_SERIAL_PORT2
 	default ETRAX_SERIAL_PORT2_DMA3_IN
 
-config CONFIG_ETRAX_SERIAL_PORT2_NO_DMA_IN
-       bool "No DMA in"
+config ETRAX_SERIAL_PORT2_NO_DMA_IN
+	bool "No DMA in"
 
-config CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
-       bool "DMA 3"
+config ETRAX_SERIAL_PORT2_DMA3_IN
+	bool "DMA 3"
 
 endchoice
 
@@ -436,11 +430,11 @@ choice
 	depends on ETRAX_SERIAL_PORT3
 	default ETRAX_SERIAL_PORT3_DMA4_OUT
 
-config CONFIG_ETRAX_SERIAL_PORT3_NO_DMA_OUT
-       bool "No DMA out"
+config ETRAX_SERIAL_PORT3_NO_DMA_OUT
+	bool "No DMA out"
 
-config CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
-       bool "DMA 4"
+config ETRAX_SERIAL_PORT3_DMA4_OUT
+	bool "DMA 4"
 
 endchoice
 
@@ -449,11 +443,11 @@ choice
 	depends on ETRAX_SERIAL_PORT3
 	default ETRAX_SERIAL_PORT3_DMA5_IN
 
-config CONFIG_ETRAX_SERIAL_PORT3_NO_DMA_IN
-       bool "No DMA in"
+config ETRAX_SERIAL_PORT3_NO_DMA_IN
+	bool "No DMA in"
 
-config CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
-       bool "DMA 5"
+config ETRAX_SERIAL_PORT3_DMA5_IN
+	bool "DMA 5"
 
 endchoice
 
@@ -554,7 +548,6 @@ config ETRAX_IDE
 	select BLK_DEV_IDEDISK
 	select BLK_DEV_IDECD
 	select BLK_DEV_IDEDMA
-	select DMA_NONPCI
 	help
 	  Enable this to get support for ATA/IDE.
 	  You can't use paralell ports or SCSI ports
@@ -579,7 +572,7 @@ config ETRAX_IDE_PB7_RESET
 	  IDE reset on pin 7 on port B
 
 config ETRAX_IDE_G27_RESET
-        bool "Port_G_Bit_27"
+	bool "Port_G_Bit_27"
 	help
 	  IDE reset on pin 27 on port G
 
@@ -588,30 +581,36 @@ endchoice
 
 config ETRAX_USB_HOST
 	bool "USB host"
+	select USB
 	help
 	   This option enables the host functionality of the ETRAX 100LX
 	   built-in USB controller. In host mode the controller is designed
 	   for CTRL and BULK traffic only, INTR traffic may work as well
 	   however (depending on the requirements of timeliness).
 
-config USB
-       tristate
-       depends on ETRAX_USB_HOST
-       default y
-
 config ETRAX_USB_HOST_PORT1
-       bool "  USB port 1 enabled"
-       depends on ETRAX_USB_HOST
-       default n
+	bool "USB port 1 enabled"
+	depends on ETRAX_USB_HOST
+	default n
 
 config ETRAX_USB_HOST_PORT2
-       bool "  USB port 2 enabled"
-       depends on ETRAX_USB_HOST
-       default n
+	bool "USB port 2 enabled"
+	depends on ETRAX_USB_HOST
+	default n
 
 config ETRAX_AXISFLASHMAP
 	bool "Axis flash-map support"
 	depends on ETRAX_ARCH_V10
+	select MTD
+	select MTD_CFI
+	select MTD_CFI_AMDSTD
+	select MTD_OBSOLETE_CHIPS
+	select MTD_AMDSTD
+	select MTD_CHAR
+	select MTD_BLOCK
+	select MTD_PARTITIONS
+	select MTD_CONCAT
+	select MTD_COMPLEX_MAPPINGS
 	help
 	  This option enables MTD mapping of flash devices.  Needed to use
 	  flash memories.  If unsure, say Y.
@@ -627,119 +626,6 @@ config ETRAX_PTABLE_SECTOR
 	  for changing this is when the flash block size is bigger
 	  than 64kB (e.g. when using two parallel 16 bit flashes).
 
-# here we define the CONFIG_'s necessary to enable MTD support
-# for the flash
-config MTD
-	tristate
-	depends on ETRAX_AXISFLASHMAP
-	default y
-	help
-	  Memory Technology Devices are flash, RAM and similar chips, often
-	  used for solid state file systems on embedded devices. This option
-	  will provide the generic support for MTD drivers to register
-	  themselves with the kernel and for potential users of MTD devices
-	  to enumerate the devices which are present and obtain a handle on
-	  them. It will also allow you to select individual drivers for 
-	  particular hardware and users of MTD devices. If unsure, say N.
-
-config MTD_CFI
-	tristate
-	depends on ETRAX_AXISFLASHMAP
-	default y
-	help
-	  The Common Flash Interface specification was developed by Intel,
-	  AMD and other flash manufactures that provides a universal method
-	  for probing the capabilities of flash devices. If you wish to
-	  support any device that is CFI-compliant, you need to enable this
-	  option. Visit <http://www.amd.com/products/nvd/overview/cfi.html>
-	  for more information on CFI.
-
-config MTD_CFI_AMDSTD
-	tristate
-	depends on ETRAX_AXISFLASHMAP
-	default y
-	help
-	  The Common Flash Interface defines a number of different command
-	  sets which a CFI-compliant chip may claim to implement. This code
-	  provides support for one of those command sets, used on chips 
-	  chips including the AMD Am29LV320.
-
-config MTD_OBSOLETE_CHIPS
-	bool
-	depends on ETRAX_AXISFLASHMAP
-	default y
-	help
-	  This option does not enable any code directly, but will allow you to
-	  select some other chip drivers which are now considered obsolete,
-	  because the generic CONFIG_JEDEC_PROBE code above should now detect
-	  the chips which are supported by these drivers, and allow the generic
-	  CFI-compatible drivers to drive the chips. Say 'N' here unless you have
-	  already tried the CONFIG_JEDEC_PROBE method and reported its failure
-	  to the MTD mailing list at <linux-mtd@lists.infradead.org>
-
-config MTD_AMDSTD
-	tristate
-	depends on ETRAX_AXISFLASHMAP
-	default y
-	help
-	  This option enables support for flash chips using AMD-compatible
-	  commands, including some which are not CFI-compatible and hence 
-	  cannot be used with the CONFIG_MTD_CFI_AMDSTD option.
-
-	  It also works on AMD compatible chips that do conform to CFI.
-
-config MTD_CHAR
-	tristate
-	depends on ETRAX_AXISFLASHMAP
-	default y
-	help
-	  This provides a character device for each MTD device present in
-	  the system, allowing the user to read and write directly to the
-	  memory chips, and also use ioctl() to obtain information about
-	  the device, or to erase parts of it.
-
-config MTD_BLOCK
-	tristate
-	depends on ETRAX_AXISFLASHMAP
-	default y
-	---help---
-	  Although most flash chips have an erase size too large to be useful
-	  as block devices, it is possible to use MTD devices which are based
-	  on RAM chips in this manner. This block device is a user of MTD
-	  devices performing that function.
-
-	  At the moment, it is also required for the Journalling Flash File
-	  System(s) to obtain a handle on the MTD device when it's mounted
-	  (although JFFS and JFFS2 don't actually use any of the functionality
-	  of the mtdblock device).
-
-	  Later, it may be extended to perform read/erase/modify/write cycles
-	  on flash chips to emulate a smaller block size. Needless to say,
-	  this is very unsafe, but could be useful for file systems which are
-	  almost never written to.
-
-	  You do not need this option for use with the DiskOnChip devices. For
-	  those, enable NFTL support (CONFIG_NFTL) instead.
-
-config MTD_PARTITIONS
-	tristate
-	depends on ETRAX_AXISFLASHMAP
-	default y
-	help
-	  If you have a device which needs to divide its flash chip(s) up
-	  into multiple 'partitions', each of which appears to the user as
-	  a separate MTD device, you require this option to be enabled. If
-	  unsure, say 'Y'.
-
-	  Note, however, that you don't need this option for the DiskOnChip
-	  devices. Partitioning on NFTL 'devices' is a different - that's the
-	  'normal' form of partitioning used on a block device.
-
-config MTD_CONCAT
-	tristate
-	depends on ETRAX_AXISFLASHMAP
-	default y
-
 config ETRAX_I2C
 	bool "I2C support"
 	depends on ETRAX_ARCH_V10
@@ -752,7 +638,7 @@ config ETRAX_I2C
 	  val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg);
 
 # this is true for most products since PB-I2C seems to be somewhat
-# flawed.. 
+# flawed..
 config ETRAX_I2C_USES_PB_NOT_PB_I2C
 	bool "I2C uses PB not PB-I2C"
 	depends on ETRAX_I2C
@@ -886,7 +772,7 @@ config ETRAX_RTC
 	bool "Real Time Clock support"
 	depends on ETRAX_ARCH_V10
 	help
-	  Enables drivers for the Real-Time Clock battery-backed chips on 
+	  Enables drivers for the Real-Time Clock battery-backed chips on
 	  some products. The kernel reads the time when booting, and
 	  the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a
 	  rtc_time struct (see <file:include/asm-cris/rtc.h>) on the /dev/rtc
@@ -903,13 +789,13 @@ config ETRAX_DS1302
 	bool "DS1302"
 	help
 	  Enables the driver for the DS1302 Real-Time Clock battery-backed
-	  chip on some products. 
+	  chip on some products.
 
 config ETRAX_PCF8563
 	bool "PCF8563"
 	help
 	  Enables the driver for the PCF8563 Real-Time Clock battery-backed
-	  chip on some products.  
+	  chip on some products.
 
 endchoice
 
@@ -954,10 +840,8 @@ config ETRAX_DS1302_TRICKLE_CHARGE
 	help
 	  This controls the initial value of the trickle charge register.
 	  0 = disabled (use this if you are unsure or have a non rechargable battery)
-	  Otherwise the following values can be OR:ed together to control the 
+	  Otherwise the following values can be OR:ed together to control the
 	  charge current:
 	  1 = 2kohm, 2 = 4kohm, 3 = 4kohm
 	  4 = 1 diode, 8 = 2 diodes
 	  Allowed values are (increasing current): 0, 11, 10, 9, 7, 6, 5
-
-

+ 4 - 1
arch/cris/arch-v10/drivers/axisflashmap.c

@@ -11,6 +11,9 @@
  * partition split defined below.
  *
  * $Log: axisflashmap.c,v $
+ * Revision 1.11  2004/11/15 10:27:14  starvik
+ * Corrected typo (Thanks to Milton Miller <miltonm@bga.com>).
+ *
  * Revision 1.10  2004/08/16 12:37:22  starvik
  * Merge of Linux 2.6.8
  *
@@ -161,7 +164,7 @@
 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==2
 #define flash_data __u16
 #elif CONFIG_ETRAX_FLASH_BUSWIDTH==4
-#define flash_data __u16
+#define flash_data __u32
 #endif
 
 /* From head.S */

+ 49 - 22
arch/cris/arch-v10/drivers/ds1302.c

@@ -7,6 +7,15 @@
 *! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init
 *!
 *! $Log: ds1302.c,v $
+*! Revision 1.18  2005/01/24 09:11:26  mikaelam
+*! Minor changes to get DS1302 RTC chip driver to work
+*!
+*! Revision 1.17  2005/01/05 06:11:22  starvik
+*! No need to do local_irq_disable after local_irq_save.
+*!
+*! Revision 1.16  2004/12/13 12:21:52  starvik
+*! Added I/O and DMA allocators from Linux 2.4
+*!
 *! Revision 1.14  2004/08/24 06:48:43  starvik
 *! Whitespace cleanup
 *!
@@ -124,9 +133,9 @@
 *!
 *! ---------------------------------------------------------------------------
 *!
-*! (C) Copyright 1999, 2000, 2001  Axis Communications AB, LUND, SWEDEN
+*! (C) Copyright 1999, 2000, 2001, 2002, 2003, 2004  Axis Communications AB, LUND, SWEDEN
 *!
-*! $Id: ds1302.c,v 1.14 2004/08/24 06:48:43 starvik Exp $
+*! $Id: ds1302.c,v 1.18 2005/01/24 09:11:26 mikaelam Exp $
 *!
 *!***************************************************************************/
 
@@ -145,6 +154,7 @@
 #include <asm/arch/svinto.h>
 #include <asm/io.h>
 #include <asm/rtc.h>
+#include <asm/arch/io_interface_mux.h>
 
 #define RTC_MAJOR_NR 121 /* local major, change later */
 
@@ -320,7 +330,6 @@ get_rtc_time(struct rtc_time *rtc_tm)
 	unsigned long flags;
 
 	local_irq_save(flags);
-	local_irq_disable();
 
 	rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
 	rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
@@ -358,7 +367,7 @@ static int
 rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	  unsigned long arg) 
 {
-        unsigned long flags;
+	unsigned long flags;
 
 	switch(cmd) {
 		case RTC_RD_TIME:	/* read the time/date from RTC	*/
@@ -382,7 +391,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 				return -EPERM;
 
 			if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time)))
-				return -EFAULT;    	
+				return -EFAULT;
 
 			yrs = rtc_tm.tm_year + 1900;
 			mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
@@ -419,7 +428,6 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 			BIN_TO_BCD(yrs);
 
 			local_irq_save(flags);
-			local_irq_disable();
 			CMOS_WRITE(yrs, RTC_YEAR);
 			CMOS_WRITE(mon, RTC_MONTH);
 			CMOS_WRITE(day, RTC_DAY_OF_MONTH);
@@ -438,7 +446,7 @@ rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 
 		case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */
 		{
-			int tcs_val;                        
+			int tcs_val;
 
 			if (!capable(CAP_SYS_TIME))
 				return -EPERM;
@@ -492,8 +500,8 @@ print_rtc_status(void)
 /* The various file operations we support. */
 
 static struct file_operations rtc_fops = {
-        .owner          = THIS_MODULE,
-        .ioctl          = rtc_ioctl,	
+	.owner =	THIS_MODULE,
+	.ioctl =	rtc_ioctl,
 }; 
 
 /* Probe for the chip by writing something to its RAM and try reading it back. */
@@ -532,7 +540,7 @@ ds1302_probe(void)
 		       "PB",
 #endif
 		       CONFIG_ETRAX_DS1302_RSTBIT);
-                print_rtc_status();
+		       print_rtc_status();
 		retval = 1;
 	} else {
 		stop();
@@ -548,7 +556,9 @@ ds1302_probe(void)
 int __init
 ds1302_init(void) 
 {
+#ifdef CONFIG_ETRAX_I2C
 	i2c_init();
+#endif
 
 	if (!ds1302_probe()) {
 #ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT
@@ -558,25 +568,42 @@ ds1302_init(void)
 		 *
 		 * Make sure that R_GEN_CONFIG is setup correct.
 		 */
-    		genconfig_shadow = ((genconfig_shadow &
-				     ~IO_MASK(R_GEN_CONFIG, ata)) | 
-				   (IO_STATE(R_GEN_CONFIG, ata, select)));    
-    		*R_GEN_CONFIG = genconfig_shadow;
+		/* Allocating the ATA interface will grab almost all
+		 * pins in I/O groups a, b, c and d.  A consequence of
+		 * allocating the ATA interface is that the fixed
+		 * interfaces shared RAM, parallel port 0, parallel
+		 * port 1, parallel port W, SCSI-8 port 0, SCSI-8 port
+		 * 1, SCSI-W, serial port 2, serial port 3,
+		 * synchronous serial port 3 and USB port 2 and almost
+		 * all GPIO pins on port g cannot be used.
+		 */
+		if (cris_request_io_interface(if_ata, "ds1302/ATA")) {
+			printk(KERN_WARNING "ds1302: Failed to get IO interface\n");
+			return -1;
+		}
+
 #elif CONFIG_ETRAX_DS1302_RSTBIT == 0
-		
-		/* Set the direction of this bit to out. */		
-    		genconfig_shadow = ((genconfig_shadow &
-				     ~IO_MASK(R_GEN_CONFIG, g0dir)) | 
-				   (IO_STATE(R_GEN_CONFIG, g0dir, out)));    
-    		*R_GEN_CONFIG = genconfig_shadow;
+		if (cris_io_interface_allocate_pins(if_gpio_grp_a,
+						    'g',
+						    CONFIG_ETRAX_DS1302_RSTBIT,
+						    CONFIG_ETRAX_DS1302_RSTBIT)) {
+			printk(KERN_WARNING "ds1302: Failed to get IO interface\n");
+			return -1;
+		}
+
+		/* Set the direction of this bit to out. */
+		genconfig_shadow = ((genconfig_shadow &
+ 				     ~IO_MASK(R_GEN_CONFIG, g0dir)) |
+ 				   (IO_STATE(R_GEN_CONFIG, g0dir, out)));
+		*R_GEN_CONFIG = genconfig_shadow;
 #endif
 		if (!ds1302_probe()) {
 			printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name);
-      			return -1;
+			return -1;
 		}
 #else
 		printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name);
-    		return -1;
+		return -1;
 #endif
   	}
 	/* Initialise trickle charger */

+ 14 - 15
arch/cris/arch-v10/drivers/eeprom.c

@@ -20,6 +20,12 @@
 *!                                  in the spin-lock.
 *!
 *!  $Log: eeprom.c,v $
+*!  Revision 1.12  2005/06/19 17:06:46  starvik
+*!  Merge of Linux 2.6.12.
+*!
+*!  Revision 1.11  2005/01/26 07:14:46  starvik
+*!  Applied diff from kernel janitors (Nish Aravamudan).
+*!
 *!  Revision 1.10  2003/09/11 07:29:48  starvik
 *!  Merge of Linux 2.6.0-test5
 *!
@@ -94,6 +100,7 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/wait.h>
 #include <asm/uaccess.h>
 #include "i2c.h"
 
@@ -526,15 +533,10 @@ static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t
     return -EFAULT;
   }
   
-  while(eeprom.busy)
-  {
-    interruptible_sleep_on(&eeprom.wait_q);
+  wait_event_interruptible(eeprom.wait_q, !eeprom.busy);
+  if (signal_pending(current))
+    return -EINTR;
 
-    /* bail out if we get interrupted */
-    if (signal_pending(current))
-      return -EINTR;
-    
-  }
   eeprom.busy++;
 
   page = (unsigned char) (p >> 8);
@@ -604,13 +606,10 @@ static ssize_t eeprom_write(struct file * file, const char * buf, size_t count,
     return -EFAULT;
   }
 
-  while(eeprom.busy)
-  {
-    interruptible_sleep_on(&eeprom.wait_q);
-    /* bail out if we get interrupted */
-    if (signal_pending(current))
-      return -EINTR;
-  }
+  wait_event_interruptible(eeprom.wait_q, !eeprom.busy);
+  /* bail out if we get interrupted */
+  if (signal_pending(current))
+    return -EINTR;
   eeprom.busy++;
   for(i = 0; (i < EEPROM_RETRIES) && (restart > 0); i++)
   {

+ 106 - 95
arch/cris/arch-v10/drivers/gpio.c

@@ -1,4 +1,4 @@
-/* $Id: gpio.c,v 1.12 2004/08/24 07:19:59 starvik Exp $
+/* $Id: gpio.c,v 1.17 2005/06/19 17:06:46 starvik Exp $
  *
  * Etrax general port I/O device
  *
@@ -9,6 +9,18 @@
  *             Johan Adolfsson  (read/set directions, write, port G)
  *
  * $Log: gpio.c,v $
+ * Revision 1.17  2005/06/19 17:06:46  starvik
+ * Merge of Linux 2.6.12.
+ *
+ * Revision 1.16  2005/03/07 13:02:29  starvik
+ * Protect driver global states with spinlock
+ *
+ * Revision 1.15  2005/01/05 06:08:55  starvik
+ * No need to do local_irq_disable after local_irq_save.
+ *
+ * Revision 1.14  2004/12/13 12:21:52  starvik
+ * Added I/O and DMA allocators from Linux 2.4
+ *
  * Revision 1.12  2004/08/24 07:19:59  starvik
  * Whitespace cleanup
  *
@@ -142,6 +154,7 @@
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/irq.h>
+#include <asm/arch/io_interface_mux.h>
 
 #define GPIO_MAJOR 120  /* experimental MAJOR number */
 
@@ -194,6 +207,8 @@ static struct gpio_private *alarmlist = 0;
 static int gpio_some_alarms = 0; /* Set if someone uses alarm */
 static unsigned long gpio_pa_irq_enabled_mask = 0;
 
+static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */
+
 /* Port A and B use 8 bit access, but Port G is 32 bit */
 #define NUM_PORTS (GPIO_MINOR_B+1)
 
@@ -241,6 +256,9 @@ static volatile unsigned char *dir_shadow[NUM_PORTS] = {
 	&port_pb_dir_shadow 
 };
 
+/* All bits in port g that can change dir. */
+static const unsigned long int changeable_dir_g_mask = 0x01FFFF01;
+
 /* Port G is 32 bit, handle it special, some bits are both inputs 
    and outputs at the same time, only some of the bits can change direction
    and some of them in groups of 8 bit. */
@@ -260,6 +278,7 @@ gpio_poll(struct file *file,
 	unsigned int mask = 0;
 	struct gpio_private *priv = (struct gpio_private *)file->private_data;
 	unsigned long data;
+	spin_lock(&gpio_lock);
 	poll_wait(file, &priv->alarm_wq, wait);
 	if (priv->minor == GPIO_MINOR_A) {
 		unsigned long flags;
@@ -270,10 +289,10 @@ gpio_poll(struct file *file,
 		 */
 		tmp = ~data & priv->highalarm & 0xFF;
 		tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
-		save_flags(flags); cli();
+		local_irq_save(flags);
 		gpio_pa_irq_enabled_mask |= tmp;
 		*R_IRQ_MASK1_SET = tmp;
-		restore_flags(flags);
+		local_irq_restore(flags);
 
 	} else if (priv->minor == GPIO_MINOR_B)
 		data = *R_PORT_PB_DATA;
@@ -286,8 +305,11 @@ gpio_poll(struct file *file,
 	    (~data & priv->lowalarm)) {
 		mask = POLLIN|POLLRDNORM;
 	}
+
+	spin_unlock(&gpio_lock);
 	
 	DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
+
 	return mask;
 }
 
@@ -296,6 +318,7 @@ int etrax_gpio_wake_up_check(void)
 	struct gpio_private *priv = alarmlist;
 	unsigned long data = 0;
         int ret = 0;
+	spin_lock(&gpio_lock);
 	while (priv) {
 		if (USE_PORTS(priv)) {
 			data = *priv->port;
@@ -310,6 +333,7 @@ int etrax_gpio_wake_up_check(void)
 		}
 		priv = priv->next;
 	}
+	spin_unlock(&gpio_lock);
         return ret;
 }
 
@@ -327,6 +351,7 @@ static irqreturn_t
 gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	unsigned long tmp;
+	spin_lock(&gpio_lock);
 	/* Find what PA interrupts are active */
 	tmp = (*R_IRQ_READ1);
 
@@ -337,6 +362,8 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 	*R_IRQ_MASK1_CLR = tmp;
 	gpio_pa_irq_enabled_mask &= ~tmp;
 
+	spin_unlock(&gpio_lock);
+
 	if (gpio_some_alarms) {
 		return IRQ_RETVAL(etrax_gpio_wake_up_check());
 	}
@@ -350,6 +377,9 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
 	struct gpio_private *priv = (struct gpio_private *)file->private_data;
 	unsigned char data, clk_mask, data_mask, write_msb;
 	unsigned long flags;
+
+	spin_lock(&gpio_lock);
+
 	ssize_t retval = count;
 	if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) {
 		return -EFAULT;
@@ -372,7 +402,7 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
 		data = *buf++;
 		if (priv->write_msb) {
 			for (i = 7; i >= 0;i--) {
-				local_irq_save(flags); local_irq_disable();
+				local_irq_save(flags);
 				*priv->port = *priv->shadow &= ~clk_mask;
 				if (data & 1<<i)
 					*priv->port = *priv->shadow |= data_mask;
@@ -384,7 +414,7 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
 			}
 		} else {
 			for (i = 0; i <= 7;i++) {
-				local_irq_save(flags); local_irq_disable();
+				local_irq_save(flags);
 				*priv->port = *priv->shadow &= ~clk_mask;
 				if (data & 1<<i)
 					*priv->port = *priv->shadow |= data_mask;
@@ -396,6 +426,7 @@ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
 			}
 		}
 	}
+	spin_unlock(&gpio_lock);
 	return retval;
 }
 
@@ -452,9 +483,14 @@ gpio_open(struct inode *inode, struct file *filp)
 static int
 gpio_release(struct inode *inode, struct file *filp)
 {
-	struct gpio_private *p = alarmlist;
-	struct gpio_private *todel = (struct gpio_private *)filp->private_data;
-	
+	struct gpio_private *p;
+	struct gpio_private *todel;
+
+	spin_lock(&gpio_lock);
+
+        p = alarmlist;
+        todel = (struct gpio_private *)filp->private_data;
+
 	/* unlink from alarmlist and free the private structure */
 
 	if (p == todel) {
@@ -476,7 +512,7 @@ gpio_release(struct inode *inode, struct file *filp)
 		p = p->next;
 	}
 	gpio_some_alarms = 0;
-		
+	spin_unlock(&gpio_lock);
 	return 0;
 }
 
@@ -491,14 +527,14 @@ unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
 	 */
 	unsigned long flags;
 	if (USE_PORTS(priv)) {
-		local_irq_save(flags); local_irq_disable();
+		local_irq_save(flags);
 		*priv->dir = *priv->dir_shadow &= 
 		~((unsigned char)arg & priv->changeable_dir);
 		local_irq_restore(flags);
 		return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
 	} else if (priv->minor == GPIO_MINOR_G) {
 		/* We must fiddle with R_GEN_CONFIG to change dir */
-		save_flags(flags); cli();
+		local_irq_save(flags);
 		if (((arg & dir_g_in_bits) != arg) && 
 		    (arg & changeable_dir_g)) {
 			arg &= changeable_dir_g;
@@ -533,7 +569,7 @@ unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
 			/* Must be a >120 ns delay before writing this again */
 				
 		}
-		restore_flags(flags);
+		local_irq_restore(flags);
 		return dir_g_in_bits;
 	}
 	return 0;
@@ -543,14 +579,14 @@ unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
 {
 	unsigned long flags;
 	if (USE_PORTS(priv)) {
-		local_irq_save(flags); local_irq_disable();
+		local_irq_save(flags);
 		*priv->dir = *priv->dir_shadow |= 
 		  ((unsigned char)arg & priv->changeable_dir);
 		local_irq_restore(flags);
 		return *priv->dir_shadow;
 	} else if (priv->minor == GPIO_MINOR_G) {
 		/* We must fiddle with R_GEN_CONFIG to change dir */			
-		save_flags(flags); cli();
+		local_irq_save(flags);
 		if (((arg & dir_g_out_bits) != arg) &&
 		    (arg & changeable_dir_g)) {
 			/* Set bits in genconfig to set to output */
@@ -583,7 +619,7 @@ unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
 			*R_GEN_CONFIG = genconfig_shadow;
 			/* Must be a >120 ns delay before writing this again */
 		}
-		restore_flags(flags);
+		local_irq_restore(flags);
 		return dir_g_out_bits & 0x7FFFFFFF;
 	}
 	return 0;
@@ -598,22 +634,26 @@ gpio_ioctl(struct inode *inode, struct file *file,
 {
 	unsigned long flags;
 	unsigned long val;
+        int ret = 0;
+
 	struct gpio_private *priv = (struct gpio_private *)file->private_data;
 	if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
 		return -EINVAL;
 	}
 
+	spin_lock(&gpio_lock);
+
 	switch (_IOC_NR(cmd)) {
 	case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
 		// read the port
 		if (USE_PORTS(priv)) {
-			return *priv->port;
+			ret =  *priv->port;
 		} else if (priv->minor == GPIO_MINOR_G) {
-			return (*R_PORT_G_DATA) & 0x7FFFFFFF;
+			ret =  (*R_PORT_G_DATA) & 0x7FFFFFFF;
 		}
 		break;
 	case IO_SETBITS:
-		local_irq_save(flags); local_irq_disable();
+		local_irq_save(flags);
 		// set changeable bits with a 1 in arg
 		if (USE_PORTS(priv)) {
 			*priv->port = *priv->shadow |= 
@@ -624,7 +664,7 @@ gpio_ioctl(struct inode *inode, struct file *file,
 		local_irq_restore(flags);
 		break;
 	case IO_CLRBITS:
-		local_irq_save(flags); local_irq_disable();
+		local_irq_save(flags);
 		// clear changeable bits with a 1 in arg
 		if (USE_PORTS(priv)) {
 			*priv->port = *priv->shadow &= 
@@ -666,33 +706,34 @@ gpio_ioctl(struct inode *inode, struct file *file,
 	case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
 		/* Read direction 0=input 1=output */
 		if (USE_PORTS(priv)) {
-			return *priv->dir_shadow;
+			ret = *priv->dir_shadow;
 		} else if (priv->minor == GPIO_MINOR_G) {
 			/* Note: Some bits are both in and out,
 			 * Those that are dual is set here as well.
 			 */
-			return (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
+			ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
 		}
+		break;
 	case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
 		/* Set direction 0=unchanged 1=input, 
 		 * return mask with 1=input 
 		 */
-		return setget_input(priv, arg) & 0x7FFFFFFF;
+		ret = setget_input(priv, arg) & 0x7FFFFFFF;
 		break;
 	case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
 		/* Set direction 0=unchanged 1=output, 
 		 * return mask with 1=output 
 		 */
-		return setget_output(priv, arg) & 0x7FFFFFFF;
-
+		ret =  setget_output(priv, arg) & 0x7FFFFFFF;
+		break;
 	case IO_SHUTDOWN:
 		SOFT_SHUTDOWN();
 		break;
 	case IO_GET_PWR_BT:
 #if defined (CONFIG_ETRAX_SOFT_SHUTDOWN)
-		return (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
+		ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
 #else
-		return 0;
+		ret = 0;
 #endif
 		break;
 	case IO_CFG_WRITE_MODE:
@@ -709,7 +750,7 @@ gpio_ioctl(struct inode *inode, struct file *file,
 		{
 			priv->clk_mask = 0;
 			priv->data_mask = 0;
-			return -EPERM;
+			ret = -EPERM;
 		}
 		break;
 	case IO_READ_INBITS: 
@@ -720,8 +761,7 @@ gpio_ioctl(struct inode *inode, struct file *file,
 			val = *R_PORT_G_DATA;
 		}
 		if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
-			return -EFAULT;
-		return 0;
+			ret = -EFAULT;
 		break;
 	case IO_READ_OUTBITS:
 		 /* *arg is result of reading the output shadow */
@@ -731,36 +771,43 @@ gpio_ioctl(struct inode *inode, struct file *file,
 			val = port_g_data_shadow;
 		}
 		if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
-			return -EFAULT;
+			ret = -EFAULT;
 		break;
 	case IO_SETGET_INPUT: 
 		/* bits set in *arg is set to input,
 		 * *arg updated with current input pins.
 		 */
 		if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
-			return -EFAULT;
+		{
+			ret = -EFAULT;
+			break;
+		}
 		val = setget_input(priv, val);
 		if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
-			return -EFAULT;
+			ret = -EFAULT;
 		break;
 	case IO_SETGET_OUTPUT:
 		/* bits set in *arg is set to output,
 		 * *arg updated with current output pins.
 		 */
 		if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
-			return -EFAULT;
+		{
+			ret = -EFAULT;
+			break;
+		}
 		val = setget_output(priv, val);
 		if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
-			return -EFAULT;
+			ret = -EFAULT;
 		break;
 	default:
 		if (priv->minor == GPIO_MINOR_LEDS)
-			return gpio_leds_ioctl(cmd, arg);
+			ret = gpio_leds_ioctl(cmd, arg);
 		else
-			return -EINVAL;
+			ret = -EINVAL;
 	} /* switch */
-	
-	return 0;
+
+	spin_unlock(&gpio_lock);
+	return ret;
 }
 
 static int
@@ -802,60 +849,20 @@ struct file_operations gpio_fops = {
 };
 
 
-static void __init gpio_init_port_g(void)
+void ioif_watcher(const unsigned int gpio_in_available,
+		  const unsigned int gpio_out_available,
+		  const unsigned char pa_available,
+		  const unsigned char pb_available)
 {
-#define GROUPA (0x0000FF3F)
-#define GROUPB (1<<6 | 1<<7)
-#define GROUPC (1<<30 | 1<<31)
-#define GROUPD (0x3FFF0000)
-#define GROUPD_LOW (0x00FF0000)
-	unsigned long used_in_bits = 0;
-	unsigned long used_out_bits = 0;
-	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0, select)){
-		used_in_bits  |= GROUPA | GROUPB | 0 | 0;
-		used_out_bits |= GROUPA | GROUPB | 0 | 0;
-	}
-	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ata, select)) {
-		used_in_bits  |= GROUPA | GROUPB | GROUPC | (GROUPD & ~(1<<25|1<<26));
-		used_out_bits |= GROUPA | GROUPB | GROUPC | GROUPD;
-	}
+	unsigned long int flags;
+	D(printk("gpio.c: ioif_watcher called\n"));
+	D(printk("gpio.c: G in: 0x%08x G out: 0x%08x PA: 0x%02x PB: 0x%02x\n",
+		 gpio_in_available, gpio_out_available, pa_available, pb_available));
 
-	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par0, select)) {
-		used_in_bits  |= (GROUPA & ~(1<<0)) | 0 | 0 | 0;
-		used_out_bits |= (GROUPA & ~(1<<0)) | 0 | 0 | 0;
-	}
-	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser2, select)) {
-		used_in_bits  |= 0 | GROUPB | 0 | 0;
-		used_out_bits |= 0 | GROUPB | 0 | 0;
-	}
-	/* mio same as shared RAM ? */
-	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio, select)) {
-		used_in_bits  |= (GROUPA & ~(1<<0)) | 0 |0 |GROUPD_LOW;
-		used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 |0 |GROUPD_LOW;
-	}
-	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi1, select)) {
-		used_in_bits  |= 0 | 0 | GROUPC | GROUPD;
-		used_out_bits |= 0 | 0 | GROUPC | GROUPD;
-	}
-	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, scsi0w, select)) {
-		used_in_bits  |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24);
-		used_out_bits |= GROUPA | GROUPB | 0 | (GROUPD_LOW | 1<<24 | 1<<25|1<<26);
-	}
+	spin_lock_irqsave(&gpio_lock, flags);
 
-	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, par1, select)) {
-		used_in_bits  |= 0 | 0 | 0 | (GROUPD & ~(1<<24));
-		used_out_bits |= 0 | 0 | 0 | (GROUPD & ~(1<<24));
-	}
-	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, ser3, select)) {
-		used_in_bits  |= 0 | 0 | GROUPC | 0;
-		used_out_bits |= 0 | 0 | GROUPC | 0;
-	}
-	/* mio same as shared RAM-W? */
-	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, mio_w, select)) {
-		used_in_bits  |= (GROUPA & ~(1<<0)) | 0 | 0 |GROUPD_LOW;
-		used_out_bits |= (GROUPA & ~(1<<0|1<<1|1<<2)) | 0 | 0 |GROUPD_LOW;
-	}
-	/* TODO: USB p2, parw, sync ser3? */
+	dir_g_in_bits = gpio_in_available;
+	dir_g_out_bits = gpio_out_available;
 
 	/* Initialise the dir_g_shadow etc. depending on genconfig */
 	/* 0=input 1=output */
@@ -868,10 +875,7 @@ static void __init gpio_init_port_g(void)
 	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out))
 		dir_g_shadow |= (1 << 24);
 
-	dir_g_in_bits = ~used_in_bits;
-	dir_g_out_bits = ~used_out_bits;
-
-	changeable_dir_g = 0x01FFFF01; /* all that can change dir */
+	changeable_dir_g = changeable_dir_g_mask;
 	changeable_dir_g &= dir_g_out_bits;
 	changeable_dir_g &= dir_g_in_bits;
 	/* Correct the bits that can change direction */ 
@@ -880,6 +884,7 @@ static void __init gpio_init_port_g(void)
 	dir_g_in_bits &= ~changeable_dir_g;
 	dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g);
 
+	spin_unlock_irqrestore(&gpio_lock, flags);
 
 	printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n",
 	       dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
@@ -896,6 +901,7 @@ gpio_init(void)
 #if defined (CONFIG_ETRAX_CSP0_LEDS)
 	int i;
 #endif
+        printk("gpio init\n");
 
 	/* do the formalities */
 
@@ -919,8 +925,13 @@ gpio_init(void)
 #endif
 
 #endif
-	gpio_init_port_g();
-	printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 Axis Communications AB\n");
+	/* The I/O interface allocation watcher will be called when
+	 * registering it. */
+	if (cris_io_interface_register_watcher(ioif_watcher)){
+		printk(KERN_WARNING "gpio_init: Failed to install IO if allocator watcher\n");
+	}
+
+	printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002, 2003, 2004 Axis Communications AB\n");
 	/* We call etrax_gpio_wake_up_check() from timer interrupt and
 	 * from cpu_idle() in kernel/process.c
 	 * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms

+ 55 - 7
arch/cris/arch-v10/drivers/i2c.c

@@ -12,6 +12,15 @@
 *!                                 don't use PB_I2C if DS1302 uses same bits,
 *!                                 use PB.
 *! $Log: i2c.c,v $
+*! Revision 1.13  2005/03/07 13:13:07  starvik
+*! Added spinlocks to protect states etc
+*!
+*! Revision 1.12  2005/01/05 06:11:22  starvik
+*! No need to do local_irq_disable after local_irq_save.
+*!
+*! Revision 1.11  2004/12/13 12:21:52  starvik
+*! Added I/O and DMA allocators from Linux 2.4
+*!
 *! Revision 1.9  2004/08/24 06:49:14  starvik
 *! Whitespace cleanup
 *!
@@ -75,7 +84,7 @@
 *! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
 *!
 *!***************************************************************************/
-/* $Id: i2c.c,v 1.9 2004/08/24 06:49:14 starvik Exp $ */
+/* $Id: i2c.c,v 1.13 2005/03/07 13:13:07 starvik Exp $ */
 
 /****************** INCLUDE FILES SECTION ***********************************/
 
@@ -95,6 +104,7 @@
 #include <asm/arch/svinto.h>
 #include <asm/io.h>
 #include <asm/delay.h>
+#include <asm/arch/io_interface_mux.h>
 
 #include "i2c.h"
 
@@ -184,6 +194,7 @@ static const char i2c_name[] = "i2c";
 
 #define i2c_delay(usecs) udelay(usecs)
 
+static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */
 
 /****************** FUNCTION DEFINITION SECTION *************************/
 
@@ -488,13 +499,14 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg,
 	int error, cntr = 3;
 	unsigned long flags;
 
+	spin_lock(&i2c_lock);
+
 	do {
 		error = 0;
 		/*
 		 * we don't like to be interrupted
 		 */
 		local_irq_save(flags);
-		local_irq_disable();
 
 		i2c_start();
 		/*
@@ -538,6 +550,8 @@ i2c_writereg(unsigned char theSlave, unsigned char theReg,
 
 	i2c_delay(CLOCK_LOW_TIME);
 
+	spin_unlock(&i2c_lock);
+
 	return -error;
 }
 
@@ -555,13 +569,14 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
 	int error, cntr = 3;
 	unsigned long flags;
 
+	spin_lock(&i2c_lock);
+
 	do {
 		error = 0;
 		/*
 		 * we don't like to be interrupted
 		 */
 		local_irq_save(flags);
-		local_irq_disable();
 		/*
 		 * generate start condition
 		 */
@@ -620,6 +635,8 @@ i2c_readreg(unsigned char theSlave, unsigned char theReg)
 		
 	} while(error && cntr--);
 
+	spin_unlock(&i2c_lock);
+
 	return b;
 }
 
@@ -686,15 +703,26 @@ static struct file_operations i2c_fops = {
 int __init
 i2c_init(void)
 {
+	static int res = 0;
+	static int first = 1;
+
+	if (!first) {
+		return res;
+	}
+
 	/* Setup and enable the Port B I2C interface */
 
 #ifndef CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C
+	if ((res = cris_request_io_interface(if_i2c, "I2C"))) {
+		printk(KERN_CRIT "i2c_init: Failed to get IO interface\n");
+		return res;
+	}
+
 	*R_PORT_PB_I2C = port_pb_i2c_shadow |= 
 		IO_STATE(R_PORT_PB_I2C, i2c_en,  on) |
 		IO_FIELD(R_PORT_PB_I2C, i2c_d,   1)  |
 		IO_FIELD(R_PORT_PB_I2C, i2c_clk, 1)  |
 		IO_STATE(R_PORT_PB_I2C, i2c_oe_, enable);
-#endif
 
 	port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir0);
 	port_pb_dir_shadow &= ~IO_MASK(R_PORT_PB_DIR, dir1);
@@ -702,8 +730,26 @@ i2c_init(void)
 	*R_PORT_PB_DIR = (port_pb_dir_shadow |=
 			  IO_STATE(R_PORT_PB_DIR, dir0, input)  |
 			  IO_STATE(R_PORT_PB_DIR, dir1, output));
+#else
+        if ((res = cris_io_interface_allocate_pins(if_i2c,
+						   'b',
+                                                   CONFIG_ETRAX_I2C_DATA_PORT,
+						   CONFIG_ETRAX_I2C_DATA_PORT))) {
+		printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C data port\n");
+		return res;
+	} else if ((res = cris_io_interface_allocate_pins(if_i2c,
+							  'b',
+							  CONFIG_ETRAX_I2C_CLK_PORT,
+							  CONFIG_ETRAX_I2C_CLK_PORT))) {
+		cris_io_interface_free_pins(if_i2c,
+					    'b',
+					    CONFIG_ETRAX_I2C_DATA_PORT,
+					    CONFIG_ETRAX_I2C_DATA_PORT);
+		printk(KERN_WARNING "i2c_init: Failed to get IO pin for I2C clk port\n");
+	}
+#endif
 
-	return 0;
+	return res;
 }
 
 static int __init
@@ -711,14 +757,16 @@ i2c_register(void)
 {
 	int res;
 
-	i2c_init();
+	res = i2c_init();
+	if (res < 0)
+		return res;
   	res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops);
 	if(res < 0) {
 		printk(KERN_ERR "i2c: couldn't get a major number.\n");
 		return res;
 	}
 
-	printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
+	printk(KERN_INFO "I2C driver v2.2, (c) 1999-2004 Axis Communications AB\n");
 	
 	return 0;
 }

+ 16 - 4
arch/cris/arch-v10/drivers/pcf8563.c

@@ -15,7 +15,7 @@
  *
  * Author: Tobias Anderberg <tobiasa@axis.com>.
  *
- * $Id: pcf8563.c,v 1.8 2004/08/24 06:42:51 starvik Exp $
+ * $Id: pcf8563.c,v 1.11 2005/03/07 13:13:07 starvik Exp $
  */
 
 #include <linux/config.h>
@@ -40,7 +40,7 @@
 #define PCF8563_MAJOR 121		/* Local major number. */
 #define DEVICE_NAME "rtc"		/* Name which is registered in /proc/devices. */
 #define PCF8563_NAME "PCF8563"
-#define DRIVER_VERSION "$Revision: 1.8 $"
+#define DRIVER_VERSION "$Revision: 1.11 $"
 
 /* I2C bus slave registers. */
 #define RTC_I2C_READ		0xa3
@@ -49,6 +49,8 @@
 /* Two simple wrapper macros, saves a few keystrokes. */
 #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
 #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y)
+
+static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */
 	
 static const unsigned char days_in_month[] =
 	{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
@@ -125,9 +127,12 @@ get_rtc_time(struct rtc_time *tm)
 int __init
 pcf8563_init(void)
 {
-	unsigned char ret;
+	int ret;
 
-	i2c_init();
+	if ((ret = i2c_init())) {
+		printk(KERN_CRIT "pcf8563_init: failed to init i2c\n");
+		return ret;
+	}
 
 	/*
 	 * First of all we need to reset the chip. This is done by
@@ -200,12 +205,15 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned
 			{
 				struct rtc_time tm;
 
+				spin_lock(&rtc_lock);
 				get_rtc_time(&tm);
 
 				if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) {
+					spin_unlock(&rtc_lock);
 					return -EFAULT;
 				}
 
+				spin_unlock(&rtc_lock);
 				return 0;
 			}
 			break;
@@ -250,6 +258,8 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned
 				BIN_TO_BCD(tm.tm_min);
 				BIN_TO_BCD(tm.tm_sec);
 				tm.tm_mon |= century;
+
+				spin_lock(&rtc_lock);
 				
 				rtc_write(RTC_YEAR, tm.tm_year);
 				rtc_write(RTC_MONTH, tm.tm_mon);
@@ -258,6 +268,8 @@ pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned
 				rtc_write(RTC_MINUTES, tm.tm_min);
 				rtc_write(RTC_SECONDS, tm.tm_sec);
 
+				spin_unlock(&rtc_lock);
+
 				return 0;
 #endif /* !CONFIG_ETRAX_RTC_READONLY */
 			}

+ 3 - 2
arch/cris/arch-v10/kernel/Makefile

@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.5 2004/06/02 08:24:38 starvik Exp $
+# $Id: Makefile,v 1.6 2004/12/13 12:21:51 starvik Exp $
 #
 # Makefile for the linux kernel.
 #
@@ -7,7 +7,8 @@ extra-y	:= head.o
 
 
 obj-y   := entry.o traps.o shadows.o debugport.o irq.o \
-	   process.o setup.o signal.o traps.o time.o ptrace.o
+	   process.o setup.o signal.o traps.o time.o ptrace.o \
+	   dma.o io_interface_mux.o
 
 obj-$(CONFIG_ETRAX_KGDB) += kgdb.o
 obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o

+ 196 - 77
arch/cris/arch-v10/kernel/debugport.c

@@ -12,6 +12,31 @@
  *    init_etrax_debug()
  *
  * $Log: debugport.c,v $
+ * Revision 1.27  2005/06/10 10:34:14  starvik
+ * Real console support
+ *
+ * Revision 1.26  2005/06/07 07:06:07  starvik
+ * Added LF->CR translation to make ETRAX customers happy.
+ *
+ * Revision 1.25  2005/03/08 08:56:47  mikaelam
+ * Do only set index as port->index if port is defined, otherwise use the index from the command line
+ *
+ * Revision 1.24  2005/01/19 10:26:33  mikaelam
+ * Return the cris serial driver in console device driver callback function
+ *
+ * Revision 1.23  2005/01/14 10:12:17  starvik
+ * KGDB on separate port.
+ * Console fixes from 2.4.
+ *
+ * Revision 1.22  2005/01/11 16:06:13  starvik
+ * typo
+ *
+ * Revision 1.21  2005/01/11 13:49:14  starvik
+ * Added raw_printk to be used where we don't trust the console.
+ *
+ * Revision 1.20  2004/12/27 11:18:32  starvik
+ * Merge of Linux 2.6.10 (not functional yet).
+ *
  * Revision 1.19  2004/10/21 07:26:16  starvik
  * Made it possible to specify console settings on kernel command line.
  *
@@ -114,7 +139,11 @@ struct dbg_port ports[]=
     R_SERIAL0_BAUD,
     R_SERIAL0_TR_CTRL,
     R_SERIAL0_REC_CTRL,
-    IO_STATE(R_IRQ_MASK1_SET, ser0_data, set)
+    IO_STATE(R_IRQ_MASK1_SET, ser0_data, set),
+    0,
+    115200,
+    'N',
+    8
   },
   {
     1,
@@ -124,7 +153,11 @@ struct dbg_port ports[]=
     R_SERIAL1_BAUD,
     R_SERIAL1_TR_CTRL,
     R_SERIAL1_REC_CTRL,
-    IO_STATE(R_IRQ_MASK1_SET, ser1_data, set)
+    IO_STATE(R_IRQ_MASK1_SET, ser1_data, set),
+    0,
+    115200,
+    'N',
+    8
   },
   {
     2,
@@ -134,7 +167,11 @@ struct dbg_port ports[]=
     R_SERIAL2_BAUD,
     R_SERIAL2_TR_CTRL,
     R_SERIAL2_REC_CTRL,
-    IO_STATE(R_IRQ_MASK1_SET, ser2_data, set)
+    IO_STATE(R_IRQ_MASK1_SET, ser2_data, set),
+    0,
+    115200,
+    'N',
+    8
   },
   {
     3,
@@ -144,11 +181,15 @@ struct dbg_port ports[]=
     R_SERIAL3_BAUD,
     R_SERIAL3_TR_CTRL,
     R_SERIAL3_REC_CTRL,
-    IO_STATE(R_IRQ_MASK1_SET, ser3_data, set)
+    IO_STATE(R_IRQ_MASK1_SET, ser3_data, set),
+    0,
+    115200,
+    'N',
+    8
   }
 };
 
-static struct tty_driver *serial_driver;
+extern struct tty_driver *serial_driver;
 
 struct dbg_port* port =
 #if defined(CONFIG_ETRAX_DEBUG_PORT0)
@@ -162,37 +203,44 @@ struct dbg_port* port =
 #else
   NULL;
 #endif
-/* Used by serial.c to register a debug_write_function so that the normal
- * serial driver is used for kernel debug output
- */
-typedef int (*debugport_write_function)(int i, const char *buf, unsigned int len);
 
-debugport_write_function debug_write_function = NULL;
+static struct dbg_port* kgdb_port =
+#if defined(CONFIG_ETRAX_KGDB_PORT0)
+  &ports[0];
+#elif defined(CONFIG_ETRAX_KGDB_PORT1)
+  &ports[1];
+#elif defined(CONFIG_ETRAX_KGDB_PORT2)
+  &ports[2];
+#elif defined(CONFIG_ETRAX_KGDB_PORT3)
+  &ports[3];
+#else
+  NULL;
+#endif
 
 static void
-start_port(void)
+start_port(struct dbg_port* p)
 {
 	unsigned long rec_ctrl = 0;
 	unsigned long tr_ctrl = 0;
 
-	if (!port)
+	if (!p)
 		return;
 
-	if (port->started)
+	if (p->started)
 		return;
-	port->started = 1;
+	p->started = 1;
 
-	if (port->index == 0)
+	if (p->index == 0)
 	{
 		genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma6);
 		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
 	}
-	else if (port->index == 1)
+	else if (p->index == 1)
 	{
 		genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma8);
 		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
 	}
-	else if (port->index == 2)
+	else if (p->index == 2)
 	{
 		genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, dma2);
 		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
@@ -211,69 +259,69 @@ start_port(void)
 
 	*R_GEN_CONFIG = genconfig_shadow;
 
-	*port->xoff =
+	*p->xoff =
 		IO_STATE(R_SERIAL0_XOFF, tx_stop, enable) |
 		IO_STATE(R_SERIAL0_XOFF, auto_xoff, disable) |
 		IO_FIELD(R_SERIAL0_XOFF, xoff_char, 0);
 
-	switch (port->baudrate)
+	switch (p->baudrate)
 	{
 	case 0:
 	case 115200:
-		*port->baud =
+		*p->baud =
 		  IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) |
 		  IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz);
 		break;
 	case 1200:
-		*port->baud =
+		*p->baud =
 		  IO_STATE(R_SERIAL0_BAUD, tr_baud, c1200Hz) |
 		  IO_STATE(R_SERIAL0_BAUD, rec_baud, c1200Hz);
 		break;
 	case 2400:
-		*port->baud =
+		*p->baud =
 		  IO_STATE(R_SERIAL0_BAUD, tr_baud, c2400Hz) |
 		  IO_STATE(R_SERIAL0_BAUD, rec_baud, c2400Hz);
 		break;
 	case 4800:
-		*port->baud =
+		*p->baud =
 		  IO_STATE(R_SERIAL0_BAUD, tr_baud, c4800Hz) |
 		  IO_STATE(R_SERIAL0_BAUD, rec_baud, c4800Hz);
 		break;
 	case 9600:
-		*port->baud =
+		*p->baud =
 		  IO_STATE(R_SERIAL0_BAUD, tr_baud, c9600Hz) |
 		  IO_STATE(R_SERIAL0_BAUD, rec_baud, c9600Hz);
 		  break;
 	case 19200:
-		*port->baud =
+		*p->baud =
 		  IO_STATE(R_SERIAL0_BAUD, tr_baud, c19k2Hz) |
 		  IO_STATE(R_SERIAL0_BAUD, rec_baud, c19k2Hz);
 		 break;
 	case 38400:
-		*port->baud =
+		*p->baud =
 		  IO_STATE(R_SERIAL0_BAUD, tr_baud, c38k4Hz) |
 		  IO_STATE(R_SERIAL0_BAUD, rec_baud, c38k4Hz);
 		break;
 	case 57600:
-		*port->baud =
+		*p->baud =
 		  IO_STATE(R_SERIAL0_BAUD, tr_baud, c57k6Hz) |
 		  IO_STATE(R_SERIAL0_BAUD, rec_baud, c57k6Hz);
 		break;
 	default:
-		*port->baud =
+		*p->baud =
 		  IO_STATE(R_SERIAL0_BAUD, tr_baud, c115k2Hz) |
 		  IO_STATE(R_SERIAL0_BAUD, rec_baud, c115k2Hz);
 		  break;
         }
 
-	if (port->parity == 'E') {
+	if (p->parity == 'E') {
 		rec_ctrl =
 		  IO_STATE(R_SERIAL0_REC_CTRL, rec_par, even) |
 		  IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
 		tr_ctrl =
 		  IO_STATE(R_SERIAL0_TR_CTRL, tr_par, even) |
 		  IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, enable);
-	} else if (port->parity == 'O') {
+	} else if (p->parity == 'O') {
 		rec_ctrl =
 		  IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd) |
 		  IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
@@ -288,8 +336,7 @@ start_port(void)
 		  IO_STATE(R_SERIAL0_TR_CTRL, tr_par, even) |
 		  IO_STATE(R_SERIAL0_TR_CTRL, tr_par_en, disable);
 	}
-
-	if (port->bits == 7)
+	if (p->bits == 7)
 	{
 		rec_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_bitnr, rec_7bit);
 		tr_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_7bit);
@@ -300,7 +347,7 @@ start_port(void)
 		tr_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_bitnr, tr_8bit);
 	}
 
-	*port->rec_ctrl =
+	*p->rec_ctrl =
 		IO_STATE(R_SERIAL0_REC_CTRL, dma_err, stop) |
 		IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable) |
 		IO_STATE(R_SERIAL0_REC_CTRL, rts_, active) |
@@ -308,7 +355,7 @@ start_port(void)
 		IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, normal) |
 		rec_ctrl;
 
-	*port->tr_ctrl =
+	*p->tr_ctrl =
 		IO_FIELD(R_SERIAL0_TR_CTRL, txd, 0) |
 		IO_STATE(R_SERIAL0_TR_CTRL, tr_enable, enable) |
 		IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, disabled) |
@@ -323,8 +370,18 @@ console_write_direct(struct console *co, const char *buf, unsigned int len)
 	int i;
 	unsigned long flags;
 	local_irq_save(flags);
+
+        if (!port)
+		return;
+
 	/* Send data */
 	for (i = 0; i < len; i++) {
+		/* LF -> CRLF */
+		if (buf[i] == '\n') {
+			while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
+			;
+			*port->write = '\r';
+		}
 		/* Wait until transmitter is ready and send.*/
 		while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
 			;
@@ -333,6 +390,25 @@ console_write_direct(struct console *co, const char *buf, unsigned int len)
 	local_irq_restore(flags);
 }
 
+int raw_printk(const char *fmt, ...)
+{
+	static char buf[1024];
+	int printed_len;
+	static int first = 1;
+	if (first) {
+		/* Force reinitialization of the port to get manual mode. */
+		port->started = 0;
+		start_port(port);
+		first = 0;
+	}
+	va_list args;
+	va_start(args, fmt);
+	printed_len = vsnprintf(buf, sizeof(buf), fmt, args);
+	va_end(args);
+	console_write_direct(NULL, buf, strlen(buf));
+	return printed_len;
+}
+
 static void
 console_write(struct console *co, const char *buf, unsigned int len)
 {
@@ -345,18 +421,7 @@ console_write(struct console *co, const char *buf, unsigned int len)
 	return;
 #endif
 
-	start_port();
-
-#ifdef CONFIG_ETRAX_KGDB
-	/* kgdb needs to output debug info using the gdb protocol */
-	putDebugString(buf, len);
-	return;
-#endif
-
-	if (debug_write_function)
-		debug_write_function(co->index, buf, len);
-	else
-		console_write_direct(co, buf, len);
+        console_write_direct(co, buf, len);
 }
 
 /* legacy function */
@@ -374,8 +439,11 @@ getDebugChar(void)
 {
 	unsigned long readval;
 
+	if (!kgdb_port)
+		return 0;
+
 	do {
-		readval = *port->read;
+		readval = *kgdb_port->read;
 	} while (!(readval & IO_MASK(R_SERIAL0_READ, data_avail)));
 
 	return (readval & IO_MASK(R_SERIAL0_READ, data_in));
@@ -386,9 +454,12 @@ getDebugChar(void)
 void
 putDebugChar(int val)
 {
-	while (!(*port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
+	if (!kgdb_port)
+		return;
+
+	while (!(*kgdb_port->read & IO_MASK(R_SERIAL0_READ, tr_ready)))
 		;
-	*port->write = val;
+	*kgdb_port->write = val;
 }
 
 /* Enable irq for receiving chars on the debug port, used by kgdb */
@@ -396,19 +467,16 @@ putDebugChar(int val)
 void
 enableDebugIRQ(void)
 {
-	*R_IRQ_MASK1_SET = port->irq;
+	if (!kgdb_port)
+		return;
+
+	*R_IRQ_MASK1_SET = kgdb_port->irq;
 	/* use R_VECT_MASK directly, since we really bypass Linux normal
 	 * IRQ handling in kgdb anyway, we don't need to use enable_irq
 	 */
 	*R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set);
 
-	*port->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
-}
-
-static struct tty_driver*
-etrax_console_device(struct console* co, int *index)
-{
-	return serial_driver;
+	*kgdb_port->rec_ctrl = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable);
 }
 
 static int __init
@@ -428,11 +496,69 @@ console_setup(struct console *co, char *options)
 		if (*s) port->parity = *s++;
 		if (*s) port->bits   = *s++ - '0';
 		port->started = 0;
-		start_port();
+		start_port(0);
 	}
 	return 0;
 }
 
+/* This is a dummy serial device that throws away anything written to it.
+ * This is used when no debug output is wanted.
+ */
+static struct tty_driver dummy_driver;
+
+static int dummy_open(struct tty_struct *tty, struct file * filp)
+{
+	return 0;
+}
+
+static void dummy_close(struct tty_struct *tty, struct file * filp)
+{
+}
+
+static int dummy_write(struct tty_struct * tty,
+                       const unsigned char *buf, int count)
+{
+	return count;
+}
+
+static int
+dummy_write_room(struct tty_struct *tty)
+{
+	return 8192;
+}
+
+void __init
+init_dummy_console(void)
+{
+	memset(&dummy_driver, 0, sizeof(struct tty_driver));
+	dummy_driver.driver_name = "serial";
+	dummy_driver.name = "ttyS";
+	dummy_driver.major = TTY_MAJOR;
+	dummy_driver.minor_start = 68;
+	dummy_driver.num = 1;       /* etrax100 has 4 serial ports */
+	dummy_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	dummy_driver.subtype = SERIAL_TYPE_NORMAL;
+	dummy_driver.init_termios = tty_std_termios;
+	dummy_driver.init_termios.c_cflag =
+		B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
+	dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+
+	dummy_driver.open = dummy_open;
+	dummy_driver.close = dummy_close;
+	dummy_driver.write = dummy_write;
+	dummy_driver.write_room = dummy_write_room;
+	if (tty_register_driver(&dummy_driver))
+		panic("Couldn't register dummy serial driver\n");
+}
+
+static struct tty_driver*
+etrax_console_device(struct console* co, int *index)
+{
+	if (port)
+		*index = port->index;
+        return port ? serial_driver : &dummy_driver;
+}
+
 static struct console sercons = {
 	name : "ttyS",
 	write: console_write,
@@ -504,28 +630,21 @@ init_etrax_debug(void)
 	static int first = 1;
 
 	if (!first) {
-		if (!port) {
-			register_console(&sercons0);
-			register_console(&sercons1);
-			register_console(&sercons2);
-			register_console(&sercons3);
-			unregister_console(&sercons);
-		}
+		unregister_console(&sercons);
+		register_console(&sercons0);
+		register_console(&sercons1);
+		register_console(&sercons2);
+		register_console(&sercons3);
+                init_dummy_console();
 		return 0;
 	}
-	first = 0;
-	if (port)
-		register_console(&sercons);
-	return 0;
-}
 
-int __init
-init_console(void)
-{
-	serial_driver = alloc_tty_driver(1);
-	if (!serial_driver)
-		return -ENOMEM;
+	first = 0;
+	register_console(&sercons);
+	start_port(port);
+#ifdef CONFIG_ETRAX_KGDB
+	start_port(kgdb_port);
+#endif
 	return 0;
 }
-
 __initcall(init_etrax_debug);

+ 287 - 0
arch/cris/arch-v10/kernel/dma.c

@@ -0,0 +1,287 @@
+/* Wrapper for DMA channel allocator that updates DMA client muxing.
+ * Copyright 2004, Axis Communications AB
+ * $Id: dma.c,v 1.1 2004/12/13 12:21:51 starvik Exp $
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+
+#include <asm/dma.h>
+#include <asm/arch/svinto.h>
+
+/* Macro to access ETRAX 100 registers */
+#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
+					  IO_STATE_(reg##_, field##_, _##val)
+
+
+static char used_dma_channels[MAX_DMA_CHANNELS];
+static const char * used_dma_channels_users[MAX_DMA_CHANNELS];
+
+int cris_request_dma(unsigned int dmanr, const char * device_id,
+		     unsigned options, enum dma_owner owner)
+{
+	unsigned long flags;
+	unsigned long int gens;
+	int fail = -EINVAL;
+
+	if ((dmanr < 0) || (dmanr >= MAX_DMA_CHANNELS)) {
+		printk(KERN_CRIT "cris_request_dma: invalid DMA channel %u\n", dmanr);
+		return -EINVAL;
+	}
+
+	local_irq_save(flags);
+	if (used_dma_channels[dmanr]) {
+		local_irq_restore(flags);
+		if (options & DMA_VERBOSE_ON_ERROR) {
+			printk(KERN_CRIT "Failed to request DMA %i for %s, already allocated by %s\n", dmanr, device_id, used_dma_channels_users[dmanr]);
+		}
+		if (options & DMA_PANIC_ON_ERROR) {
+			panic("request_dma error!");
+		}
+		return -EBUSY;
+	}
+
+	gens = genconfig_shadow;
+
+	switch(owner)
+	{
+	case dma_eth:
+		if ((dmanr != NETWORK_TX_DMA_NBR) &&
+		    (dmanr != NETWORK_RX_DMA_NBR)) {
+			printk(KERN_CRIT "Invalid DMA channel for eth\n");
+			goto bail;
+		}
+		break;
+	case dma_ser0:
+		if (dmanr == SER0_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma6, serial0);
+		} else if (dmanr == SER0_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma7, serial0);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for ser0\n");
+			goto bail;
+		}
+		break;
+	case dma_ser1:
+		if (dmanr == SER1_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma8, serial1);
+		} else if (dmanr == SER1_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma9, serial1);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for ser1\n");
+			goto bail;
+		}
+		break;
+	case dma_ser2:
+		if (dmanr == SER2_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma2, serial2);
+		} else if (dmanr == SER2_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma3, serial2);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for ser2\n");
+			goto bail;
+		}
+		break;
+	case dma_ser3:
+		if (dmanr == SER3_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma4, serial3);
+		} else if (dmanr == SER3_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma5, serial3);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for ser3\n");
+			goto bail;
+		}
+		break;
+	case dma_ata:
+		if (dmanr == ATA_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma2, ata);
+		} else if (dmanr == ATA_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma3, ata);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for ata\n");
+			goto bail;
+		}
+		break;
+	case dma_ext0:
+		if (dmanr == EXTDMA0_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma4, extdma0);
+		} else if (dmanr == EXTDMA0_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma5, extdma0);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for ext0\n");
+			goto bail;
+		}
+		break;
+	case dma_ext1:
+		if (dmanr == EXTDMA1_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma6, extdma1);
+		} else if (dmanr == EXTDMA1_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma7, extdma1);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for ext1\n");
+			goto bail;
+		}
+		break;
+	case dma_int6:
+		if (dmanr == MEM2MEM_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma7, intdma6);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for int6\n");
+			goto bail;
+		}
+		break;
+	case dma_int7:
+		if (dmanr == MEM2MEM_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma6, intdma7);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for int7\n");
+			goto bail;
+		}
+		break;
+	case dma_usb:
+		if (dmanr == USB_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma8, usb);
+		} else if (dmanr == USB_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma9, usb);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for usb\n");
+			goto bail;
+		}
+		break;
+	case dma_scsi0:
+		if (dmanr == SCSI0_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma2, scsi0);
+		} else if (dmanr == SCSI0_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma3, scsi0);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for scsi0\n");
+			goto bail;
+		}
+		break;
+	case dma_scsi1:
+		if (dmanr == SCSI1_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma4, scsi1);
+		} else if (dmanr == SCSI1_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma5, scsi1);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for scsi1\n");
+			goto bail;
+		}
+		break;
+	case dma_par0:
+		if (dmanr == PAR0_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma2, par0);
+		} else if (dmanr == PAR0_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma3, par0);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for par0\n");
+			goto bail;
+		}
+		break;
+	case dma_par1:
+		if (dmanr == PAR1_TX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma4, par1);
+		} else if (dmanr == PAR1_RX_DMA_NBR) {
+			SETS(gens, R_GEN_CONFIG, dma5, par1);
+		} else {
+			printk(KERN_CRIT "Invalid DMA channel for par1\n");
+			goto bail;
+		}
+		break;
+	default:
+		printk(KERN_CRIT "Invalid DMA owner.\n");
+		goto bail;
+	}
+
+	used_dma_channels[dmanr] = 1;
+	used_dma_channels_users[dmanr] = device_id;
+
+	{
+		volatile int i;
+		genconfig_shadow = gens;
+		*R_GEN_CONFIG = genconfig_shadow;
+		/* Wait 12 cycles before doing any DMA command */
+		for(i = 6; i > 0; i--)
+			nop();
+	}
+	fail = 0;
+ bail:
+	local_irq_restore(flags);
+	return fail;
+}
+
+void cris_free_dma(unsigned int dmanr, const char * device_id)
+{
+	unsigned long flags;
+	if ((dmanr < 0) || (dmanr >= MAX_DMA_CHANNELS)) {
+		printk(KERN_CRIT "cris_free_dma: invalid DMA channel %u\n", dmanr);
+		return;
+	}
+
+	local_irq_save(flags);
+	if (!used_dma_channels[dmanr]) {
+		printk(KERN_CRIT "cris_free_dma: DMA channel %u not allocated\n", dmanr);
+	} else if (device_id != used_dma_channels_users[dmanr]) {
+		printk(KERN_CRIT "cris_free_dma: DMA channel %u not allocated by device\n", dmanr);
+	} else {
+		switch(dmanr)
+		{
+		case 0:
+			*R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, reset);
+			while (IO_EXTRACT(R_DMA_CH0_CMD, cmd, *R_DMA_CH0_CMD) ==
+			       IO_STATE_VALUE(R_DMA_CH0_CMD, cmd, reset));
+			break;
+		case 1:
+			*R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, reset);
+			while (IO_EXTRACT(R_DMA_CH1_CMD, cmd, *R_DMA_CH1_CMD) ==
+			       IO_STATE_VALUE(R_DMA_CH1_CMD, cmd, reset));
+			break;
+		case 2:
+			*R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, reset);
+			while (IO_EXTRACT(R_DMA_CH2_CMD, cmd, *R_DMA_CH2_CMD) ==
+			       IO_STATE_VALUE(R_DMA_CH2_CMD, cmd, reset));
+			break;
+		case 3:
+			*R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, reset);
+			while (IO_EXTRACT(R_DMA_CH3_CMD, cmd, *R_DMA_CH3_CMD) ==
+			       IO_STATE_VALUE(R_DMA_CH3_CMD, cmd, reset));
+			break;
+		case 4:
+			*R_DMA_CH4_CMD = IO_STATE(R_DMA_CH4_CMD, cmd, reset);
+			while (IO_EXTRACT(R_DMA_CH4_CMD, cmd, *R_DMA_CH4_CMD) ==
+			       IO_STATE_VALUE(R_DMA_CH4_CMD, cmd, reset));
+			break;
+		case 5:
+			*R_DMA_CH5_CMD = IO_STATE(R_DMA_CH5_CMD, cmd, reset);
+			while (IO_EXTRACT(R_DMA_CH5_CMD, cmd, *R_DMA_CH5_CMD) ==
+			       IO_STATE_VALUE(R_DMA_CH5_CMD, cmd, reset));
+			break;
+		case 6:
+			*R_DMA_CH6_CMD = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+			while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *R_DMA_CH6_CMD) ==
+			       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+			break;
+		case 7:
+			*R_DMA_CH7_CMD = IO_STATE(R_DMA_CH7_CMD, cmd, reset);
+			while (IO_EXTRACT(R_DMA_CH7_CMD, cmd, *R_DMA_CH7_CMD) ==
+			       IO_STATE_VALUE(R_DMA_CH7_CMD, cmd, reset));
+			break;
+		case 8:
+			*R_DMA_CH8_CMD = IO_STATE(R_DMA_CH8_CMD, cmd, reset);
+			while (IO_EXTRACT(R_DMA_CH8_CMD, cmd, *R_DMA_CH8_CMD) ==
+			       IO_STATE_VALUE(R_DMA_CH8_CMD, cmd, reset));
+			break;
+		case 9:
+			*R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, reset);
+			while (IO_EXTRACT(R_DMA_CH9_CMD, cmd, *R_DMA_CH9_CMD) ==
+			       IO_STATE_VALUE(R_DMA_CH9_CMD, cmd, reset));
+			break;
+		}
+		used_dma_channels[dmanr] = 0;
+	}
+	local_irq_restore(flags);
+}
+
+EXPORT_SYMBOL(cris_request_dma);
+EXPORT_SYMBOL(cris_free_dma);

+ 36 - 18
arch/cris/arch-v10/kernel/entry.S

@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.23 2004/10/19 13:07:37 starvik Exp $
+/* $Id: entry.S,v 1.28 2005/06/20 05:06:30 starvik Exp $
  *
  *  linux/arch/cris/entry.S
  *
@@ -7,6 +7,22 @@
  *  Authors:	Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: entry.S,v $
+ *  Revision 1.28  2005/06/20 05:06:30  starvik
+ *  Remove unnecessary diff to kernel.org tree
+ *
+ *  Revision 1.27  2005/03/04 08:16:16  starvik
+ *  Merge of Linux 2.6.11.
+ *
+ *  Revision 1.26  2005/01/11 13:49:47  starvik
+ *  Added NMI handler.
+ *
+ *  Revision 1.25  2004/12/27 11:18:32  starvik
+ *  Merge of Linux 2.6.10 (not functional yet).
+ *
+ *  Revision 1.24  2004/12/22 10:41:23  starvik
+ *  Updates to make v10 compile with the latest SMP aware generic code (even
+ *  though v10 will never have SMP).
+ *
  *  Revision 1.23  2004/10/19 13:07:37  starvik
  *  Merge of Linux 2.6.9
  *
@@ -279,6 +295,7 @@
 #ifdef CONFIG_PREEMPT	
 	; Check if preemptive kernel scheduling should be done
 _resume_kernel:
+	di
 	; Load current task struct
 	movs.w	-8192, $r0	;  THREAD_SIZE = 8192
 	and.d	$sp, $r0
@@ -291,12 +308,7 @@ _need_resched:
 	bpl	_Rexit
 	nop
 	; Ok, lets's do some preemptive kernel scheduling
-	move.d	PREEMPT_ACTIVE, $r10
-	move.d	$r10, [$r0+TI_preempt_count] ; Mark as active
-	ei
-	jsr	schedule
-	clear.d	[$r0+TI_preempt_count] ;  Mark as inactive
-	di
+	jsr	preempt_schedule_irq
 	; Load new task struct
 	movs.w	-8192, $r0	;  THREAD_SIZE = 8192
 	and.d	$sp, $r0
@@ -590,15 +602,15 @@ mmu_bus_fault:
 	move.d	$r0, [$sp+16]
 1:	btstq	12, $r1		   ; Refill?
 	bpl	2f
-	lsrq	PMD_SHIFT, $r1     ; Get PMD index into PGD (bit 24-31)
-	move.d  [current_pgd], $r0 ; PGD for the current process
+	lsrq	24, $r1     ; Get PGD index (bit 24-31)
+	move.d  [per_cpu__current_pgd], $r0 ; PGD for the current process
 	move.d	[$r0+$r1.d], $r0   ; Get PMD
 	beq	2f
 	nop
 	and.w	PAGE_MASK, $r0	   ; Remove PMD flags
 	move.d  [R_MMU_CAUSE], $r1
 	lsrq	PAGE_SHIFT, $r1
-	and.d	0x7ff, $r1         ; Get PTE index into PMD (bit 13-24)
+	and.d	0x7ff, $r1         ; Get PTE index into PGD (bit 13-23)
 	move.d	[$r0+$r1.d], $r1   ; Get PTE
 	beq	2f
 	nop
@@ -656,11 +668,6 @@ hwbreakpoint:
 	nop
 	
 IRQ1_interrupt:
-
-#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
-;; If we receive a watchdog interrupt while it is not expected, then set
-;; up a canonical frame and dump register contents before dying.
-
 	;; this prologue MUST match the one in irq.h and the struct in ptregs.h!!!
 	move	$brp,[$sp=$sp-16]; instruction pointer and room for a fake SBFS frame
 	push	$srp
@@ -672,9 +679,16 @@ IRQ1_interrupt:
 	push	$r10		; push orig_r10
 	clear.d [$sp=$sp-4]	; frametype == 0, normal frame
 
-;; We don't check that we actually were bit by the watchdog as opposed to
-;; an external NMI, since there is currently no handler for external NMI.
-
+	move.d	[R_IRQ_MASK0_RD], $r1 ; External NMI or watchdog?
+	and.d   0x80000000, $r1
+	beq	wdog
+	move.d  $sp, $r10
+	jsr	handle_nmi
+	setf m			; Enable NMI again
+	retb			; Return from NMI
+	nop
+wdog:
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
 ;; Check if we're waiting for reset to happen, as signalled by
 ;; hard_reset_now setting cause_of_death to a magic value.  If so, just
 ;; get stuck until reset happens.
@@ -1118,6 +1132,10 @@ sys_call_table:
 	.long sys_mq_getsetattr
 	.long sys_ni_syscall		/* reserved for kexec */
 	.long sys_waitid
+	.long sys_ni_syscall		/* 285 */ /* available */
+	.long sys_add_key
+	.long sys_request_key
+	.long sys_keyctl
 
         /*
          * NOTE!! This doesn't have to be exact - we just have

+ 29 - 26
arch/cris/arch-v10/kernel/fasttimer.c

@@ -1,10 +1,20 @@
-/* $Id: fasttimer.c,v 1.6 2004/05/14 10:18:39 starvik Exp $
+/* $Id: fasttimer.c,v 1.9 2005/03/04 08:16:16 starvik Exp $
  * linux/arch/cris/kernel/fasttimer.c
  *
  * Fast timers for ETRAX100/ETRAX100LX
  * This may be useful in other OS than Linux so use 2 space indentation...
  *
  * $Log: fasttimer.c,v $
+ * Revision 1.9  2005/03/04 08:16:16  starvik
+ * Merge of Linux 2.6.11.
+ *
+ * Revision 1.8  2005/01/05 06:09:29  starvik
+ * cli()/sti() will be obsolete in 2.6.11.
+ *
+ * Revision 1.7  2005/01/03 13:35:46  starvik
+ * Removed obsolete stuff.
+ * Mark fast timer IRQ as not shared.
+ *
  * Revision 1.6  2004/05/14 10:18:39  starvik
  * Export fast_timer_list
  *
@@ -148,8 +158,7 @@ static int debug_log_cnt_wrapped = 0;
 #define DEBUG_LOG(string, value) \
 { \
   unsigned long log_flags; \
-  save_flags(log_flags); \
-  cli(); \
+  local_irq_save(log_flags); \
   debug_log_string[debug_log_cnt] = (string); \
   debug_log_value[debug_log_cnt] = (unsigned long)(value); \
   if (++debug_log_cnt >= DEBUG_LOG_MAX) \
@@ -157,7 +166,7 @@ static int debug_log_cnt_wrapped = 0;
     debug_log_cnt = debug_log_cnt % DEBUG_LOG_MAX; \
     debug_log_cnt_wrapped = 1; \
   } \
-  restore_flags(log_flags); \
+  local_irq_restore(log_flags); \
 }
 #else
 #define DEBUG_LOG(string, value)
@@ -320,8 +329,7 @@ void start_one_shot_timer(struct fast_timer *t,
 
   D1(printk("sft %s %d us\n", name, delay_us));
 
-  save_flags(flags);
-  cli();
+  local_irq_save(flags);
 
   do_gettimeofday_fast(&t->tv_set);
   tmp = fast_timer_list;
@@ -395,7 +403,7 @@ void start_one_shot_timer(struct fast_timer *t,
 
   D2(printk("start_one_shot_timer: %d us done\n", delay_us));
 
-  restore_flags(flags);
+  local_irq_restore(flags);
 } /* start_one_shot_timer */
 
 static inline int fast_timer_pending (const struct fast_timer * t)
@@ -425,11 +433,10 @@ int del_fast_timer(struct fast_timer * t)
   unsigned long flags;
   int ret;
   
-  save_flags(flags);
-  cli();
+  local_irq_save(flags);
   ret = detach_fast_timer(t);
   t->next = t->prev = NULL;
-  restore_flags(flags);
+  local_irq_restore(flags);
   return ret;
 } /* del_fast_timer */
 
@@ -444,8 +451,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
   struct fast_timer *t;
   unsigned long flags;
 
-  save_flags(flags);
-  cli();
+  local_irq_save(flags);
 
   /* Clear timer1 irq */
   *R_IRQ_MASK0_CLR = IO_STATE(R_IRQ_MASK0_CLR, timer1, clr);
@@ -462,7 +468,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
   fast_timer_running = 0;
   fast_timer_ints++;
 
-  restore_flags(flags);
+  local_irq_restore(flags);
 
   t = fast_timer_list;
   while (t)
@@ -482,8 +488,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
       fast_timers_expired++;
 
       /* Remove this timer before call, since it may reuse the timer */
-      save_flags(flags);
-      cli();
+      local_irq_save(flags);
       if (t->prev)
       {
         t->prev->next = t->next;
@@ -498,7 +503,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
       }
       t->prev = NULL;
       t->next = NULL;
-      restore_flags(flags);
+      local_irq_restore(flags);
 
       if (t->function != NULL)
       {
@@ -515,8 +520,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
       D1(printk(".\n"));
     }
 
-    save_flags(flags);
-    cli();
+    local_irq_save(flags);
     if ((t = fast_timer_list) != NULL)
     {
       /* Start next timer.. */
@@ -535,7 +539,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
 #endif
           start_timer1(us);
         }
-        restore_flags(flags);
+        local_irq_restore(flags);
         break;
       }
       else
@@ -546,7 +550,7 @@ timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
         D1(printk("e! %d\n", us));
       }
     }
-    restore_flags(flags);
+    local_irq_restore(flags);
   }
 
   if (!t)
@@ -748,13 +752,12 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len
 #endif
 
     used += sprintf(bigbuf + used, "Active timers:\n");
-    save_flags(flags);
-    cli();
+    local_irq_save(flags);
     t = fast_timer_list;
     while (t != NULL && (used+100 < BIG_BUF_SIZE))
     {
       nextt = t->next;
-      restore_flags(flags);
+      local_irq_restore(flags);
       used += sprintf(bigbuf + used, "%-14s s: %6lu.%06lu e: %6lu.%06lu "
                       "d: %6li us data: 0x%08lX"
 /*                      " func: 0x%08lX" */
@@ -768,14 +771,14 @@ static int proc_fasttimer_read(char *buf, char **start, off_t offset, int len
                       t->data
 /*                      , t->function */
                       );
-      cli();
+      local_irq_disable();
       if (t->next != nextt)
       {
         printk(KERN_WARNING "timer removed!\n");
       }
       t = nextt;
     }
-    restore_flags(flags);
+    local_irq_restore(flags);
   }
 
   if (used - offset < len)
@@ -963,7 +966,7 @@ void fast_timer_init(void)
    if ((fasttimer_proc_entry = create_proc_entry( "fasttimer", 0, 0 )))
      fasttimer_proc_entry->read_proc = proc_fasttimer_read;
 #endif /* PROC_FS */
-    if(request_irq(TIMER1_IRQ_NBR, timer1_handler, SA_SHIRQ,
+    if(request_irq(TIMER1_IRQ_NBR, timer1_handler, 0,
                    "fast timer int", NULL))
     {
       printk("err: timer1 irq\n");

+ 55 - 71
arch/cris/arch-v10/kernel/head.S

@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.7 2004/05/14 07:58:01 starvik Exp $
+/* $Id: head.S,v 1.10 2005/06/20 05:12:54 starvik Exp $
  * 
  * Head of the kernel - alter with care
  *
@@ -7,6 +7,16 @@
  * Authors:	Bjorn Wesen (bjornw@axis.com)
  * 
  * $Log: head.S,v $
+ * Revision 1.10  2005/06/20 05:12:54  starvik
+ * Remove unnecessary diff to kernel.org tree
+ *
+ * Revision 1.9  2004/12/13 12:21:51  starvik
+ * Added I/O and DMA allocators from Linux 2.4
+ *
+ * Revision 1.8  2004/11/22 11:41:14  starvik
+ * Kernel command line may be supplied to kernel. Not used by Axis but may
+ * be used by customers.
+ *
  * Revision 1.7  2004/05/14 07:58:01  starvik
  * Merge of changes from 2.4
  *
@@ -181,6 +191,7 @@
 
 #define CRAMFS_MAGIC 0x28cd3d45
 #define RAM_INIT_MAGIC 0x56902387
+#define COMMAND_LINE_MAGIC 0x87109563
 
 #define START_ETHERNET_CLOCK IO_STATE(R_NETWORK_GEN_CONFIG, enable, on) |\
                              IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk)
@@ -490,6 +501,23 @@ _no_romfs_in_flash:
 	
 _start_it:
 
+	;; Check if kernel command line is supplied
+	cmp.d	COMMAND_LINE_MAGIC, $r10
+	bne	no_command_line
+	nop
+
+	move.d	256, $r13
+	move.d  cris_command_line, $r10
+	or.d	0x80000000, $r11 ; Make it virtual
+1:
+	move.b  [$r11+], $r12
+	move.b  $r12, [$r10+]
+	subq	1, $r13
+	bne	1b
+	nop
+
+no_command_line:
+
 	;; the kernel stack is overlayed with the task structure for each
 	;; task. thus the initial kernel stack is in the same page as the
 	;; init_task (but starts in the top of the page, size 8192)
@@ -567,76 +595,32 @@ _start_it:
 	;; Etrax product HW genconfig setup
 
 	moveq	0,$r0
-#if (!defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT0)) \
-	&& !defined(CONFIG_DMA_MEMCPY)
-	; DMA channels 6 and 7 to ser0, kgdb doesnt want DMA
-	or.d	  IO_STATE (R_GEN_CONFIG, dma7, serial0)	\
-		| IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0
-#endif
-#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT1)
-	; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA
-	or.d	  IO_STATE (R_GEN_CONFIG, dma9, serial1)	\
-		| IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0
-#endif	
-#ifdef CONFIG_DMA_MEMCPY
-	; 6/7 memory-memory DMA
-	or.d	  IO_STATE (R_GEN_CONFIG, dma7, intdma6)	\
-		| IO_STATE (R_GEN_CONFIG, dma6, intdma7),$r0
-#endif
-#ifdef CONFIG_ETRAX_SERIAL_PORT2
-	; Enable serial port 2
-	or.w	IO_STATE (R_GEN_CONFIG, ser2, select),$r0
-#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT2)
-	; DMA channels 2 and 3 to ser2, kgdb doesnt want DMA
-	or.d	  IO_STATE (R_GEN_CONFIG, dma3, serial2)	\
-		| IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0
-#endif
-#endif
-#if defined(CONFIG_ETRAX_SERIAL_PORT3) || defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
-	; Enable serial port 3
-	or.w	IO_STATE (R_GEN_CONFIG, ser3, select),$r0
-#if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT3)
-	; DMA channels 4 and 5 to ser3, kgdb doesnt want DMA
-	or.d	  IO_STATE (R_GEN_CONFIG, dma5, serial3)	\
-		| IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0
-#endif
-#endif
-#if defined(CONFIG_ETRAX_PARALLEL_PORT0) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE)
-	; parport 0 enabled using DMA 2/3
-	or.w	IO_STATE (R_GEN_CONFIG, par0, select),$r0
-#endif
-#if defined(CONFIG_ETRAX_PARALLEL_PORT1) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE)
-	; parport 1 enabled using DMA 4/5
-	or.w	IO_STATE (R_GEN_CONFIG, par1, select),$r0
-#endif
-#ifdef CONFIG_ETRAX_IDE
-	; DMA channels 2 and 3 to ATA, ATA enabled
-	or.d	  IO_STATE (R_GEN_CONFIG, dma3, ata)	\
-		| IO_STATE (R_GEN_CONFIG, dma2, ata)	\
-		| IO_STATE (R_GEN_CONFIG, ata, select),$r0
-#endif
-	
-#ifdef CONFIG_ETRAX_USB_HOST_PORT1
-	; Set the USB port 1 enable bit
-	or.d	IO_STATE (R_GEN_CONFIG, usb1, select),$r0
-#endif
-#ifdef CONFIG_ETRAX_USB_HOST_PORT2
-	; Set the USB port 2 enable bit
-	or.d	IO_STATE (R_GEN_CONFIG, usb2, select),$r0
-#endif
-#ifdef CONFIG_ETRAX_USB_HOST
-	; Connect DMA channels 8 and 9 to USB
-	and.d	(~(IO_MASK (R_GEN_CONFIG, dma9)		\
-		   | IO_MASK (R_GEN_CONFIG, dma8)))	\
-		| IO_STATE (R_GEN_CONFIG, dma9, usb)	\
-		| IO_STATE (R_GEN_CONFIG, dma8, usb),$r0
-#endif
-	
-#ifdef CONFIG_JULIETTE
-	; DMA channels 4 and 5 to EXTDMA0, for Juliette
-	or.d	  IO_STATE (R_GEN_CONFIG, dma5, extdma0)	\
-		| IO_STATE (R_GEN_CONFIG, dma4, extdma0),$r0
-#endif
+
+	;; Init interfaces (disable them).
+	or.d	  IO_STATE (R_GEN_CONFIG, scsi0, disable) \
+		| IO_STATE (R_GEN_CONFIG, ata, disable) \
+		| IO_STATE (R_GEN_CONFIG, par0, disable) \
+		| IO_STATE (R_GEN_CONFIG, ser2, disable) \
+		| IO_STATE (R_GEN_CONFIG, mio, disable) \
+		| IO_STATE (R_GEN_CONFIG, scsi1, disable) \
+		| IO_STATE (R_GEN_CONFIG, scsi0w, disable) \
+		| IO_STATE (R_GEN_CONFIG, par1, disable) \
+		| IO_STATE (R_GEN_CONFIG, ser3, disable) \
+		| IO_STATE (R_GEN_CONFIG, mio_w, disable) \
+		| IO_STATE (R_GEN_CONFIG, usb1, disable) \
+		| IO_STATE (R_GEN_CONFIG, usb2, disable) \
+		| IO_STATE (R_GEN_CONFIG, par_w, disable),$r0
+
+	;; Init DMA channel muxing (set to unused clients).
+	or.d	  IO_STATE (R_GEN_CONFIG, dma2, ata)	\
+		| IO_STATE (R_GEN_CONFIG, dma3, ata) \
+		| IO_STATE (R_GEN_CONFIG, dma4, scsi1) \
+		| IO_STATE (R_GEN_CONFIG, dma5, scsi1) \
+		| IO_STATE (R_GEN_CONFIG, dma6, unused) \
+		| IO_STATE (R_GEN_CONFIG, dma7, unused) \
+		| IO_STATE (R_GEN_CONFIG, dma8, usb) \
+		| IO_STATE (R_GEN_CONFIG, dma9, usb),$r0
+
 
 #if defined(CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT)
         or.d      IO_STATE (R_GEN_CONFIG, g0dir, out),$r0

+ 879 - 0
arch/cris/arch-v10/kernel/io_interface_mux.c

@@ -0,0 +1,879 @@
+/* IO interface mux allocator for ETRAX100LX.
+ * Copyright 2004, Axis Communications AB
+ * $Id: io_interface_mux.c,v 1.2 2004/12/21 12:08:38 starvik Exp $
+ */
+
+
+/* C.f. ETRAX100LX Designer's Reference 20.9 */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/arch/svinto.h>
+#include <asm/io.h>
+#include <asm/arch/io_interface_mux.h>
+
+
+#define DBG(s)
+
+/* Macro to access ETRAX 100 registers */
+#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
+					  IO_STATE_(reg##_, field##_, _##val)
+
+enum io_if_group {
+	group_a = (1<<0),
+	group_b = (1<<1),
+	group_c = (1<<2),
+	group_d = (1<<3),
+	group_e = (1<<4),
+	group_f = (1<<5)
+};
+
+struct watcher
+{
+	void (*notify)(const unsigned int gpio_in_available,
+		       const unsigned int gpio_out_available,
+		       const unsigned char pa_available,
+		       const unsigned char pb_available);
+	struct watcher *next;
+};
+
+
+struct if_group
+{
+	enum io_if_group        group;
+	unsigned char           used;
+	enum cris_io_interface  owner;
+};
+
+
+struct interface
+{
+	enum cris_io_interface   ioif;
+	unsigned char            groups;
+	unsigned char            used;
+	char                    *owner;
+	unsigned int             gpio_g_in;
+	unsigned int             gpio_g_out;
+	unsigned char            gpio_b;
+};
+
+static struct if_group if_groups[6] = {
+	{
+		.group = group_a,
+		.used = 0,
+	},
+	{
+		.group = group_b,
+		.used = 0,
+	},
+	{
+		.group = group_c,
+		.used = 0,
+	},
+	{
+		.group = group_d,
+		.used = 0,
+	},
+	{
+		.group = group_e,
+		.used = 0,
+	},
+	{
+		.group = group_f,
+		.used = 0,
+	}
+};
+
+/* The order in the array must match the order of enum
+ * cris_io_interface in io_interface_mux.h */
+static struct interface interfaces[] = {
+	/* Begin Non-multiplexed interfaces */
+	{
+		.ioif = if_eth,
+		.groups = 0,
+		.gpio_g_in = 0,
+		.gpio_g_out = 0,
+		.gpio_b = 0
+	},
+	{
+		.ioif = if_serial_0,
+		.groups = 0,
+		.gpio_g_in = 0,
+		.gpio_g_out = 0,
+		.gpio_b = 0
+	},
+	/* End Non-multiplexed interfaces */
+	{
+		.ioif = if_serial_1,
+		.groups = group_e,
+		.gpio_g_in =  0x00000000,
+		.gpio_g_out = 0x00000000,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_serial_2,
+		.groups = group_b,
+		.gpio_g_in =  0x000000c0,
+		.gpio_g_out = 0x000000c0,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_serial_3,
+		.groups = group_c,
+		.gpio_g_in =  0xc0000000,
+		.gpio_g_out = 0xc0000000,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_sync_serial_1,
+		.groups = group_e | group_f, /* if_sync_serial_1 and if_sync_serial_3
+					       can be used simultaneously */
+		.gpio_g_in =  0x00000000,
+		.gpio_g_out = 0x00000000,
+		.gpio_b = 0x10
+	},
+	{
+		.ioif = if_sync_serial_3,
+		.groups = group_c | group_f,
+		.gpio_g_in =  0xc0000000,
+		.gpio_g_out = 0xc0000000,
+		.gpio_b = 0x80
+	},
+	{
+		.ioif = if_shared_ram,
+		.groups = group_a,
+		.gpio_g_in =  0x0000ff3e,
+		.gpio_g_out = 0x0000ff38,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_shared_ram_w,
+		.groups = group_a | group_d,
+		.gpio_g_in =  0x00ffff3e,
+		.gpio_g_out = 0x00ffff38,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_par_0,
+		.groups = group_a,
+		.gpio_g_in =  0x0000ff3e,
+		.gpio_g_out = 0x0000ff3e,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_par_1,
+		.groups = group_d,
+		.gpio_g_in =  0x3eff0000,
+		.gpio_g_out = 0x3eff0000,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_par_w,
+		.groups = group_a | group_d,
+		.gpio_g_in =  0x00ffff3e,
+		.gpio_g_out = 0x00ffff3e,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_scsi8_0,
+		.groups = group_a | group_b | group_f, /* if_scsi8_0 and if_scsi8_1
+							  can be used simultaneously */
+		.gpio_g_in =  0x0000ffff,
+		.gpio_g_out = 0x0000ffff,
+		.gpio_b = 0x10
+	},
+	{
+		.ioif = if_scsi8_1,
+		.groups = group_c | group_d | group_f, /* if_scsi8_0 and if_scsi8_1
+							  can be used simultaneously */
+		.gpio_g_in =  0xffff0000,
+		.gpio_g_out = 0xffff0000,
+		.gpio_b = 0x80
+	},
+	{
+		.ioif = if_scsi_w,
+		.groups = group_a | group_b | group_d | group_f,
+		.gpio_g_in =  0x01ffffff,
+		.gpio_g_out = 0x07ffffff,
+		.gpio_b = 0x80
+	},
+	{
+		.ioif = if_ata,
+		.groups = group_a | group_b | group_c | group_d,
+		.gpio_g_in =  0xf9ffffff,
+		.gpio_g_out = 0xffffffff,
+		.gpio_b = 0x80
+	},
+	{
+		.ioif = if_csp,
+		.groups = group_f, /* if_csp and if_i2c can be used simultaneously */
+		.gpio_g_in =  0x00000000,
+		.gpio_g_out = 0x00000000,
+		.gpio_b = 0xfc
+	},
+	{
+		.ioif = if_i2c,
+		.groups = group_f, /* if_csp and if_i2c can be used simultaneously */
+		.gpio_g_in =  0x00000000,
+		.gpio_g_out = 0x00000000,
+		.gpio_b = 0x03
+	},
+	{
+		.ioif = if_usb_1,
+		.groups = group_e | group_f,
+		.gpio_g_in =  0x00000000,
+		.gpio_g_out = 0x00000000,
+		.gpio_b = 0x2c
+	},
+	{
+		.ioif = if_usb_2,
+		.groups = group_d,
+		.gpio_g_in =  0x0e000000,
+		.gpio_g_out = 0x3c000000,
+		.gpio_b = 0x00
+	},
+	/* GPIO pins */
+	{
+		.ioif = if_gpio_grp_a,
+		.groups = group_a,
+		.gpio_g_in =  0x0000ff3f,
+		.gpio_g_out = 0x0000ff3f,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_gpio_grp_b,
+		.groups = group_b,
+		.gpio_g_in =  0x000000c0,
+		.gpio_g_out = 0x000000c0,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_gpio_grp_c,
+		.groups = group_c,
+		.gpio_g_in =  0xc0000000,
+		.gpio_g_out = 0xc0000000,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_gpio_grp_d,
+		.groups = group_d,
+		.gpio_g_in =  0x3fff0000,
+		.gpio_g_out = 0x3fff0000,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_gpio_grp_e,
+		.groups = group_e,
+		.gpio_g_in =  0x00000000,
+		.gpio_g_out = 0x00000000,
+		.gpio_b = 0x00
+	},
+	{
+		.ioif = if_gpio_grp_f,
+		.groups = group_f,
+		.gpio_g_in =  0x00000000,
+		.gpio_g_out = 0x00000000,
+		.gpio_b = 0xff
+	}
+	/* Array end */
+};
+
+static struct watcher *watchers = NULL;
+
+static unsigned int gpio_in_pins =  0xffffffff;
+static unsigned int gpio_out_pins = 0xffffffff;
+static unsigned char gpio_pb_pins = 0xff;
+static unsigned char gpio_pa_pins = 0xff;
+
+static enum cris_io_interface gpio_pa_owners[8];
+static enum cris_io_interface gpio_pb_owners[8];
+static enum cris_io_interface gpio_pg_owners[32];
+
+static int cris_io_interface_init(void);
+
+static unsigned char clear_group_from_set(const unsigned char groups, struct if_group *group)
+{
+	return (groups & ~group->group);
+}
+
+
+static struct if_group *get_group(const unsigned char groups)
+{
+	int i;
+	for (i = 0; i < sizeof(if_groups)/sizeof(struct if_group); i++) {
+		if (groups & if_groups[i].group) {
+			return &if_groups[i];
+		}
+	}
+	return NULL;
+}
+
+
+static void notify_watchers(void)
+{
+	struct watcher *w = watchers;
+
+	DBG(printk("io_interface_mux: notifying watchers\n"));
+
+	while (NULL != w) {
+		w->notify((const unsigned int)gpio_in_pins,
+			  (const unsigned int)gpio_out_pins,
+			  (const unsigned char)gpio_pa_pins,
+			  (const unsigned char)gpio_pb_pins);
+		w = w->next;
+	}
+}
+
+
+int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id)
+{
+	int set_gen_config = 0;
+	int set_gen_config_ii = 0;
+	unsigned long int gens;
+	unsigned long int gens_ii;
+	struct if_group *grp;
+	unsigned char group_set;
+	unsigned long flags;
+
+	(void)cris_io_interface_init();
+
+	DBG(printk("cris_request_io_interface(%d, \"%s\")\n", ioif, device_id));
+
+	if ((ioif >= if_max_interfaces) || (ioif < 0)) {
+		printk(KERN_CRIT "cris_request_io_interface: Bad interface %u submitted for %s\n",
+		       ioif,
+		       device_id);
+		return -EINVAL;
+	}
+
+	local_irq_save(flags);
+
+	if (interfaces[ioif].used) {
+		local_irq_restore(flags);
+		printk(KERN_CRIT "cris_io_interface: Cannot allocate interface for %s, in use by %s\n",
+		       device_id,
+		       interfaces[ioif].owner);
+		return -EBUSY;
+	}
+
+	/* Check that all required groups are free before allocating, */
+	group_set = interfaces[ioif].groups;
+	while (NULL != (grp = get_group(group_set))) {
+		if (grp->used) {
+			if (grp->group == group_f) {
+				if ((if_sync_serial_1 ==  ioif) ||
+				    (if_sync_serial_3 ==  ioif)) {
+					if ((grp->owner != if_sync_serial_1) &&
+					    (grp->owner != if_sync_serial_3)) {
+						local_irq_restore(flags);
+						return -EBUSY;
+					}
+				} else if ((if_scsi8_0 == ioif) ||
+					   (if_scsi8_1 == ioif)) {
+					if ((grp->owner != if_scsi8_0) &&
+					    (grp->owner != if_scsi8_1)) {
+						local_irq_restore(flags);
+						return -EBUSY;
+					}
+				}
+			} else {
+				local_irq_restore(flags);
+				return -EBUSY;
+			}
+		}
+		group_set = clear_group_from_set(group_set, grp);
+	}
+
+	/* Are the required GPIO pins available too? */
+	if (((interfaces[ioif].gpio_g_in & gpio_in_pins) != interfaces[ioif].gpio_g_in) ||
+	    ((interfaces[ioif].gpio_g_out & gpio_out_pins) != interfaces[ioif].gpio_g_out) ||
+	    ((interfaces[ioif].gpio_b & gpio_pb_pins) != interfaces[ioif].gpio_b)) {
+		printk(KERN_CRIT "cris_request_io_interface: Could not get required pins for interface %u\n",
+		       ioif);
+		return -EBUSY;
+	}
+
+	/* All needed I/O pins and pin groups are free, allocate. */
+	group_set = interfaces[ioif].groups;
+	while (NULL != (grp = get_group(group_set))) {
+		grp->used = 1;
+		grp->owner = ioif;
+		group_set = clear_group_from_set(group_set, grp);
+	}
+
+	gens = genconfig_shadow;
+	gens_ii = gen_config_ii_shadow;
+
+	set_gen_config = 1;
+	switch (ioif)
+	{
+	/* Begin Non-multiplexed interfaces */
+	case if_eth:
+		/* fall through */
+	case if_serial_0:
+		set_gen_config = 0;
+		break;
+	/* End Non-multiplexed interfaces */
+	case if_serial_1:
+		set_gen_config_ii = 1;
+		SETS(gens_ii, R_GEN_CONFIG_II, sermode1, async);
+		break;
+	case if_serial_2:
+		SETS(gens, R_GEN_CONFIG, ser2, select);
+		break;
+	case if_serial_3:
+		SETS(gens, R_GEN_CONFIG, ser3, select);
+		set_gen_config_ii = 1;
+		SETS(gens_ii, R_GEN_CONFIG_II, sermode3, async);
+		break;
+	case if_sync_serial_1:
+		set_gen_config_ii = 1;
+		SETS(gens_ii, R_GEN_CONFIG_II, sermode1, sync);
+		break;
+	case if_sync_serial_3:
+		SETS(gens, R_GEN_CONFIG, ser3, select);
+		set_gen_config_ii = 1;
+		SETS(gens_ii, R_GEN_CONFIG_II, sermode3, sync);
+		break;
+	case if_shared_ram:
+		SETS(gens, R_GEN_CONFIG, mio, select);
+		break;
+	case if_shared_ram_w:
+		SETS(gens, R_GEN_CONFIG, mio_w, select);
+		break;
+	case if_par_0:
+		SETS(gens, R_GEN_CONFIG, par0, select);
+		break;
+	case if_par_1:
+		SETS(gens, R_GEN_CONFIG, par1, select);
+		break;
+	case if_par_w:
+		SETS(gens, R_GEN_CONFIG, par0, select);
+		SETS(gens, R_GEN_CONFIG, par_w, select);
+		break;
+	case if_scsi8_0:
+		SETS(gens, R_GEN_CONFIG, scsi0, select);
+		break;
+	case if_scsi8_1:
+		SETS(gens, R_GEN_CONFIG, scsi1, select);
+		break;
+	case if_scsi_w:
+		SETS(gens, R_GEN_CONFIG, scsi0, select);
+		SETS(gens, R_GEN_CONFIG, scsi0w, select);
+		break;
+	case if_ata:
+		SETS(gens, R_GEN_CONFIG, ata, select);
+		break;
+	case if_csp:
+		/* fall through */
+	case if_i2c:
+		set_gen_config = 0;
+		break;
+	case if_usb_1:
+		SETS(gens, R_GEN_CONFIG, usb1, select);
+		break;
+	case if_usb_2:
+		SETS(gens, R_GEN_CONFIG, usb2, select);
+		break;
+	case if_gpio_grp_a:
+		/* GPIO groups are only accounted, don't do configuration changes. */
+		/* fall through */
+	case if_gpio_grp_b:
+		/* fall through */
+	case if_gpio_grp_c:
+		/* fall through */
+	case if_gpio_grp_d:
+		/* fall through */
+	case if_gpio_grp_e:
+		/* fall through */
+	case if_gpio_grp_f:
+		set_gen_config = 0;
+		break;
+	default:
+		panic("cris_request_io_interface: Bad interface %u submitted for %s\n",
+		      ioif,
+		      device_id);
+	}
+
+	interfaces[ioif].used = 1;
+	interfaces[ioif].owner = (char*)device_id;
+
+	if (set_gen_config) {
+		volatile int i;
+		genconfig_shadow = gens;
+		*R_GEN_CONFIG = genconfig_shadow;
+		/* Wait 12 cycles before doing any DMA command */
+		for(i = 6; i > 0; i--)
+			nop();
+	}
+	if (set_gen_config_ii) {
+		gen_config_ii_shadow = gens_ii;
+		*R_GEN_CONFIG_II = gen_config_ii_shadow;
+	}
+
+	DBG(printk("GPIO pins: available before: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+		   gpio_in_pins, gpio_out_pins, gpio_pb_pins));
+	DBG(printk("grabbing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+		   interfaces[ioif].gpio_g_in,
+		   interfaces[ioif].gpio_g_out,
+		   interfaces[ioif].gpio_b));
+
+	gpio_in_pins &= ~interfaces[ioif].gpio_g_in;
+	gpio_out_pins &= ~interfaces[ioif].gpio_g_out;
+	gpio_pb_pins &= ~interfaces[ioif].gpio_b;
+
+	DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+		   gpio_in_pins, gpio_out_pins, gpio_pb_pins));
+
+	local_irq_restore(flags);
+
+	notify_watchers();
+
+	return 0;
+}
+
+
+void cris_free_io_interface(enum cris_io_interface ioif)
+{
+	struct if_group *grp;
+	unsigned char group_set;
+	unsigned long flags;
+
+	(void)cris_io_interface_init();
+
+	if ((ioif >= if_max_interfaces) || (ioif < 0)) {
+		printk(KERN_CRIT "cris_free_io_interface: Bad interface %u\n",
+		       ioif);
+		return;
+	}
+	local_irq_save(flags);
+	if (!interfaces[ioif].used) {
+		printk(KERN_CRIT "cris_free_io_interface: Freeing free interface %u\n",
+		       ioif);
+		local_irq_restore(flags);
+		return;
+	}
+	group_set = interfaces[ioif].groups;
+	while (NULL != (grp = get_group(group_set))) {
+		if (grp->group == group_f) {
+			switch (ioif)
+			{
+			case if_sync_serial_1:
+				if ((grp->owner == if_sync_serial_1) &&
+				    interfaces[if_sync_serial_3].used) {
+					grp->owner = if_sync_serial_3;
+				} else
+					grp->used = 0;
+				break;
+			case if_sync_serial_3:
+				if ((grp->owner == if_sync_serial_3) &&
+				    interfaces[if_sync_serial_1].used) {
+					grp->owner = if_sync_serial_1;
+				} else
+					grp->used = 0;
+				break;
+			case if_scsi8_0:
+				if ((grp->owner == if_scsi8_0) &&
+				    interfaces[if_scsi8_1].used) {
+					grp->owner = if_scsi8_1;
+				} else
+					grp->used = 0;
+				break;
+			case if_scsi8_1:
+				if ((grp->owner == if_scsi8_1) &&
+				    interfaces[if_scsi8_0].used) {
+					grp->owner = if_scsi8_0;
+				} else
+					grp->used = 0;
+				break;
+			default:
+				grp->used = 0;
+			}
+		} else {
+			grp->used = 0;
+		}
+		group_set = clear_group_from_set(group_set, grp);
+	}
+	interfaces[ioif].used = 0;
+	interfaces[ioif].owner = NULL;
+
+	DBG(printk("GPIO pins: available before: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+		   gpio_in_pins, gpio_out_pins, gpio_pb_pins));
+	DBG(printk("freeing pins: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+		   interfaces[ioif].gpio_g_in,
+		   interfaces[ioif].gpio_g_out,
+		   interfaces[ioif].gpio_b));
+
+	gpio_in_pins |= interfaces[ioif].gpio_g_in;
+	gpio_out_pins |= interfaces[ioif].gpio_g_out;
+	gpio_pb_pins |= interfaces[ioif].gpio_b;
+
+	DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+		   gpio_in_pins, gpio_out_pins, gpio_pb_pins));
+
+	local_irq_restore(flags);
+
+	notify_watchers();
+}
+
+/* Create a bitmask from bit 0 (inclusive) to bit stop_bit
+   (non-inclusive).  stop_bit == 0 returns 0x0 */
+static inline unsigned int create_mask(const unsigned stop_bit)
+{
+	/* Avoid overflow */
+	if (stop_bit >= 32) {
+		return 0xffffffff;
+	}
+	return (1<<stop_bit)-1;
+}
+
+
+/* port can be 'a', 'b' or 'g' */
+int cris_io_interface_allocate_pins(const enum cris_io_interface ioif,
+				    const char port,
+				    const unsigned start_bit,
+				    const unsigned stop_bit)
+{
+	unsigned int i;
+	unsigned int mask = 0;
+	unsigned int tmp_mask;
+	unsigned long int flags;
+	enum cris_io_interface *owners;
+
+	(void)cris_io_interface_init();
+
+	DBG(printk("cris_io_interface_allocate_pins: if=%d port=%c start=%u stop=%u\n",
+		   ioif, port, start_bit, stop_bit));
+
+	if (!((start_bit <= stop_bit) &&
+	      ((((port == 'a') || (port == 'b')) && (stop_bit < 8)) ||
+	       ((port == 'g') && (stop_bit < 32))))) {
+		return -EINVAL;
+	}
+
+	mask = create_mask(stop_bit + 1);
+	tmp_mask = create_mask(start_bit);
+	mask &= ~tmp_mask;
+
+	DBG(printk("cris_io_interface_allocate_pins: port=%c start=%u stop=%u mask=0x%08x\n",
+		   port, start_bit, stop_bit, mask));
+
+	local_irq_save(flags);
+
+	switch (port) {
+	case 'a':
+		if ((gpio_pa_pins & mask) != mask) {
+			local_irq_restore(flags);
+			return -EBUSY;
+		}
+		owners = gpio_pa_owners;
+		gpio_pa_pins &= ~mask;
+		break;
+	case 'b':
+		if ((gpio_pb_pins & mask) != mask) {
+			local_irq_restore(flags);
+			return -EBUSY;
+		}
+		owners = gpio_pb_owners;
+		gpio_pb_pins &= ~mask;
+		break;
+	case 'g':
+		if (((gpio_in_pins & mask) != mask) ||
+		    ((gpio_out_pins & mask) != mask)) {
+			local_irq_restore(flags);
+			return -EBUSY;
+		}
+		owners = gpio_pg_owners;
+		gpio_in_pins &= ~mask;
+		gpio_out_pins &= ~mask;
+		break;
+	default:
+		local_irq_restore(flags);
+		return -EINVAL;
+	}
+
+	for (i = start_bit; i <= stop_bit; i++) {
+		owners[i] = ioif;
+	}
+	local_irq_restore(flags);
+
+	notify_watchers();
+	return 0;
+}
+
+
+/* port can be 'a', 'b' or 'g' */
+int cris_io_interface_free_pins(const enum cris_io_interface ioif,
+                                const char port,
+                                const unsigned start_bit,
+                                const unsigned stop_bit)
+{
+	unsigned int i;
+	unsigned int mask = 0;
+	unsigned int tmp_mask;
+	unsigned long int flags;
+	enum cris_io_interface *owners;
+
+	(void)cris_io_interface_init();
+
+	if (!((start_bit <= stop_bit) &&
+	      ((((port == 'a') || (port == 'b')) && (stop_bit < 8)) ||
+	       ((port == 'g') && (stop_bit < 32))))) {
+		return -EINVAL;
+	}
+
+	mask = create_mask(stop_bit + 1);
+	tmp_mask = create_mask(start_bit);
+	mask &= ~tmp_mask;
+
+	DBG(printk("cris_io_interface_free_pins: port=%c start=%u stop=%u mask=0x%08x\n",
+		   port, start_bit, stop_bit, mask));
+
+	local_irq_save(flags);
+
+	switch (port) {
+	case 'a':
+		if ((~gpio_pa_pins & mask) != mask) {
+			local_irq_restore(flags);
+			printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
+		}
+		owners = gpio_pa_owners;
+		break;
+	case 'b':
+		if ((~gpio_pb_pins & mask) != mask) {
+			local_irq_restore(flags);
+			printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
+		}
+		owners = gpio_pb_owners;
+		break;
+	case 'g':
+		if (((~gpio_in_pins & mask) != mask) ||
+		    ((~gpio_out_pins & mask) != mask)) {
+			local_irq_restore(flags);
+			printk(KERN_CRIT "cris_io_interface_free_pins: Freeing free pins");
+		}
+		owners = gpio_pg_owners;
+		break;
+	default:
+		owners = NULL; /* Cannot happen. Shut up, gcc! */
+	}
+
+	for (i = start_bit; i <= stop_bit; i++) {
+		if (owners[i] != ioif) {
+			printk(KERN_CRIT "cris_io_interface_free_pins: Freeing unowned pins");
+		}
+	}
+
+	/* All was ok, change data. */
+	switch (port) {
+	case 'a':
+		gpio_pa_pins |= mask;
+		break;
+	case 'b':
+		gpio_pb_pins |= mask;
+		break;
+	case 'g':
+		gpio_in_pins |= mask;
+		gpio_out_pins |= mask;
+		break;
+	}
+
+	for (i = start_bit; i <= stop_bit; i++) {
+		owners[i] = if_unclaimed;
+	}
+	local_irq_restore(flags);
+	notify_watchers();
+
+        return 0;
+}
+
+
+int cris_io_interface_register_watcher(void (*notify)(const unsigned int gpio_in_available,
+                                                      const unsigned int gpio_out_available,
+                                                      const unsigned char pa_available,
+                                                      const unsigned char pb_available))
+{
+	struct watcher *w;
+
+	(void)cris_io_interface_init();
+
+	if (NULL == notify) {
+		return -EINVAL;
+	}
+	w = kmalloc(sizeof(*w), GFP_KERNEL);
+	if (!w) {
+		return -ENOMEM;
+	}
+	w->notify = notify;
+	w->next = watchers;
+	watchers = w;
+
+	w->notify((const unsigned int)gpio_in_pins,
+		  (const unsigned int)gpio_out_pins,
+		  (const unsigned char)gpio_pa_pins,
+		  (const unsigned char)gpio_pb_pins);
+
+	return 0;
+}
+
+void cris_io_interface_delete_watcher(void (*notify)(const unsigned int gpio_in_available,
+						     const unsigned int gpio_out_available,
+                                                     const unsigned char pa_available,
+						     const unsigned char pb_available))
+{
+	struct watcher *w = watchers, *prev = NULL;
+
+	(void)cris_io_interface_init();
+
+	while ((NULL != w) && (w->notify != notify)){
+		prev = w;
+		w = w->next;
+	}
+	if (NULL != w) {
+		if (NULL != prev) {
+			prev->next = w->next;
+		} else {
+			watchers = w->next;
+		}
+		kfree(w);
+		return;
+	}
+	printk(KERN_WARNING "cris_io_interface_delete_watcher: Deleting unknown watcher 0x%p\n", notify);
+}
+
+
+static int cris_io_interface_init(void)
+{
+	static int first = 1;
+	int i;
+
+	if (!first) {
+		return 0;
+	}
+	first = 0;
+
+	for (i = 0; i<8; i++) {
+		gpio_pa_owners[i] = if_unclaimed;
+		gpio_pb_owners[i] = if_unclaimed;
+		gpio_pg_owners[i] = if_unclaimed;
+	}
+	for (; i<32; i++) {
+		gpio_pg_owners[i] = if_unclaimed;
+	}
+	return 0;
+}
+
+
+module_init(cris_io_interface_init);
+
+
+EXPORT_SYMBOL(cris_request_io_interface);
+EXPORT_SYMBOL(cris_free_io_interface);
+EXPORT_SYMBOL(cris_io_interface_allocate_pins);
+EXPORT_SYMBOL(cris_io_interface_free_pins);
+EXPORT_SYMBOL(cris_io_interface_register_watcher);
+EXPORT_SYMBOL(cris_io_interface_delete_watcher);

+ 43 - 33
arch/cris/arch-v10/kernel/irq.c

@@ -1,4 +1,4 @@
-/* $Id: irq.c,v 1.2 2004/06/09 05:30:27 starvik Exp $
+/* $Id: irq.c,v 1.4 2005/01/04 12:22:28 starvik Exp $
  *
  *	linux/arch/cris/kernel/irq.c
  *
@@ -12,11 +12,13 @@
  */
 
 #include <asm/irq.h>
+#include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/config.h>
 
-irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */
+#define mask_irq(irq_nr) (*R_VECT_MASK_CLR = 1 << (irq_nr));
+#define unmask_irq(irq_nr) (*R_VECT_MASK_SET = 1 << (irq_nr));
 
 /* don't use set_int_vector, it bypasses the linux interrupt handlers. it is
  * global just so that the kernel gdb can use it.
@@ -102,41 +104,52 @@ static void (*interrupt[NR_IRQS])(void) = {
 	IRQ31_interrupt
 };
 
-static void (*bad_interrupt[NR_IRQS])(void) = {
-        NULL, NULL,
-	NULL, bad_IRQ3_interrupt,
-	bad_IRQ4_interrupt, bad_IRQ5_interrupt,
-	bad_IRQ6_interrupt, bad_IRQ7_interrupt,
-	bad_IRQ8_interrupt, bad_IRQ9_interrupt,
-	bad_IRQ10_interrupt, bad_IRQ11_interrupt,
-	bad_IRQ12_interrupt, bad_IRQ13_interrupt,
-	NULL, NULL,
-	bad_IRQ16_interrupt, bad_IRQ17_interrupt,
-	bad_IRQ18_interrupt, bad_IRQ19_interrupt,
-	bad_IRQ20_interrupt, bad_IRQ21_interrupt,
-	bad_IRQ22_interrupt, bad_IRQ23_interrupt,
-	bad_IRQ24_interrupt, bad_IRQ25_interrupt,
-	NULL, NULL, NULL, NULL, NULL,
-	bad_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
 
-void arch_setup_irq(int irq)
+static void enable_crisv10_irq(unsigned int irq)
 {
-  set_int_vector(irq, interrupt[irq]);
+	unmask_irq(irq);
 }
 
-void arch_free_irq(int irq)
+static void disable_crisv10_irq(unsigned int irq)
 {
-  set_int_vector(irq, bad_interrupt[irq]);
+	mask_irq(irq);
 }
 
+static void ack_crisv10_irq(unsigned int irq)
+{
+}
+
+static void end_crisv10_irq(unsigned int irq)
+{
+}
+
+static struct hw_interrupt_type crisv10_irq_type = {
+	.typename =    "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
+};
+
 void weird_irq(void);
 void system_call(void);  /* from entry.S */
 void do_sigtrap(void); /* from entry.S */
 void gdb_handle_breakpoint(void); /* from entry.S */
 
 /* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and
-   setting the irq vector table to point to bad_interrupt ptrs.
+   setting the irq vector table.
 */
 
 void __init
@@ -154,14 +167,15 @@ init_IRQ(void)
 
 	*R_VECT_MASK_CLR = 0xffffffff;
 
-	/* clear the shortcut entry points */
-
-	for(i = 0; i < NR_IRQS; i++)
-		irq_shortcuts[i] = NULL;
-        
         for (i = 0; i < 256; i++)
                etrax_irv->v[i] = weird_irq;
 
+	/* Initialize IRQ handler descriptiors. */
+	for(i = 2; i < NR_IRQS; i++) {
+		irq_desc[i].handler = &crisv10_irq_type;
+		set_int_vector(i, interrupt[i]);
+	}
+
         /* the entries in the break vector contain actual code to be
            executed by the associated break handler, rather than just a jump
            address. therefore we need to setup a default breakpoint handler
@@ -170,10 +184,6 @@ init_IRQ(void)
 	for (i = 0; i < 16; i++)
                 set_break_vector(i, do_sigtrap);
         
-	/* set all etrax irq's to the bad handlers */
-	for (i = 2; i < NR_IRQS; i++)
-		set_int_vector(i, bad_interrupt[i]);
-        
 	/* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */
 
 	set_int_vector(15, multiple_interrupt);

+ 13 - 4
arch/cris/arch-v10/kernel/kgdb.c

@@ -18,6 +18,10 @@
 *! Jul 21 1999  Bjorn Wesen     eLinux port
 *!
 *! $Log: kgdb.c,v $
+*! Revision 1.6  2005/01/14 10:12:17  starvik
+*! KGDB on separate port.
+*! Console fixes from 2.4.
+*!
 *! Revision 1.5  2004/10/07 13:59:08  starvik
 *! Corrected call to set_int_vector
 *!
@@ -71,7 +75,7 @@
 *!
 *!---------------------------------------------------------------------------
 *!
-*! $Id: kgdb.c,v 1.5 2004/10/07 13:59:08 starvik Exp $
+*! $Id: kgdb.c,v 1.6 2005/01/14 10:12:17 starvik Exp $
 *!
 *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN
 *!
@@ -225,6 +229,7 @@
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/linkage.h>
+#include <linux/reboot.h>
 
 #include <asm/setup.h>
 #include <asm/ptrace.h>
@@ -1344,12 +1349,11 @@ handle_exception (int sigval)
 	}
 }
 
-/* The jump is to the address 0x00000002. Performs a complete re-start
-   from scratch. */
+/* Performs a complete re-start from scratch. */
 static void
 kill_restart ()
 {
-	__asm__ volatile ("jump 2");
+	machine_restart("");
 }
 
 /********************************** Breakpoint *******************************/
@@ -1506,6 +1510,11 @@ kgdb_handle_serial:
   bne goback
   nop
 
+  move.d  [reg+0x5E], $r10		; Get DCCR
+  btstq	   8, $r10			; Test the U-flag.
+  bmi	   goback
+  nop
+
 ;;
 ;; Handle the communication
 ;;

+ 2 - 1
arch/cris/arch-v10/kernel/process.c

@@ -1,4 +1,4 @@
-/* $Id: process.c,v 1.9 2004/10/19 13:07:37 starvik Exp $
+/* $Id: process.c,v 1.12 2004/12/27 11:18:32 starvik Exp $
  * 
  *  linux/arch/cris/kernel/process.c
  *
@@ -101,6 +101,7 @@ int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 	regs.r11 = (unsigned long)fn;
 	regs.r12 = (unsigned long)arg;
 	regs.irp = (unsigned long)kernel_thread_helper;
+	regs.dccr = 1 << I_DCCR_BITNR;
 
 	/* Ok, create the new process.. */
         return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);

+ 7 - 2
arch/cris/arch-v10/kernel/ptrace.c

@@ -11,6 +11,7 @@
 #include <linux/ptrace.h>
 #include <linux/user.h>
 #include <linux/signal.h>
+#include <linux/security.h>
 
 #include <asm/uaccess.h>
 #include <asm/page.h>
@@ -86,9 +87,13 @@ sys_ptrace(long request, long pid, long addr, long data)
 	ret = -EPERM;
 	
 	if (request == PTRACE_TRACEME) {
+		/* are we already being traced? */
 		if (current->ptrace & PT_PTRACED)
 			goto out;
-
+		ret = security_ptrace(current->parent, current);
+		if (ret)
+			goto out;
+		/* set the ptrace bit in the process flags. */
 		current->ptrace |= PT_PTRACED;
 		ret = 0;
 		goto out;
@@ -207,7 +212,7 @@ sys_ptrace(long request, long pid, long addr, long data)
 		case PTRACE_KILL:
 			ret = 0;
 			
-			if (child->state == TASK_ZOMBIE)
+			if (child->exit_state == EXIT_ZOMBIE)
 				break;
 			
 			child->exit_code = SIGKILL;

+ 2 - 1
arch/cris/arch-v10/kernel/shadows.c

@@ -1,4 +1,4 @@
-/* $Id: shadows.c,v 1.1 2001/12/17 13:59:27 bjornw Exp $
+/* $Id: shadows.c,v 1.2 2004/12/13 12:21:51 starvik Exp $
  * 
  * Various shadow registers. Defines for these are in include/asm-etrax100/io.h
  */
@@ -6,6 +6,7 @@
 /* Shadows for internal Etrax-registers */
 
 unsigned long genconfig_shadow;
+unsigned long gen_config_ii_shadow;
 unsigned long port_g_data_shadow;
 unsigned char port_pa_dir_shadow;
 unsigned char port_pa_data_shadow;

+ 22 - 15
arch/cris/arch-v10/kernel/traps.c

@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.2 2003/07/04 08:27:41 starvik Exp $
+/* $Id: traps.c,v 1.4 2005/04/24 18:47:55 starvik Exp $
  *
  *  linux/arch/cris/arch-v10/traps.c
  *
@@ -16,6 +16,8 @@
 #include <asm/uaccess.h>
 #include <asm/arch/sv_addr_ag.h>
 
+extern int raw_printk(const char *fmt, ...);
+
 void 
 show_registers(struct pt_regs * regs)
 {
@@ -26,18 +28,18 @@ show_registers(struct pt_regs * regs)
 	   register.  */
 	unsigned long usp = rdusp();
 
-	printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n",
+	raw_printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n",
 	       regs->irp, regs->srp, regs->dccr, usp, regs->mof );
-	printk(" r0: %08lx  r1: %08lx   r2: %08lx  r3: %08lx\n",
+	raw_printk(" r0: %08lx  r1: %08lx   r2: %08lx  r3: %08lx\n",
 	       regs->r0, regs->r1, regs->r2, regs->r3);
-	printk(" r4: %08lx  r5: %08lx   r6: %08lx  r7: %08lx\n",
+	raw_printk(" r4: %08lx  r5: %08lx   r6: %08lx  r7: %08lx\n",
 	       regs->r4, regs->r5, regs->r6, regs->r7);
-	printk(" r8: %08lx  r9: %08lx  r10: %08lx r11: %08lx\n",
+	raw_printk(" r8: %08lx  r9: %08lx  r10: %08lx r11: %08lx\n",
 	       regs->r8, regs->r9, regs->r10, regs->r11);
-	printk("r12: %08lx r13: %08lx oR10: %08lx\n",
-	       regs->r12, regs->r13, regs->orig_r10);
-	printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
-	printk("Process %s (pid: %d, stackpage=%08lx)\n",
+	raw_printk("r12: %08lx r13: %08lx oR10: %08lx  sp: %08lx\n",
+	       regs->r12, regs->r13, regs->orig_r10, regs);
+	raw_printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
+	raw_printk("Process %s (pid: %d, stackpage=%08lx)\n",
 	       current->comm, current->pid, (unsigned long)current);
 
 	/*
@@ -53,7 +55,7 @@ show_registers(struct pt_regs * regs)
 		if (usp != 0)
 			show_stack (NULL, NULL);
 
-                printk("\nCode: ");
+                raw_printk("\nCode: ");
                 if(regs->irp < PAGE_OFFSET)
                         goto bad;
 
@@ -70,16 +72,16 @@ show_registers(struct pt_regs * regs)
                         unsigned char c;
                         if(__get_user(c, &((unsigned char*)regs->irp)[i])) {
 bad:
-                                printk(" Bad IP value.");
+                                raw_printk(" Bad IP value.");
                                 break;
                         }
 
 			if (i == 0)
-			  printk("(%02x) ", c);
+			  raw_printk("(%02x) ", c);
 			else
-			  printk("%02x ", c);
+			  raw_printk("%02x ", c);
                 }
-		printk("\n");
+		raw_printk("\n");
         }
 }
 
@@ -121,7 +123,7 @@ die_if_kernel(const char * str, struct pt_regs * regs, long err)
 	stop_watchdog();
 #endif
 
-	printk("%s: %04lx\n", str, err & 0xffff);
+	raw_printk("%s: %04lx\n", str, err & 0xffff);
 
 	show_registers(regs);
 
@@ -130,3 +132,8 @@ die_if_kernel(const char * str, struct pt_regs * regs, long err)
 #endif
 	do_exit(SIGSEGV);
 }
+
+void arch_enable_nmi(void)
+{
+  asm volatile("setf m");
+}

+ 2 - 24
arch/cris/arch-v10/mm/fault.c

@@ -14,6 +14,7 @@
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/arch/svinto.h>
+#include <asm/mmu_context.h>
 
 /* debug of low-level TLB reload */
 #undef DEBUG
@@ -24,8 +25,6 @@
 #define D(x)
 #endif
 
-extern volatile pgd_t *current_pgd;
-
 extern const struct exception_table_entry
 	*search_exception_tables(unsigned long addr);
 
@@ -46,7 +45,7 @@ handle_mmu_bus_fault(struct pt_regs *regs)
 	int page_id;
 	int acc, inv;
 #endif
-	pgd_t* pgd = (pgd_t*)current_pgd;
+	pgd_t* pgd = (pgd_t*)per_cpu(current_pgd, smp_processor_id());
 	pmd_t *pmd;
 	pte_t pte;
 	int miss, we, writeac;
@@ -94,24 +93,3 @@ handle_mmu_bus_fault(struct pt_regs *regs)
 	*R_TLB_LO = pte_val(pte);
 	local_irq_restore(flags);
 }
-
-/* Called from arch/cris/mm/fault.c to find fixup code. */
-int
-find_fixup_code(struct pt_regs *regs)
-{
-	const struct exception_table_entry *fixup;
-
-	if ((fixup = search_exception_tables(regs->irp)) != 0) {
-		/* Adjust the instruction pointer in the stackframe. */
-		regs->irp = fixup->fixup;
-		
-		/* 
-		 * Don't return by restoring the CPU state, so switch
-		 * frame-type. 
-		 */
-		regs->frametype = CRIS_FRAME_NORMAL;
-		return 1;
-	}
-
-	return 0;
-}

+ 1 - 1
arch/cris/arch-v10/mm/init.c

@@ -42,7 +42,7 @@ paging_init(void)
 	 *  switch_mm)
 	 */
 
-	current_pgd = init_mm.pgd;
+	per_cpu(current_pgd, smp_processor_id()) = init_mm.pgd;
 
 	/* initialise the TLB (tlb.c) */
 

+ 1 - 48
arch/cris/arch-v10/mm/tlb.c

@@ -139,53 +139,6 @@ flush_tlb_page(struct vm_area_struct *vma,
 	local_irq_restore(flags);
 }
 
-/* invalidate a page range */
-
-void
-flush_tlb_range(struct vm_area_struct *vma, 
-		unsigned long start,
-		unsigned long end)
-{
-	struct mm_struct *mm = vma->vm_mm;
-	int page_id = mm->context.page_id;
-	int i;
-	unsigned long flags;
-
-	D(printk("tlb: flush range %p<->%p in context %d (%p)\n",
-		 start, end, page_id, mm));
-
-	if(page_id == NO_CONTEXT)
-		return;
-
-	start &= PAGE_MASK;  /* probably not necessary */
-	end &= PAGE_MASK;    /* dito */
-
-	/* invalidate those TLB entries that match both the mm context
-	 * and the virtual address range
-	 */
-
-	local_save_flags(flags);
-	local_irq_disable();
-	for(i = 0; i < NUM_TLB_ENTRIES; i++) {
-		unsigned long tlb_hi, vpn;
-		*R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i);
-		tlb_hi = *R_TLB_HI;
-		vpn = tlb_hi & PAGE_MASK;
-		if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id &&
-		    vpn >= start && vpn < end) {
-			*R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) |
-				      IO_FIELD(R_TLB_HI, vpn,     i & 0xf ) );
-			
-			*R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no  ) |
-				      IO_STATE(R_TLB_LO, valid, no  ) |
-				      IO_STATE(R_TLB_LO, kernel,no  ) |
-				      IO_STATE(R_TLB_LO, we,    no  ) |
-				      IO_FIELD(R_TLB_LO, pfn,   0   ) );
-		}
-	}
-	local_irq_restore(flags);
-}
-
 /* dump the entire TLB for debug purposes */
 
 #if 0
@@ -237,7 +190,7 @@ switch_mm(struct mm_struct *prev, struct mm_struct *next,
 	 * the pgd.
 	 */
 
-	current_pgd = next->pgd;
+	per_cpu(current_pgd, smp_processor_id()) = next->pgd;
 
 	/* switch context in the MMU */
 	

+ 296 - 0
arch/cris/arch-v32/Kconfig

@@ -0,0 +1,296 @@
+config ETRAX_DRAM_VIRTUAL_BASE
+	hex
+	depends on ETRAX_ARCH_V32
+	default "c0000000"
+
+config ETRAX_LED1G
+	string "First green LED bit"
+	depends on ETRAX_ARCH_V32
+	default "PA3"
+	help
+	  Bit to use for the first green LED (network LED).
+	  Most Axis products use bit A3 here.
+
+config ETRAX_LED1R
+	string "First red LED bit"
+	depends on ETRAX_ARCH_V32
+	default "PA4"
+	help
+	  Bit to use for the first red LED (network LED).
+	  Most Axis products use bit A4 here.
+
+config ETRAX_LED2G
+	string "Second green LED bit"
+	depends on ETRAX_ARCH_V32
+	default "PA5"
+	help
+	  Bit to use for the first green LED (status LED).
+	  Most Axis products use bit A5 here.
+
+config ETRAX_LED2R
+	string "Second red LED bit"
+	depends on ETRAX_ARCH_V32
+	default "PA6"
+	help
+	  Bit to use for the first red LED (network LED).
+	  Most Axis products use bit A6 here.
+
+config ETRAX_LED3G
+	string "Third green LED bit"
+	depends on ETRAX_ARCH_V32
+	default "PA7"
+	help
+	  Bit to use for the first green LED (drive/power LED).
+	  Most Axis products use bit A7 here.
+
+config ETRAX_LED3R
+	string "Third red LED bit"
+	depends on ETRAX_ARCH_V32
+	default "PA7"
+	help
+	  Bit to use for the first red LED (drive/power LED).
+	  Most Axis products use bit A7 here.
+
+choice
+	prompt "Product debug-port"
+	depends on ETRAX_ARCH_V32
+	default ETRAX_DEBUG_PORT0
+
+config ETRAX_DEBUG_PORT0
+	bool "Serial-0"
+	help
+	  Choose a serial port for the ETRAX debug console.  Default to
+	  port 0.
+
+config ETRAX_DEBUG_PORT1
+	bool "Serial-1"
+	help
+	  Use serial port 1 for the console.
+
+config ETRAX_DEBUG_PORT2
+	bool "Serial-2"
+	help
+	  Use serial port 2 for the console.
+
+config ETRAX_DEBUG_PORT3
+	bool "Serial-3"
+	help
+	  Use serial port 3 for the console.
+
+config ETRAX_DEBUG_PORT_NULL
+	bool "disabled"
+	help
+	  Disable serial-port debugging.
+
+endchoice
+
+choice
+	prompt "Kernel GDB port"
+	depends on ETRAX_KGDB
+	default ETRAX_KGDB_PORT0
+	help
+	  Choose a serial port for kernel debugging.  NOTE: This port should
+	  not be enabled under Drivers for built-in interfaces (as it has its
+	  own initialization code) and should not be the same as the debug port.
+
+config ETRAX_KGDB_PORT0
+	bool "Serial-0"
+	help
+	  Use serial port 0 for kernel debugging.
+
+config ETRAX_KGDB_PORT1
+	bool "Serial-1"
+	help
+	  Use serial port 1 for kernel debugging.
+
+config ETRAX_KGDB_PORT2
+	bool "Serial-2"
+	help
+	  Use serial port 2 for kernel debugging.
+
+config ETRAX_KGDB_PORT3
+	bool "Serial-3"
+	help
+	  Use serial port 3 for kernel debugging.
+
+endchoice
+
+config ETRAX_MEM_GRP1_CONFIG
+	hex "MEM_GRP1_CONFIG"
+	depends on ETRAX_ARCH_V32
+	default "4044a"
+	help
+	  Waitstates for flash. The default value is suitable for the
+	  standard flashes used in axis products (120 ns).
+
+config ETRAX_MEM_GRP2_CONFIG
+	hex "MEM_GRP2_CONFIG"
+	depends on ETRAX_ARCH_V32
+	default "0"
+	help
+	  Waitstates for SRAM. 0 is a good choice for most Axis products.
+
+config ETRAX_MEM_GRP3_CONFIG
+	hex "MEM_GRP3_CONFIG"
+	depends on ETRAX_ARCH_V32
+	default "0"
+	help
+	  Waitstates for CSP0-3. 0 is a good choice for most Axis products.
+	  It may need to be changed if external devices such as extra
+	  register-mapped LEDs are used.
+
+config ETRAX_MEM_GRP4_CONFIG
+	hex "MEM_GRP4_CONFIG"
+	depends on ETRAX_ARCH_V32
+	default "0"
+	help
+	  Waitstates for CSP4-6. 0 is a good choice for most Axis products.
+
+config ETRAX_SDRAM_GRP0_CONFIG
+	hex "SDRAM_GRP0_CONFIG"
+	depends on ETRAX_ARCH_V32
+	default "336"
+	help
+	  SDRAM configuration for group 0. The value depends on the
+	  hardware configuration. The default value is suitable
+	  for 32 MB organized as two 16 bits chips (e.g. Axis
+	  part number 18550) connected as one 32 bit device (i.e. in
+	  the same group).
+
+config ETRAX_SDRAM_GRP1_CONFIG
+	hex "SDRAM_GRP1_CONFIG"
+	depends on ETRAX_ARCH_V32
+	default "0"
+	help
+	  SDRAM configuration for group 1. The defult value is 0
+	  because group 1 is not used in the default configuration,
+	  described in the help for SDRAM_GRP0_CONFIG.
+
+config ETRAX_SDRAM_TIMING
+	hex "SDRAM_TIMING"
+	depends on ETRAX_ARCH_V32
+	default "104a"
+	help
+	  SDRAM timing parameters. The default value is ok for
+	  most hardwares but large SDRAMs may require a faster
+	  refresh (a.k.a 8K refresh). The default value implies
+	  100MHz clock and SDR mode.
+
+config ETRAX_SDRAM_COMMAND
+	hex "SDRAM_COMMAND"
+	depends on ETRAX_ARCH_V32
+	default "0"
+	help
+	  SDRAM command. Should be 0 unless you really know what
+	  you are doing (may be != 0 for unusual address line
+	  mappings such as in a MCM)..
+
+config ETRAX_DEF_GIO_PA_OE
+	hex "GIO_PA_OE"
+	depends on ETRAX_ARCH_V32
+	default "1c"
+	help
+	  Configures the direction of general port A bits.  1 is out, 0 is in.
+	  This is often totally different depending on the product used.
+	  There are some guidelines though - if you know that only LED's are
+	  connected to port PA, then they are usually connected to bits 2-4
+	  and you can therefore use 1c.  On other boards which don't have the
+	  LED's at the general ports, these bits are used for all kinds of
+	  stuff.  If you don't know what to use, it is always safe to put all
+	  as inputs, although floating inputs isn't good.
+
+config ETRAX_DEF_GIO_PA_OUT
+	hex "GIO_PA_OUT"
+	depends on ETRAX_ARCH_V32
+	default "00"
+	help
+	  Configures the initial data for the general port A bits.  Most
+	  products should use 00 here.
+
+config ETRAX_DEF_GIO_PB_OE
+	hex "GIO_PB_OE"
+	depends on ETRAX_ARCH_V32
+	default "00000"
+	help
+	  Configures the direction of general port B bits.  1 is out, 0 is in.
+	  This is often totally different depending on the product used.
+	  There are some guidelines though - if you know that only LED's are
+	  connected to port PA, then they are usually connected to bits 2-4
+	  and you can therefore use 1c.  On other boards which don't have the
+	  LED's at the general ports, these bits are used for all kinds of
+	  stuff.  If you don't know what to use, it is always safe to put all
+	  as inputs, although floating inputs isn't good.
+
+config ETRAX_DEF_GIO_PB_OUT
+	hex "GIO_PB_OUT"
+	depends on ETRAX_ARCH_V32
+	default "00000"
+	help
+	  Configures the initial data for the general port B bits.  Most
+	  products should use 00000 here.
+
+config ETRAX_DEF_GIO_PC_OE
+	hex "GIO_PC_OE"
+	depends on ETRAX_ARCH_V32
+	default "00000"
+	help
+	  Configures the direction of general port C bits.  1 is out, 0 is in.
+	  This is often totally different depending on the product used.
+	  There are some guidelines though - if you know that only LED's are
+	  connected to port PA, then they are usually connected to bits 2-4
+	  and you can therefore use 1c.  On other boards which don't have the
+	  LED's at the general ports, these bits are used for all kinds of
+	  stuff.  If you don't know what to use, it is always safe to put all
+	  as inputs, although floating inputs isn't good.
+
+config ETRAX_DEF_GIO_PC_OUT
+	hex "GIO_PC_OUT"
+	depends on ETRAX_ARCH_V32
+	default "00000"
+	help
+	  Configures the initial data for the general port C bits.  Most
+	  products should use 00000 here.
+
+config ETRAX_DEF_GIO_PD_OE
+	hex "GIO_PD_OE"
+	depends on ETRAX_ARCH_V32
+	default "00000"
+	help
+	  Configures the direction of general port D bits.  1 is out, 0 is in.
+	  This is often totally different depending on the product used.
+	  There are some guidelines though - if you know that only LED's are
+	  connected to port PA, then they are usually connected to bits 2-4
+	  and you can therefore use 1c.  On other boards which don't have the
+	  LED's at the general ports, these bits are used for all kinds of
+	  stuff.  If you don't know what to use, it is always safe to put all
+	  as inputs, although floating inputs isn't good.
+
+config ETRAX_DEF_GIO_PD_OUT
+	hex "GIO_PD_OUT"
+	depends on ETRAX_ARCH_V32
+	default "00000"
+	help
+	  Configures the initial data for the general port D bits.  Most
+	  products should use 00000 here.
+
+config ETRAX_DEF_GIO_PE_OE
+	hex "GIO_PE_OE"
+	depends on ETRAX_ARCH_V32
+	default "00000"
+	help
+	  Configures the direction of general port E bits.  1 is out, 0 is in.
+	  This is often totally different depending on the product used.
+	  There are some guidelines though - if you know that only LED's are
+	  connected to port PA, then they are usually connected to bits 2-4
+	  and you can therefore use 1c.  On other boards which don't have the
+	  LED's at the general ports, these bits are used for all kinds of
+	  stuff.  If you don't know what to use, it is always safe to put all
+	  as inputs, although floating inputs isn't good.
+
+config ETRAX_DEF_GIO_PE_OUT
+	hex "GIO_PE_OUT"
+	depends on ETRAX_ARCH_V32
+	default "00000"
+	help
+	  Configures the initial data for the general port E bits.  Most
+	  products should use 00000 here.

+ 14 - 0
arch/cris/arch-v32/boot/Makefile

@@ -0,0 +1,14 @@
+#
+# arch/cris/arch-v32/boot/Makefile
+#
+target = $(target_boot_dir)
+src    = $(src_boot_dir)
+
+zImage: compressed/vmlinuz
+
+compressed/vmlinuz: $(objtree)/vmlinux
+	@$(MAKE) -f $(src)/compressed/Makefile $(objtree)/vmlinuz
+
+clean:
+	rm -f zImage tools/build compressed/vmlinux.out
+	@$(MAKE) -f $(src)/compressed/Makefile clean

+ 41 - 0
arch/cris/arch-v32/boot/compressed/Makefile

@@ -0,0 +1,41 @@
+#
+# lx25/arch/cris/arch-v32/boot/compressed/Makefile
+#
+# create a compressed vmlinux image from the original vmlinux files and romfs
+#
+
+target = $(target_compressed_dir)
+src    = $(src_compressed_dir)
+
+CC = gcc-cris -mlinux -march=v32 -I $(TOPDIR)/include
+CFLAGS = -O2
+LD = gcc-cris -mlinux -march=v32 -nostdlib
+OBJCOPY = objcopy-cris
+OBJCOPYFLAGS = -O binary --remove-section=.bss
+OBJECTS = $(target)/head.o $(target)/misc.o
+
+# files to compress
+SYSTEM = $(objtree)/vmlinux.bin
+
+all: vmlinuz
+
+$(target)/decompress.bin: $(OBJECTS)
+	$(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS)
+	$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin
+
+$(objtree)/vmlinuz: $(target) piggy.img $(target)/decompress.bin
+	cat $(target)/decompress.bin piggy.img > $(objtree)/vmlinuz
+	rm -f piggy.img
+	cp $(objtree)/vmlinuz $(src)
+
+$(target)/head.o: $(src)/head.S
+	$(CC) -D__ASSEMBLY__ -c $< -o $@
+
+# gzip the kernel image
+
+piggy.img: $(SYSTEM)
+	cat $(SYSTEM) | gzip -f -9 > piggy.img
+
+clean:
+	rm -f piggy.img $(objtree)/vmlinuz vmlinuz.o decompress.o decompress.bin $(OBJECTS)
+

+ 25 - 0
arch/cris/arch-v32/boot/compressed/README

@@ -0,0 +1,25 @@
+Creation of the self-extracting compressed kernel image (vmlinuz)
+-----------------------------------------------------------------
+$Id: README,v 1.1 2003/08/21 09:37:03 johana Exp $
+
+This can be slightly confusing because it's a process with many steps.
+
+The kernel object built by the arch/etrax100/Makefile, vmlinux, is split
+by that makefile into text and data binary files, vmlinux.text and
+vmlinux.data.
+
+Those files together with a ROM filesystem can be catted together and
+burned into a flash or executed directly at the DRAM origin.
+
+They can also be catted together and compressed with gzip, which is what
+happens in this makefile. Together they make up piggy.img.
+
+The decompressor is built into the file decompress.o. It is turned into
+the binary file decompress.bin, which is catted together with piggy.img
+into the file vmlinuz. It can be executed in an arbitrary place in flash.
+
+Be careful - it assumes some things about free locations in DRAM. It
+assumes the DRAM starts at 0x40000000 and that it is at least 8 MB,
+so it puts its code at 0x40700000, and initial stack at 0x40800000.
+
+-Bjorn

+ 30 - 0
arch/cris/arch-v32/boot/compressed/decompress.ld

@@ -0,0 +1,30 @@
+/*#OUTPUT_FORMAT(elf32-us-cris) */
+OUTPUT_ARCH (crisv32)
+
+MEMORY
+	{
+	dram : ORIGIN = 0x40700000,
+	       LENGTH = 0x00100000
+	}
+
+SECTIONS
+{
+	.text :
+	{
+		_stext = . ;
+		*(.text)
+		*(.rodata)
+		*(.rodata.*)
+		_etext = . ;
+	} > dram
+	.data :
+	{
+		*(.data)
+		_edata = . ;
+	} > dram
+	.bss :
+	{
+		*(.bss)
+		_end = ALIGN( 0x10 ) ;
+	} > dram
+}

+ 193 - 0
arch/cris/arch-v32/boot/compressed/head.S

@@ -0,0 +1,193 @@
+/*
+ *  Code that sets up the DRAM registers, calls the
+ *  decompressor to unpack the piggybacked kernel, and jumps.
+ *
+ *  Copyright (C) 1999 - 2003, Axis Communications AB
+ */
+
+#include <linux/config.h>
+#define ASSEMBLER_MACROS_ONLY
+#include <asm/arch/hwregs/asm/reg_map_asm.h>
+#include <asm/arch/hwregs/asm/gio_defs_asm.h>
+#include <asm/arch/hwregs/asm/config_defs_asm.h>
+
+#define RAM_INIT_MAGIC 0x56902387
+#define COMMAND_LINE_MAGIC 0x87109563
+
+	;; Exported symbols
+
+	.globl	input_data
+
+	.text
+start:
+	di
+
+	;; Start clocks for used blocks.
+	move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1
+	move.d [$r1], $r0
+	or.d   REG_STATE(config, rw_clk_ctrl, cpu, yes) | \
+	       REG_STATE(config, rw_clk_ctrl, bif, yes) | \
+	       REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0
+	move.d $r0, [$r1]
+
+	;; If booting from NAND flash we first have to copy some
+	;; data from NAND flash to internal RAM to get the code
+	;; that initializes the SDRAM. Lets copy 20 KB. This
+	;; code executes at 0x38010000 if booting from NAND and
+	;; we are guaranted that at least 0x200 bytes are good so
+	;; lets start from there. The first 8192 bytes in the nand
+	;; flash is spliced with zeroes and is thus 16384 bytes.
+	move.d 0x38010200, $r10
+	move.d 0x14200, $r11	; Start offset in NAND flash 0x10200 + 16384
+	move.d 0x5000, $r12	; Length of copy
+
+	;; Before this code the tools add a partitiontable so the PC
+	;; has an offset from the linked address.
+offset1:
+	lapcq  ., $r13		; get PC
+	add.d	first_copy_complete-offset1, $r13
+
+#include "../../lib/nand_init.S"
+
+first_copy_complete:
+	;; Initialze the DRAM registers.
+	cmp.d	RAM_INIT_MAGIC, $r8	; Already initialized?
+	beq	dram_init_finished
+	nop
+
+#include "../../lib/dram_init.S"
+
+dram_init_finished:
+	lapcq  ., $r13		; get PC
+	add.d	second_copy_complete-dram_init_finished, $r13
+
+	move.d REG_ADDR(config, regi_config, r_bootsel), $r0
+	move.d [$r0], $r0
+	and.d  REG_MASK(config, r_bootsel, boot_mode), $r0
+	cmp.d  REG_STATE(config, r_bootsel, boot_mode, nand), $r0
+	bne second_copy_complete ; No NAND boot
+	nop
+
+	;; Copy 2MB from NAND flash to SDRAM (at 2-4MB into the SDRAM)
+	move.d 0x40204000, $r10
+	move.d 0x8000, $r11
+	move.d 0x200000, $r12
+	ba copy_nand_to_ram
+	nop
+second_copy_complete:
+
+	;; Initiate the PA port.
+	move.d	CONFIG_ETRAX_DEF_GIO_PA_OUT, $r0
+	move.d	REG_ADDR(gio, regi_gio, rw_pa_dout), $r1
+	move.d	$r0, [$r1]
+
+	move.d	CONFIG_ETRAX_DEF_GIO_PA_OE, $r0
+	move.d	REG_ADDR(gio, regi_gio, rw_pa_oe), $r1
+	move.d	$r0, [$r1]
+
+	;; Setup the stack to a suitably high address.
+	;; We assume 8 MB is the minimum DRAM and put
+	;; the SP at the top for now.
+
+	move.d	0x40800000, $sp
+
+	;; Figure out where the compressed piggyback image is
+	;; in the flash (since we wont try to copy it to DRAM
+	;; before unpacking). It is at _edata, but in flash.
+	;; Use (_edata - herami) as offset to the current PC.
+
+	move.d REG_ADDR(config, regi_config, r_bootsel), $r0
+	move.d [$r0], $r0
+	and.d  REG_MASK(config, r_bootsel, boot_mode), $r0
+	cmp.d  REG_STATE(config, r_bootsel, boot_mode, nand), $r0
+	beq hereami2
+	nop
+hereami:
+	lapcq	., $r5		; get PC
+	and.d	0x7fffffff, $r5	; strip any non-cache bit
+	move.d	$r5, $r0	; save for later - flash address of 'herami'
+	add.d	_edata, $r5
+	sub.d	hereami, $r5	; r5 = flash address of '_edata'
+	move.d	hereami, $r1	; destination
+	ba 2f
+	nop
+hereami2:
+	lapcq	., $r5		; get PC
+	and.d	0x00ffffff, $r5	; strip any non-cache bit
+	move.d  $r5, $r6
+	or.d    0x40200000, $r6
+	move.d	$r6, $r0	; save for later - flash address of 'herami'
+	add.d	_edata, $r5
+	sub.d	hereami2, $r5	; r5 = flash address of '_edata'
+	add.d   0x40200000, $r5
+	move.d	hereami2, $r1	; destination
+2:
+	;; Copy text+data to DRAM
+
+	move.d	_edata, $r2	; end destination
+1:	move.w	[$r0+], $r3
+	move.w	$r3, [$r1+]
+	cmp.d	$r2, $r1
+	bcs	1b
+	nop
+
+	move.d	input_data, $r0 ; for the decompressor
+	move.d	$r5, [$r0]	; for the decompressor
+
+	;; Clear the decompressors BSS (between _edata and _end)
+
+	moveq	0, $r0
+	move.d	_edata, $r1
+	move.d	_end, $r2
+1:	move.w	$r0, [$r1+]
+	cmp.d	$r2, $r1
+	bcs	1b
+	nop
+
+	;;  Save command line magic and address.
+	move.d	_cmd_line_magic, $r12
+	move.d  $r10, [$r12]
+	move.d	_cmd_line_addr, $r12
+	move.d  $r11, [$r12]
+
+	;; Do the decompression and save compressed size in _inptr
+
+	jsr	decompress_kernel
+	nop
+
+	;; Restore command line magic and address.
+	move.d  _cmd_line_magic, $r10
+	move.d  [$r10], $r10
+	move.d  _cmd_line_addr, $r11
+	move.d  [$r11], $r11
+
+	;; Put start address of root partition in r9 so the kernel can use it
+	;; when mounting from flash
+	move.d  input_data, $r0
+	move.d	[$r0], $r9		; flash address of compressed kernel
+	move.d  inptr, $r0
+	add.d	[$r0], $r9		; size of compressed kernel
+	cmp.d   0x40200000, $r9
+	blo	enter_kernel
+	nop
+	sub.d   0x40200000, $r9
+	add.d   0x4000, $r9
+
+enter_kernel:
+	;; Enter the decompressed kernel
+	move.d	RAM_INIT_MAGIC, $r8	; Tell kernel that DRAM is initialized
+	jump	0x40004000	; kernel is linked to this address
+	nop
+
+	.data
+
+input_data:
+	.dword	0		; used by the decompressor
+_cmd_line_magic:
+	.dword 0
+_cmd_line_addr:
+	.dword 0
+is_nand_boot:
+	.dword  0
+
+#include "../../lib/hw_settings.S"

+ 318 - 0
arch/cris/arch-v32/boot/compressed/misc.c

@@ -0,0 +1,318 @@
+/*
+ * misc.c
+ *
+ * $Id: misc.c,v 1.8 2005/04/24 18:34:29 starvik Exp $
+ *
+ * This is a collection of several routines from gzip-1.0.3
+ * adapted for Linux.
+ *
+ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
+ * puts by Nick Holloway 1993, better puts by Martin Mares 1995
+ * adoptation for Linux/CRIS Axis Communications AB, 1999
+ *
+ */
+
+/* where the piggybacked kernel image expects itself to live.
+ * it is the same address we use when we network load an uncompressed
+ * image into DRAM, and it is the address the kernel is linked to live
+ * at by vmlinux.lds.S
+ */
+
+#define KERNEL_LOAD_ADR 0x40004000
+
+#include <linux/config.h>
+
+#include <linux/types.h>
+#include <asm/arch/hwregs/reg_rdwr.h>
+#include <asm/arch/hwregs/reg_map.h>
+#include <asm/arch/hwregs/ser_defs.h>
+
+/*
+ * gzip declarations
+ */
+
+#define OF(args)  args
+#define STATIC static
+
+void* memset(void* s, int c, size_t n);
+void* memcpy(void* __dest, __const void* __src,
+	     size_t __n);
+
+#define memzero(s, n)     memset ((s), 0, (n))
+
+
+typedef unsigned char  uch;
+typedef unsigned short ush;
+typedef unsigned long  ulg;
+
+#define WSIZE 0x8000		/* Window size must be at least 32k, */
+				/* and a power of two */
+
+static uch *inbuf;	     /* input buffer */
+static uch window[WSIZE];    /* Sliding window buffer */
+
+unsigned inptr = 0;	/* index of next byte to be processed in inbuf
+			 * After decompression it will contain the
+			 * compressed size, and head.S will read it.
+			 */
+
+static unsigned outcnt = 0;  /* bytes in output buffer */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
+#define RESERVED     0xC0 /* bit 6,7:   reserved */
+
+#define get_byte() inbuf[inptr++]
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  define Assert(cond,msg) {if(!(cond)) error(msg);}
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x ;}
+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+static int  fill_inbuf(void);
+static void flush_window(void);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+extern char *input_data;  /* lives in head.S */
+
+static long bytes_out = 0;
+static uch *output_data;
+static unsigned long output_ptr = 0;
+
+static void *malloc(int size);
+static void free(void *where);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+static void puts(const char *);
+
+/* the "heap" is put directly after the BSS ends, at end */
+
+extern int _end;
+static long free_mem_ptr = (long)&_end;
+
+#include "../../../../../lib/inflate.c"
+
+static void *malloc(int size)
+{
+	void *p;
+
+	if (size <0) error("Malloc error");
+
+	free_mem_ptr = (free_mem_ptr + 3) & ~3;	/* Align */
+
+	p = (void *)free_mem_ptr;
+	free_mem_ptr += size;
+
+	return p;
+}
+
+static void free(void *where)
+{	/* Don't care */
+}
+
+static void gzip_mark(void **ptr)
+{
+	*ptr = (void *) free_mem_ptr;
+}
+
+static void gzip_release(void **ptr)
+{
+	free_mem_ptr = (long) *ptr;
+}
+
+/* decompressor info and error messages to serial console */
+
+static inline void
+serout(const char *s, reg_scope_instances regi_ser)
+{
+	reg_ser_rs_stat_din rs;
+	reg_ser_rw_dout dout = {.data = *s};
+
+	do {
+		rs = REG_RD(ser, regi_ser, rs_stat_din);
+	}
+	while (!rs.tr_rdy);/* Wait for tranceiver. */
+
+	REG_WR(ser, regi_ser, rw_dout, dout);
+}
+
+static void
+puts(const char *s)
+{
+#ifndef CONFIG_ETRAX_DEBUG_PORT_NULL
+	while (*s) {
+#ifdef CONFIG_ETRAX_DEBUG_PORT0
+		serout(s, regi_ser0);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT1
+		serout(s, regi_ser1);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT2
+		serout(s, regi_ser2);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT3
+		serout(s, regi_ser3);
+#endif
+		*s++;
+	}
+/* CONFIG_ETRAX_DEBUG_PORT_NULL */
+#endif
+}
+
+void*
+memset(void* s, int c, size_t n)
+{
+	int i;
+	char *ss = (char*)s;
+
+	for (i=0;i<n;i++) ss[i] = c;
+}
+
+void*
+memcpy(void* __dest, __const void* __src,
+			    size_t __n)
+{
+	int i;
+	char *d = (char *)__dest, *s = (char *)__src;
+
+	for (i=0;i<__n;i++) d[i] = s[i];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+
+static void
+flush_window()
+{
+    ulg c = crc;         /* temporary variable */
+    unsigned n;
+    uch *in, *out, ch;
+
+    in = window;
+    out = &output_data[output_ptr];
+    for (n = 0; n < outcnt; n++) {
+	    ch = *out++ = *in++;
+	    c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
+    }
+    crc = c;
+    bytes_out += (ulg)outcnt;
+    output_ptr += (ulg)outcnt;
+    outcnt = 0;
+}
+
+static void
+error(char *x)
+{
+	puts("\n\n");
+	puts(x);
+	puts("\n\n -- System halted\n");
+
+	while(1);	/* Halt */
+}
+
+void
+setup_normal_output_buffer()
+{
+	output_data = (char *)KERNEL_LOAD_ADR;
+}
+
+static inline void
+serial_setup(reg_scope_instances regi_ser)
+{
+	reg_ser_rw_xoff xoff;
+	reg_ser_rw_tr_ctrl tr_ctrl;
+	reg_ser_rw_rec_ctrl rec_ctrl;
+	reg_ser_rw_tr_baud_div tr_baud;
+	reg_ser_rw_rec_baud_div rec_baud;
+
+	/* Turn off XOFF. */
+	xoff = REG_RD(ser, regi_ser, rw_xoff);
+
+	xoff.chr = 0;
+	xoff.automatic = regk_ser_no;
+
+	REG_WR(ser, regi_ser, rw_xoff, xoff);
+
+	/* Set baudrate and stopbits. */
+	tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
+	rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+	tr_baud = REG_RD(ser, regi_ser, rw_tr_baud_div);
+	rec_baud = REG_RD(ser, regi_ser, rw_rec_baud_div);
+
+	tr_ctrl.stop_bits = 1;	/* 2 stop bits. */
+
+	/*
+	 * The baudrate setup is a bit fishy, but in the end the tranceiver is
+	 * set to 4800 and the receiver to 115200. The magic value is
+	 * 29.493 MHz.
+	 */
+	tr_ctrl.base_freq = regk_ser_f29_493;
+	rec_ctrl.base_freq = regk_ser_f29_493;
+	tr_baud.div = (29493000 / 8) / 4800;
+	rec_baud.div = (29493000 / 8) / 115200;
+
+	REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+	REG_WR(ser, regi_ser, rw_tr_baud_div, tr_baud);
+	REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+	REG_WR(ser, regi_ser, rw_rec_baud_div, rec_baud);
+}
+
+void
+decompress_kernel()
+{
+	char revision;
+
+	/* input_data is set in head.S */
+	inbuf = input_data;
+
+#ifdef CONFIG_ETRAX_DEBUG_PORT0
+	serial_setup(regi_ser0);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT1
+	serial_setup(regi_ser1);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT2
+	serial_setup(regi_ser2);
+#endif
+#ifdef CONFIG_ETRAX_DEBUG_PORT3
+	serial_setup(regi_ser3);
+#endif
+
+	setup_normal_output_buffer();
+
+	makecrc();
+
+	__asm__ volatile ("move $vr,%0" : "=rm" (revision));
+	if (revision < 32)
+	{
+		puts("You need an ETRAX FS to run Linux 2.6/crisv32.\n");
+		while(1);
+	}
+
+	puts("Uncompressing Linux...\n");
+	gunzip();
+	puts("Done. Now booting the kernel.\n");
+}

+ 36 - 0
arch/cris/arch-v32/boot/rescue/Makefile

@@ -0,0 +1,36 @@
+#
+# Makefile for rescue code
+#
+target = $(target_rescue_dir)
+src    = $(src_rescue_dir)
+
+CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE)
+CFLAGS = -O2
+LD = gcc-cris -mlinux -march=v32 -nostdlib
+OBJCOPY = objcopy-cris
+OBJCOPYFLAGS = -O binary --remove-section=.bss
+
+all: $(target)/rescue.bin
+
+rescue: rescue.bin
+	# do nothing
+
+$(target)/rescue.bin: $(target) $(target)/head.o
+	$(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o
+	$(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin
+	cp -p $(target)/rescue.bin $(objtree)
+
+$(target):
+	mkdir -p $(target)
+
+$(target)/head.o: $(src)/head.S
+	$(CC) -D__ASSEMBLY__ -c $< -o $*.o
+
+clean:
+	rm -f $(target)/*.o $(target)/*.bin
+
+fastdep:
+
+modules:
+
+modules-install:

+ 39 - 0
arch/cris/arch-v32/boot/rescue/head.S

@@ -0,0 +1,39 @@
+/* $Id: head.S,v 1.4 2004/11/01 16:10:28 starvik Exp $
+ *
+ * This used to be the rescue code but now that is handled by the
+ * RedBoot based RFL instead. Nothing to see here, move along.
+ */
+
+#include <linux/config.h>
+#include <asm/arch/hwregs/reg_map_asm.h>
+#include <asm/arch/hwregs/config_defs_asm.h>
+
+	.text
+
+	;; Start clocks for used blocks.
+	move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1
+	move.d [$r1], $r0
+	or.d   REG_STATE(config, rw_clk_ctrl, cpu, yes) | \
+	       REG_STATE(config, rw_clk_ctrl, bif, yes) | \
+	       REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0
+	move.d $r0, [$r1]
+
+	;; Copy 68KB NAND flash to Internal RAM (if NAND boot)
+	move.d 0x38004000, $r10
+	move.d 0x8000, $r11
+	move.d 0x11000, $r12
+	move.d copy_complete, $r13
+	and.d  0x000fffff, $r13
+	or.d   0x38000000, $r13
+
+#include "../../lib/nand_init.S"
+
+	;; No NAND found
+	move.d	CONFIG_ETRAX_PTABLE_SECTOR, $r10
+	jump	$r10 ; Jump to decompresser
+	nop
+
+copy_complete:
+	move.d	0x38000000 + CONFIG_ETRAX_PTABLE_SECTOR, $r10
+	jump	$r10 ; Jump to decompresser
+	nop

+ 20 - 0
arch/cris/arch-v32/boot/rescue/rescue.ld

@@ -0,0 +1,20 @@
+MEMORY
+	{
+	flash : ORIGIN = 0x00000000,
+	        LENGTH = 0x00100000
+	}
+
+SECTIONS
+{
+	.text :
+	{
+		stext = . ;
+		*(.text)
+		etext = . ;
+	} > flash
+	.data :
+	{
+		*(.data)
+		edata = . ;
+	} > flash
+}

+ 625 - 0
arch/cris/arch-v32/drivers/Kconfig

@@ -0,0 +1,625 @@
+config ETRAX_ETHERNET
+	bool "Ethernet support"
+	depends on ETRAX_ARCH_V32
+	select NET_ETHERNET
+	help
+	  This option enables the ETRAX FS built-in 10/100Mbit Ethernet
+	  controller.
+
+config ETRAX_ETHERNET_HW_CSUM
+	bool "Hardware accelerated ethernet checksum and scatter/gather"
+	depends on ETRAX_ETHERNET
+	depends on ETRAX_STREAMCOPROC
+	default y
+	help
+	  Hardware acceleration of checksumming and scatter/gather
+
+config ETRAX_ETHERNET_IFACE0
+	depends on ETRAX_ETHERNET
+	bool "Enable network interface 0"
+
+config ETRAX_ETHERNET_IFACE1
+	depends on ETRAX_ETHERNET
+	bool "Enable network interface 1 (uses DMA6 and DMA7)"
+
+choice
+	prompt "Network LED behavior"
+	depends on ETRAX_ETHERNET
+	default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
+
+config ETRAX_NETWORK_LED_ON_WHEN_LINK
+	bool "LED_on_when_link"
+	help
+	  Selecting LED_on_when_link will light the LED when there is a
+	  connection and will flash off when there is activity.
+
+	  Selecting LED_on_when_activity will light the LED only when
+	  there is activity.
+
+	  This setting will also affect the behaviour of other activity LEDs
+	  e.g. Bluetooth.
+
+config ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
+	bool "LED_on_when_activity"
+	help
+	  Selecting LED_on_when_link will light the LED when there is a
+	  connection and will flash off when there is activity.
+
+	  Selecting LED_on_when_activity will light the LED only when
+	  there is activity.
+
+	  This setting will also affect the behaviour of other activity LEDs
+	  e.g. Bluetooth.
+
+endchoice
+
+config ETRAXFS_SERIAL
+	bool "Serial-port support"
+	depends on ETRAX_ARCH_V32
+	help
+	  Enables the ETRAX FS serial driver for ser0 (ttyS0)
+	  You probably want this enabled.
+
+config ETRAX_SERIAL_PORT0
+	bool "Serial port 0 enabled"
+	depends on ETRAXFS_SERIAL
+	help
+	  Enables the ETRAX FS serial driver for ser0 (ttyS0)
+	  Normally you want this on. You can control what DMA channels to use
+	  if you do not need DMA to something else.
+	  ser0 can use dma4 or dma6 for output and dma5 or dma7 for input.
+
+choice
+	prompt "Ser0 DMA in channel "
+	depends on ETRAX_SERIAL_PORT0
+	default ETRAX_SERIAL_PORT0_NO_DMA_IN
+	help
+	  What DMA channel to use for ser0.
+
+
+config ETRAX_SERIAL_PORT0_NO_DMA_IN
+	bool "Ser0 uses no DMA for input"
+	help
+	  Do not use DMA for ser0 input.
+
+config ETRAX_SERIAL_PORT0_DMA7_IN
+	bool "Ser0 uses DMA7 for input"
+	depends on ETRAX_SERIAL_PORT0
+	help
+	  Enables the DMA7 input channel for ser0 (ttyS0).
+	  If you do not enable DMA, an interrupt for each character will be
+	  used when receiveing data.
+	  Normally you want to use DMA, unless you use the DMA channel for
+	  something else.
+
+endchoice
+
+choice
+	prompt "Ser0 DMA out channel"
+	depends on ETRAX_SERIAL_PORT0
+	default ETRAX_SERIAL_PORT0_NO_DMA_OUT
+
+config ETRAX_SERIAL_PORT0_NO_DMA_OUT
+	bool "Ser0 uses no DMA for output"
+	help
+	  Do not use DMA for ser0 output.
+
+config ETRAX_SERIAL_PORT0_DMA6_OUT
+	bool "Ser0 uses DMA6 for output"
+	depends on ETRAX_SERIAL_PORT0
+	help
+	  Enables the DMA6 output channel for ser0 (ttyS0).
+	  If you do not enable DMA, an interrupt for each character will be
+	  used when transmitting data.
+	  Normally you want to use DMA, unless you use the DMA channel for
+	  something else.
+
+endchoice
+
+config ETRAX_SER0_DTR_BIT
+	string "Ser 0 DTR bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT0
+
+config ETRAX_SER0_RI_BIT
+	string "Ser 0 RI bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT0
+
+config ETRAX_SER0_DSR_BIT
+	string "Ser 0 DSR bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT0
+
+config ETRAX_SER0_CD_BIT
+	string "Ser 0 CD bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT0
+
+config ETRAX_SERIAL_PORT1
+	bool "Serial port 1 enabled"
+	depends on ETRAXFS_SERIAL
+	help
+	  Enables the ETRAX FS serial driver for ser1 (ttyS1).
+
+choice
+	prompt "Ser1 DMA in channel "
+	depends on ETRAX_SERIAL_PORT1
+	default ETRAX_SERIAL_PORT1_NO_DMA_IN
+	help
+	  What DMA channel to use for ser1.
+
+
+config ETRAX_SERIAL_PORT1_NO_DMA_IN
+	bool "Ser1 uses no DMA for input"
+	help
+	  Do not use DMA for ser1 input.
+
+config ETRAX_SERIAL_PORT1_DMA5_IN
+	bool "Ser1 uses DMA5 for input"
+	depends on ETRAX_SERIAL_PORT1
+	help
+	  Enables the DMA5 input channel for ser1 (ttyS1).
+	  If you do not enable DMA, an interrupt for each character will be
+	  used when receiveing data.
+	  Normally you want this on, unless you use the DMA channel for
+	  something else.
+
+endchoice
+
+choice
+	prompt "Ser1 DMA out channel "
+	depends on ETRAX_SERIAL_PORT1
+	default ETRAX_SERIAL_PORT1_NO_DMA_OUT
+	help
+	  What DMA channel to use for ser1.
+
+config ETRAX_SERIAL_PORT1_NO_DMA_OUT
+	bool "Ser1 uses no DMA for output"
+	help
+	  Do not use DMA for ser1 output.
+
+config ETRAX_SERIAL_PORT1_DMA4_OUT
+	bool "Ser1 uses DMA4 for output"
+	depends on ETRAX_SERIAL_PORT1
+	help
+	  Enables the DMA4 output channel for ser1 (ttyS1).
+	  If you do not enable DMA, an interrupt for each character will be
+	  used when transmitting data.
+	  Normally you want this on, unless you use the DMA channel for
+	  something else.
+
+endchoice
+
+config ETRAX_SER1_DTR_BIT
+	string "Ser 1 DTR bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT1
+
+config ETRAX_SER1_RI_BIT
+	string "Ser 1 RI bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT1
+
+config ETRAX_SER1_DSR_BIT
+	string "Ser 1 DSR bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT1
+
+config ETRAX_SER1_CD_BIT
+	string "Ser 1 CD bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT1
+
+config ETRAX_SERIAL_PORT2
+	bool "Serial port 2 enabled"
+	depends on ETRAXFS_SERIAL
+	help
+	  Enables the ETRAX FS serial driver for ser2 (ttyS2).
+
+choice
+	prompt "Ser2 DMA in channel "
+	depends on ETRAX_SERIAL_PORT2
+	default ETRAX_SERIAL_PORT2_NO_DMA_IN
+	help
+	  What DMA channel to use for ser2.
+
+
+config ETRAX_SERIAL_PORT2_NO_DMA_IN
+	bool "Ser2 uses no DMA for input"
+	help
+	  Do not use DMA for ser2 input.
+
+config ETRAX_SERIAL_PORT2_DMA3_IN
+	bool "Ser2 uses DMA3 for input"
+	depends on ETRAX_SERIAL_PORT2
+	help
+	  Enables the DMA3 input channel for ser2 (ttyS2).
+	  If you do not enable DMA, an interrupt for each character will be
+	  used when receiveing data.
+	  Normally you want to use DMA, unless you use the DMA channel for
+	  something else.
+
+endchoice
+
+choice
+	prompt "Ser2 DMA out channel"
+	depends on ETRAX_SERIAL_PORT2
+	default ETRAX_SERIAL_PORT2_NO_DMA_OUT
+
+config ETRAX_SERIAL_PORT2_NO_DMA_OUT
+	bool "Ser2 uses no DMA for output"
+	help
+	  Do not use DMA for ser2 output.
+
+config ETRAX_SERIAL_PORT2_DMA2_OUT
+	bool "Ser2 uses DMA2 for output"
+	depends on ETRAX_SERIAL_PORT2
+	help
+	  Enables the DMA2 output channel for ser2 (ttyS2).
+	  If you do not enable DMA, an interrupt for each character will be
+	  used when transmitting data.
+	  Normally you want to use DMA, unless you use the DMA channel for
+	  something else.
+
+endchoice
+
+config ETRAX_SER2_DTR_BIT
+	string "Ser 2 DTR bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT2
+
+config ETRAX_SER2_RI_BIT
+	string "Ser 2 RI bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT2
+
+config ETRAX_SER2_DSR_BIT
+	string "Ser 2 DSR bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT2
+
+config ETRAX_SER2_CD_BIT
+	string "Ser 2 CD bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT2
+
+config ETRAX_SERIAL_PORT3
+	bool "Serial port 3 enabled"
+	depends on ETRAXFS_SERIAL
+	help
+	  Enables the ETRAX FS serial driver for ser3 (ttyS3).
+
+choice
+	prompt "Ser3 DMA in channel "
+	depends on ETRAX_SERIAL_PORT3
+	default ETRAX_SERIAL_PORT3_NO_DMA_IN
+	help
+	  What DMA channel to use for ser3.
+
+
+config ETRAX_SERIAL_PORT3_NO_DMA_IN
+	bool "Ser3 uses no DMA for input"
+	help
+	  Do not use DMA for ser3 input.
+
+config ETRAX_SERIAL_PORT3_DMA9_IN
+	bool "Ser3 uses DMA9 for input"
+	depends on ETRAX_SERIAL_PORT3
+	help
+	  Enables the DMA9 input channel for ser3 (ttyS3).
+	  If you do not enable DMA, an interrupt for each character will be
+	  used when receiveing data.
+	  Normally you want to use DMA, unless you use the DMA channel for
+	  something else.
+
+endchoice
+
+choice
+	prompt "Ser3 DMA out channel"
+	depends on ETRAX_SERIAL_PORT3
+	default ETRAX_SERIAL_PORT3_NO_DMA_OUT
+
+config ETRAX_SERIAL_PORT3_NO_DMA_OUT
+	bool "Ser3 uses no DMA for output"
+	help
+	  Do not use DMA for ser3 output.
+
+config ETRAX_SERIAL_PORT3_DMA8_OUT
+	bool "Ser3 uses DMA8 for output"
+	depends on ETRAX_SERIAL_PORT3
+	help
+	  Enables the DMA8 output channel for ser3 (ttyS3).
+	  If you do not enable DMA, an interrupt for each character will be
+	  used when transmitting data.
+	  Normally you want to use DMA, unless you use the DMA channel for
+	  something else.
+
+endchoice
+
+config ETRAX_SER3_DTR_BIT
+	string "Ser 3 DTR bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT3
+
+config ETRAX_SER3_RI_BIT
+	string "Ser 3 RI bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT3
+
+config ETRAX_SER3_DSR_BIT
+	string "Ser 3 DSR bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT3
+
+config ETRAX_SER3_CD_BIT
+	string "Ser 3 CD bit (empty = not used)"
+	depends on ETRAX_SERIAL_PORT3
+
+config ETRAX_RS485
+	bool "RS-485 support"
+	depends on ETRAX_SERIAL
+	help
+	  Enables support for RS-485 serial communication.  For a primer on
+	  RS-485, see <http://www.hw.cz/english/docs/rs485/rs485.html>.
+
+config ETRAX_RS485_DISABLE_RECEIVER
+	bool "Disable serial receiver"
+	depends on ETRAX_RS485
+	help
+	  It is necessary to disable the serial receiver to avoid serial
+	  loopback.  Not all products are able to do this in software only.
+	  Axis 2400/2401 must disable receiver.
+
+config ETRAX_AXISFLASHMAP
+	bool "Axis flash-map support"
+	depends on ETRAX_ARCH_V32
+	select MTD
+	select MTD_CFI
+	select MTD_CFI_AMDSTD
+	select MTD_OBSOLETE_CHIPS
+	select MTD_AMDSTD
+	select MTD_CHAR
+	select MTD_BLOCK
+	select MTD_PARTITIONS
+	select MTD_CONCAT
+	select MTD_COMPLEX_MAPPINGS
+	help
+	  This option enables MTD mapping of flash devices.  Needed to use
+	  flash memories.  If unsure, say Y.
+
+config ETRAX_SYNCHRONOUS_SERIAL
+	bool "Synchronous serial-port support"
+	depends on ETRAX_ARCH_V32
+	help
+	  Enables the ETRAX FS synchronous serial driver.
+
+config ETRAX_SYNCHRONOUS_SERIAL_PORT0
+         bool "Synchronous serial port 0 enabled"
+         depends on ETRAX_SYNCHRONOUS_SERIAL
+         help
+           Enabled synchronous serial port 0.
+
+config ETRAX_SYNCHRONOUS_SERIAL0_DMA
+         bool "Enable DMA on synchronous serial port 0."
+         depends on ETRAX_SYNCHRONOUS_SERIAL_PORT0
+         help
+           A synchronous serial port can run in manual or DMA mode.
+           Selecting this option will make it run in DMA mode.
+
+config ETRAX_SYNCHRONOUS_SERIAL_PORT1
+         bool "Synchronous serial port 1 enabled"
+         depends on ETRAX_SYNCHRONOUS_SERIAL
+         help
+           Enabled synchronous serial port 1.
+
+config ETRAX_SYNCHRONOUS_SERIAL1_DMA
+         bool "Enable DMA on synchronous serial port 1."
+         depends on ETRAX_SYNCHRONOUS_SERIAL_PORT1
+         help
+           A synchronous serial port can run in manual or DMA mode.
+           Selecting this option will make it run in DMA mode.
+
+config ETRAX_PTABLE_SECTOR
+	int "Byte-offset of partition table sector"
+	depends on ETRAX_AXISFLASHMAP
+	default "65536"
+	help
+	  Byte-offset of the partition table in the first flash chip.
+	  The default value is 64kB and should not be changed unless
+	  you know exactly what you are doing. The only valid reason
+	  for changing this is when the flash block size is bigger
+	  than 64kB (e.g. when using two parallel 16 bit flashes).
+
+config ETRAX_NANDFLASH
+	bool "NAND flash support"
+	depends on ETRAX_ARCH_V32
+	select MTD_NAND
+	select MTD_NAND_IDS
+	help
+	  This option enables MTD mapping of NAND flash devices.  Needed to use
+	  NAND flash memories.  If unsure, say Y.
+
+config ETRAX_I2C
+	bool "I2C driver"
+	depends on ETRAX_ARCH_V32
+	help
+	  This option enabled the I2C driver used by e.g. the RTC driver.
+
+config ETRAX_I2C_DATA_PORT
+	string "I2C data pin"
+	depends on ETRAX_I2C
+	help
+	  The pin to use for I2C data.
+
+config ETRAX_I2C_CLK_PORT
+	string "I2C clock pin"
+	depends on ETRAX_I2C
+	help
+	  The pin to use for I2C clock.
+
+config ETRAX_RTC
+	bool "Real Time Clock support"
+	depends on ETRAX_ARCH_V32
+	help
+	  Enabled RTC support.
+
+choice
+	prompt "RTC chip"
+	depends on ETRAX_RTC
+	default ETRAX_PCF8563
+
+config ETRAX_PCF8563
+	bool "PCF8563"
+	help
+	  Philips PCF8563 RTC
+
+endchoice
+
+config ETRAX_GPIO
+	bool "GPIO support"
+	depends on ETRAX_ARCH_V32
+	---help---
+	  Enables the ETRAX general port device (major 120, minors 0-4).
+	  You can use this driver to access the general port bits. It supports
+	  these ioctl's:
+	  #include <linux/etraxgpio.h>
+	  fd = open("/dev/gpioa", O_RDWR); // or /dev/gpiob
+	  ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_SETBITS), bits_to_set);
+	  ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_CLRBITS), bits_to_clear);
+	  err = ioctl(fd, _IO(ETRAXGPIO_IOCTYPE, IO_READ_INBITS), &val);
+	  Remember that you need to setup the port directions appropriately in
+	  the General configuration.
+
+config ETRAX_PA_BUTTON_BITMASK
+	hex "PA-buttons bitmask"
+	depends on ETRAX_GPIO
+	default "0x02"
+	help
+	  This is a bitmask (8 bits) with information about what bits on PA
+	  that are used for buttons.
+	  Most products has a so called TEST button on PA1, if that is true
+	  use 0x02 here.
+	  Use 00 if there are no buttons on PA.
+	  If the bitmask is <> 00 a button driver will be included in the gpio
+	  driver. ETRAX general I/O support must be enabled.
+
+config ETRAX_PA_CHANGEABLE_DIR
+	hex "PA user changeable dir mask"
+	depends on ETRAX_GPIO
+	default "0x00"
+	help
+	  This is a bitmask (8 bits) with information of what bits in PA that a
+	  user can change direction on using ioctl's.
+	  Bit set = changeable.
+	  You probably want 0x00 here, but it depends on your hardware.
+
+config ETRAX_PA_CHANGEABLE_BITS
+	hex "PA user changeable bits mask"
+	depends on ETRAX_GPIO
+	default "0x00"
+	help
+	  This is a bitmask (8 bits) with information of what bits in PA
+	  that a user can change the value on using ioctl's.
+	  Bit set = changeable.
+
+config ETRAX_PB_CHANGEABLE_DIR
+	hex "PB user changeable dir mask"
+	depends on ETRAX_GPIO
+	default "0x00000"
+	help
+	  This is a bitmask (18 bits) with information of what bits in PB
+	  that a user can change direction on using ioctl's.
+	  Bit set = changeable.
+	  You probably want 0x00000 here, but it depends on your hardware.
+
+config ETRAX_PB_CHANGEABLE_BITS
+	hex "PB user changeable bits mask"
+	depends on ETRAX_GPIO
+	default "0x00000"
+	help
+	  This is a bitmask (18 bits) with information of what bits in PB
+	  that a user can change the value on using ioctl's.
+	  Bit set = changeable.
+
+config ETRAX_PC_CHANGEABLE_DIR
+	hex "PC user changeable dir mask"
+	depends on ETRAX_GPIO
+	default "0x00000"
+	help
+	  This is a bitmask (18 bits) with information of what bits in PC
+	  that a user can change direction on using ioctl's.
+	  Bit set = changeable.
+	  You probably want 0x00000 here, but it depends on your hardware.
+
+config ETRAX_PC_CHANGEABLE_BITS
+	hex "PC user changeable bits mask"
+	depends on ETRAX_GPIO
+	default "0x00000"
+	help
+	  This is a bitmask (18 bits) with information of what bits in PC
+	  that a user can change the value on using ioctl's.
+	  Bit set = changeable.
+
+config ETRAX_PD_CHANGEABLE_DIR
+	hex "PD user changeable dir mask"
+	depends on ETRAX_GPIO
+	default "0x00000"
+	help
+	  This is a bitmask (18 bits) with information of what bits in PD
+	  that a user can change direction on using ioctl's.
+	  Bit set = changeable.
+	  You probably want 0x00000 here, but it depends on your hardware.
+
+config ETRAX_PD_CHANGEABLE_BITS
+	hex "PD user changeable bits mask"
+	depends on ETRAX_GPIO
+	default "0x00000"
+	help
+	  This is a bitmask (18 bits) with information of what bits in PD
+	  that a user can change the value on using ioctl's.
+	  Bit set = changeable.
+
+config ETRAX_PE_CHANGEABLE_DIR
+	hex "PE user changeable dir mask"
+	depends on ETRAX_GPIO
+	default "0x00000"
+	help
+	  This is a bitmask (18 bits) with information of what bits in PE
+	  that a user can change direction on using ioctl's.
+	  Bit set = changeable.
+	  You probably want 0x00000 here, but it depends on your hardware.
+
+config ETRAX_PE_CHANGEABLE_BITS
+	hex "PE user changeable bits mask"
+	depends on ETRAX_GPIO
+	default "0x00000"
+	help
+	  This is a bitmask (18 bits) with information of what bits in PE
+	  that a user can change the value on using ioctl's.
+	  Bit set = changeable.
+
+config ETRAX_IDE
+	bool "ATA/IDE support"
+	depends on ETRAX_ARCH_V32
+	select IDE
+	select BLK_DEV_IDE
+	select BLK_DEV_IDEDISK
+	select BLK_DEV_IDECD
+	select BLK_DEV_IDEDMA
+	help
+	  Enables the ETRAX IDE driver.
+
+config ETRAX_CARDBUS
+        bool "Cardbus support"
+        depends on ETRAX_ARCH_V32
+        select PCCARD
+        select CARDBUS
+        select HOTPLUG
+        select PCCARD_NONSTATIC
+        help
+	 Enabled the ETRAX Carbus driver.
+
+config PCI
+       bool
+       depends on ETRAX_CARDBUS
+       default y
+
+config ETRAX_IOP_FW_LOAD
+	tristate "IO-processor hotplug firmware loading support"
+	depends on ETRAX_ARCH_V32
+	select FW_LOADER
+	help
+	  Enables IO-processor hotplug firmware loading support.
+
+config ETRAX_STREAMCOPROC
+	tristate "Stream co-processor driver enabled"
+	depends on ETRAX_ARCH_V32
+	help
+	  This option enables a driver for the stream co-processor
+	  for cryptographic operations.

+ 13 - 0
arch/cris/arch-v32/drivers/Makefile

@@ -0,0 +1,13 @@
+#
+# Makefile for Etrax-specific drivers
+#
+
+obj-$(CONFIG_ETRAX_STREAMCOPROC)        += cryptocop.o
+obj-$(CONFIG_ETRAX_AXISFLASHMAP)        += axisflashmap.o
+obj-$(CONFIG_ETRAX_NANDFLASH)           += nandflash.o
+obj-$(CONFIG_ETRAX_GPIO) 	        += gpio.o
+obj-$(CONFIG_ETRAX_IOP_FW_LOAD)         += iop_fw_load.o
+obj-$(CONFIG_ETRAX_PCF8563)		+= pcf8563.o
+obj-$(CONFIG_ETRAX_I2C)			+= i2c.o
+obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL)	+= sync_serial.o
+obj-$(CONFIG_PCI)			+= pci/

+ 455 - 0
arch/cris/arch-v32/drivers/axisflashmap.c

@@ -0,0 +1,455 @@
+/*
+ * Physical mapping layer for MTD using the Axis partitiontable format
+ *
+ * Copyright (c) 2001, 2002, 2003 Axis Communications AB
+ *
+ * This file is under the GPL.
+ *
+ * First partition is always sector 0 regardless of if we find a partitiontable
+ * or not. In the start of the next sector, there can be a partitiontable that
+ * tells us what other partitions to define. If there isn't, we use a default
+ * partition split defined below.
+ *
+ * Copy of os/lx25/arch/cris/arch-v10/drivers/axisflashmap.c 1.5
+ * with minor changes.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <linux/mtd/concat.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/mtdram.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/arch/hwregs/config_defs.h>
+#include <asm/axisflashmap.h>
+#include <asm/mmu.h>
+
+#define MEM_CSE0_SIZE (0x04000000)
+#define MEM_CSE1_SIZE (0x04000000)
+
+#define FLASH_UNCACHED_ADDR  KSEG_E
+#define FLASH_CACHED_ADDR    KSEG_F
+
+#if CONFIG_ETRAX_FLASH_BUSWIDTH==1
+#define flash_data __u8
+#elif CONFIG_ETRAX_FLASH_BUSWIDTH==2
+#define flash_data __u16
+#elif CONFIG_ETRAX_FLASH_BUSWIDTH==4
+#define flash_data __u16
+#endif
+
+/* From head.S */
+extern unsigned long romfs_start, romfs_length, romfs_in_flash;
+
+/* The master mtd for the entire flash. */
+struct mtd_info* axisflash_mtd = NULL;
+
+/* Map driver functions. */
+
+static map_word flash_read(struct map_info *map, unsigned long ofs)
+{
+	map_word tmp;
+	tmp.x[0] = *(flash_data *)(map->map_priv_1 + ofs);
+	return tmp;
+}
+
+static void flash_copy_from(struct map_info *map, void *to,
+			    unsigned long from, ssize_t len)
+{
+	memcpy(to, (void *)(map->map_priv_1 + from), len);
+}
+
+static void flash_write(struct map_info *map, map_word d, unsigned long adr)
+{
+	*(flash_data *)(map->map_priv_1 + adr) = (flash_data)d.x[0];
+}
+
+/*
+ * The map for chip select e0.
+ *
+ * We run into tricky coherence situations if we mix cached with uncached
+ * accesses to we only use the uncached version here.
+ *
+ * The size field is the total size where the flash chips may be mapped on the
+ * chip select. MTD probes should find all devices there and it does not matter
+ * if there are unmapped gaps or aliases (mirrors of flash devices). The MTD
+ * probes will ignore them.
+ *
+ * The start address in map_priv_1 is in virtual memory so we cannot use
+ * MEM_CSE0_START but must rely on that FLASH_UNCACHED_ADDR is the start
+ * address of cse0.
+ */
+static struct map_info map_cse0 = {
+	.name = "cse0",
+	.size = MEM_CSE0_SIZE,
+	.bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH,
+	.read = flash_read,
+	.copy_from = flash_copy_from,
+	.write = flash_write,
+	.map_priv_1 = FLASH_UNCACHED_ADDR
+};
+
+/*
+ * The map for chip select e1.
+ *
+ * If there was a gap between cse0 and cse1, map_priv_1 would get the wrong
+ * address, but there isn't.
+ */
+static struct map_info map_cse1 = {
+	.name = "cse1",
+	.size = MEM_CSE1_SIZE,
+	.bankwidth = CONFIG_ETRAX_FLASH_BUSWIDTH,
+	.read = flash_read,
+	.copy_from = flash_copy_from,
+	.write = flash_write,
+	.map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE
+};
+
+/* If no partition-table was found, we use this default-set. */
+#define MAX_PARTITIONS         7
+#define NUM_DEFAULT_PARTITIONS 3
+
+/*
+ * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the
+ * size of one flash block and "filesystem"-partition needs 5 blocks to be able
+ * to use JFFS.
+ */
+static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = {
+	{
+		.name = "boot firmware",
+		.size = CONFIG_ETRAX_PTABLE_SECTOR,
+		.offset = 0
+	},
+	{
+		.name = "kernel",
+		.size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR),
+		.offset = CONFIG_ETRAX_PTABLE_SECTOR
+	},
+	{
+		.name = "filesystem",
+		.size = 5 * CONFIG_ETRAX_PTABLE_SECTOR,
+		.offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR)
+	}
+};
+
+/* Initialize the ones normally used. */
+static struct mtd_partition axis_partitions[MAX_PARTITIONS] = {
+	{
+		.name = "part0",
+		.size = CONFIG_ETRAX_PTABLE_SECTOR,
+		.offset = 0
+	},
+	{
+		.name = "part1",
+		.size = 0,
+		.offset = 0
+	},
+	{
+		.name = "part2",
+		.size = 0,
+		.offset = 0
+	},
+	{
+		.name = "part3",
+		.size = 0,
+		.offset = 0
+	},
+	{
+		.name = "part4",
+		.size = 0,
+		.offset = 0
+	},
+	{
+		.name = "part5",
+		.size = 0,
+		.offset = 0
+	},
+	{
+		.name = "part6",
+		.size = 0,
+		.offset = 0
+	},
+};
+
+/*
+ * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash
+ * chips in that order (because the amd_flash-driver is faster).
+ */
+static struct mtd_info *probe_cs(struct map_info *map_cs)
+{
+	struct mtd_info *mtd_cs = NULL;
+
+	printk(KERN_INFO
+	       "%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n",
+	       map_cs->name, map_cs->size, map_cs->map_priv_1);
+
+#ifdef CONFIG_MTD_AMDSTD
+	mtd_cs = do_map_probe("amd_flash", map_cs);
+#endif
+#ifdef CONFIG_MTD_CFI
+	if (!mtd_cs) {
+		mtd_cs = do_map_probe("cfi_probe", map_cs);
+	}
+#endif
+
+	return mtd_cs;
+}
+
+/*
+ * Probe each chip select individually for flash chips. If there are chips on
+ * both cse0 and cse1, the mtd_info structs will be concatenated to one struct
+ * so that MTD partitions can cross chip boundries.
+ *
+ * The only known restriction to how you can mount your chips is that each
+ * chip select must hold similar flash chips. But you need external hardware
+ * to do that anyway and you can put totally different chips on cse0 and cse1
+ * so it isn't really much of a restriction.
+ */
+extern struct mtd_info* __init crisv32_nand_flash_probe (void);
+static struct mtd_info *flash_probe(void)
+{
+	struct mtd_info *mtd_cse0;
+	struct mtd_info *mtd_cse1;
+	struct mtd_info *mtd_nand = NULL;
+	struct mtd_info *mtd_total;
+	struct mtd_info *mtds[3];
+	int count = 0;
+
+	if ((mtd_cse0 = probe_cs(&map_cse0)) != NULL)
+		mtds[count++] = mtd_cse0;
+	if ((mtd_cse1 = probe_cs(&map_cse1)) != NULL)
+		mtds[count++] = mtd_cse1;
+
+#ifdef CONFIG_ETRAX_NANDFLASH
+	if ((mtd_nand = crisv32_nand_flash_probe()) != NULL)
+		mtds[count++] = mtd_nand;
+#endif
+
+	if (!mtd_cse0 && !mtd_cse1 && !mtd_nand) {
+		/* No chip found. */
+		return NULL;
+	}
+
+	if (count > 1) {
+#ifdef CONFIG_MTD_CONCAT
+		/* Since the concatenation layer adds a small overhead we
+		 * could try to figure out if the chips in cse0 and cse1 are
+		 * identical and reprobe the whole cse0+cse1 window. But since
+		 * flash chips are slow, the overhead is relatively small.
+		 * So we use the MTD concatenation layer instead of further
+		 * complicating the probing procedure.
+		 */
+		mtd_total = mtd_concat_create(mtds,
+		                              count,
+		                              "cse0+cse1+nand");
+#else
+		printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel "
+		       "(mis)configuration!\n", map_cse0.name, map_cse1.name);
+		mtd_toal = NULL;
+#endif
+		if (!mtd_total) {
+			printk(KERN_ERR "%s and %s: Concatenation failed!\n",
+			       map_cse0.name, map_cse1.name);
+
+			/* The best we can do now is to only use what we found
+			 * at cse0.
+			 */
+			mtd_total = mtd_cse0;
+			map_destroy(mtd_cse1);
+		}
+	} else {
+		mtd_total = mtd_cse0? mtd_cse0 : mtd_cse1 ? mtd_cse1 : mtd_nand;
+
+	}
+
+	return mtd_total;
+}
+
+extern unsigned long crisv32_nand_boot;
+extern unsigned long crisv32_nand_cramfs_offset;
+
+/*
+ * Probe the flash chip(s) and, if it succeeds, read the partition-table
+ * and register the partitions with MTD.
+ */
+static int __init init_axis_flash(void)
+{
+	struct mtd_info *mymtd;
+	int err = 0;
+	int pidx = 0;
+	struct partitiontable_head *ptable_head = NULL;
+	struct partitiontable_entry *ptable;
+	int use_default_ptable = 1; /* Until proven otherwise. */
+	const char *pmsg = KERN_INFO "  /dev/flash%d at 0x%08x, size 0x%08x\n";
+	static char page[512];
+	size_t len;
+
+#ifndef CONFIG_ETRAXFS_SIM
+	mymtd = flash_probe();
+	mymtd->read(mymtd, CONFIG_ETRAX_PTABLE_SECTOR, 512, &len, page);
+	ptable_head = (struct partitiontable_head *)(page + PARTITION_TABLE_OFFSET);
+
+	if (!mymtd) {
+		/* There's no reason to use this module if no flash chip can
+		 * be identified. Make sure that's understood.
+		 */
+		printk(KERN_INFO "axisflashmap: Found no flash chip.\n");
+	} else {
+		printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n",
+		       mymtd->name, mymtd->size);
+		axisflash_mtd = mymtd;
+	}
+
+	if (mymtd) {
+		mymtd->owner = THIS_MODULE;
+	}
+	pidx++;  /* First partition is always set to the default. */
+
+	if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC)
+	    && (ptable_head->size <
+		(MAX_PARTITIONS * sizeof(struct partitiontable_entry) +
+		PARTITIONTABLE_END_MARKER_SIZE))
+	    && (*(unsigned long*)((void*)ptable_head + sizeof(*ptable_head) +
+				  ptable_head->size -
+				  PARTITIONTABLE_END_MARKER_SIZE)
+		== PARTITIONTABLE_END_MARKER)) {
+		/* Looks like a start, sane length and end of a
+		 * partition table, lets check csum etc.
+		 */
+		int ptable_ok = 0;
+		struct partitiontable_entry *max_addr =
+			(struct partitiontable_entry *)
+			((unsigned long)ptable_head + sizeof(*ptable_head) +
+			 ptable_head->size);
+		unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR;
+		unsigned char *p;
+		unsigned long csum = 0;
+
+		ptable = (struct partitiontable_entry *)
+			((unsigned long)ptable_head + sizeof(*ptable_head));
+
+		/* Lets be PARANOID, and check the checksum. */
+		p = (unsigned char*) ptable;
+
+		while (p <= (unsigned char*)max_addr) {
+			csum += *p++;
+			csum += *p++;
+			csum += *p++;
+			csum += *p++;
+		}
+		ptable_ok = (csum == ptable_head->checksum);
+
+		/* Read the entries and use/show the info.  */
+		printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n",
+		       (ptable_ok ? " valid" : "n invalid"), ptable_head,
+		       max_addr);
+
+		/* We have found a working bootblock.  Now read the
+		 * partition table.  Scan the table.  It ends when
+		 * there is 0xffffffff, that is, empty flash.
+		 */
+		while (ptable_ok
+		       && ptable->offset != 0xffffffff
+		       && ptable < max_addr
+		       && pidx < MAX_PARTITIONS) {
+
+			axis_partitions[pidx].offset = offset + ptable->offset + (crisv32_nand_boot ? 16384 : 0);
+			axis_partitions[pidx].size = ptable->size;
+
+			printk(pmsg, pidx, axis_partitions[pidx].offset,
+			       axis_partitions[pidx].size);
+			pidx++;
+			ptable++;
+		}
+		use_default_ptable = !ptable_ok;
+	}
+
+	if (romfs_in_flash) {
+		/* Add an overlapping device for the root partition (romfs). */
+
+		axis_partitions[pidx].name = "romfs";
+		if (crisv32_nand_boot) {
+			char* data = kmalloc(1024, GFP_KERNEL);
+			int len;
+			int offset = crisv32_nand_cramfs_offset & ~(1024-1);
+			char* tmp;
+
+			mymtd->read(mymtd, offset, 1024, &len, data);
+			tmp = &data[crisv32_nand_cramfs_offset % 512];
+			axis_partitions[pidx].size = *(unsigned*)(tmp + 4);
+			axis_partitions[pidx].offset = crisv32_nand_cramfs_offset;
+			kfree(data);
+		} else {
+			axis_partitions[pidx].size = romfs_length;
+			axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR;
+		}
+
+		axis_partitions[pidx].mask_flags |= MTD_WRITEABLE;
+
+		printk(KERN_INFO
+                       " Adding readonly flash partition for romfs image:\n");
+		printk(pmsg, pidx, axis_partitions[pidx].offset,
+		       axis_partitions[pidx].size);
+		pidx++;
+	}
+
+        if (mymtd) {
+		if (use_default_ptable) {
+			printk(KERN_INFO " Using default partition table.\n");
+			err = add_mtd_partitions(mymtd, axis_default_partitions,
+						 NUM_DEFAULT_PARTITIONS);
+		} else {
+			err = add_mtd_partitions(mymtd, axis_partitions, pidx);
+		}
+
+		if (err) {
+			panic("axisflashmap could not add MTD partitions!\n");
+		}
+	}
+/* CONFIG_EXTRAXFS_SIM */
+#endif
+
+	if (!romfs_in_flash) {
+		/* Create an RAM device for the root partition (romfs). */
+
+#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0)
+		/* No use trying to boot this kernel from RAM. Panic! */
+		printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
+		       "device due to kernel (mis)configuration!\n");
+		panic("This kernel cannot boot from RAM!\n");
+#else
+		struct mtd_info *mtd_ram;
+
+		mtd_ram = (struct mtd_info *)kmalloc(sizeof(struct mtd_info),
+						     GFP_KERNEL);
+		if (!mtd_ram) {
+			panic("axisflashmap couldn't allocate memory for "
+			      "mtd_info!\n");
+		}
+
+		printk(KERN_INFO " Adding RAM partition for romfs image:\n");
+		printk(pmsg, pidx, romfs_start, romfs_length);
+
+		err = mtdram_init_device(mtd_ram, (void*)romfs_start,
+		                         romfs_length, "romfs");
+		if (err) {
+			panic("axisflashmap could not initialize MTD RAM "
+			      "device!\n");
+		}
+#endif
+	}
+
+	return err;
+}
+
+/* This adds the above to the kernels init-call chain. */
+module_init(init_axis_flash);
+
+EXPORT_SYMBOL(axisflash_mtd);

+ 3522 - 0
arch/cris/arch-v32/drivers/cryptocop.c

@@ -0,0 +1,3522 @@
+/* $Id: cryptocop.c,v 1.13 2005/04/21 17:27:55 henriken Exp $
+ *
+ * Stream co-processor driver for the ETRAX FS
+ *
+ *    Copyright (C) 2003-2005  Axis Communications AB
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+#include <linux/stddef.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#include <asm/signal.h>
+#include <asm/irq.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/hwregs/dma.h>
+#include <asm/arch/hwregs/reg_map.h>
+#include <asm/arch/hwregs/reg_rdwr.h>
+#include <asm/arch/hwregs/intr_vect_defs.h>
+
+#include <asm/arch/hwregs/strcop.h>
+#include <asm/arch/hwregs/strcop_defs.h>
+#include <asm/arch/cryptocop.h>
+
+
+
+#define DESCR_ALLOC_PAD  (31)
+
+struct cryptocop_dma_desc {
+	char *free_buf; /* If non-null will be kfreed in free_cdesc() */
+	dma_descr_data *dma_descr;
+
+	unsigned char dma_descr_buf[sizeof(dma_descr_data) + DESCR_ALLOC_PAD];
+
+	unsigned int from_pool:1; /* If 1 'allocated' from the descriptor pool. */
+	struct cryptocop_dma_desc *next;
+};
+
+
+struct cryptocop_int_operation{
+	void                        *alloc_ptr;
+	cryptocop_session_id        sid;
+
+	dma_descr_context           ctx_out;
+	dma_descr_context           ctx_in;
+
+	/* DMA descriptors allocated by driver. */
+	struct cryptocop_dma_desc   *cdesc_out;
+	struct cryptocop_dma_desc   *cdesc_in;
+
+	/* Strcop config to use. */
+	cryptocop_3des_mode         tdes_mode;
+	cryptocop_csum_type         csum_mode;
+
+	/* DMA descrs provided by consumer. */
+	dma_descr_data              *ddesc_out;
+	dma_descr_data              *ddesc_in;
+};
+
+
+struct cryptocop_tfrm_ctx {
+	cryptocop_tfrm_id tid;
+	unsigned int blocklength;
+
+	unsigned int start_ix;
+
+	struct cryptocop_tfrm_cfg *tcfg;
+	struct cryptocop_transform_ctx *tctx;
+
+	unsigned char previous_src;
+	unsigned char current_src;
+
+	/* Values to use in metadata out. */
+	unsigned char hash_conf;
+	unsigned char hash_mode;
+	unsigned char ciph_conf;
+	unsigned char cbcmode;
+	unsigned char decrypt;
+
+	unsigned int requires_padding:1;
+	unsigned int strict_block_length:1;
+	unsigned int active:1;
+	unsigned int done:1;
+	size_t consumed;
+	size_t produced;
+
+	/* Pad (input) descriptors to put in the DMA out list when the transform
+	 * output is put on the DMA in list. */
+	struct cryptocop_dma_desc *pad_descs;
+
+	struct cryptocop_tfrm_ctx *prev_src;
+	struct cryptocop_tfrm_ctx *curr_src;
+
+	/* Mapping to HW. */
+	unsigned char unit_no;
+};
+
+
+struct cryptocop_private{
+	cryptocop_session_id sid;
+	struct cryptocop_private *next;
+};
+
+/* Session list. */
+
+struct cryptocop_transform_ctx{
+	struct cryptocop_transform_init init;
+	unsigned char dec_key[CRYPTOCOP_MAX_KEY_LENGTH];
+	unsigned int dec_key_set:1;
+
+	struct cryptocop_transform_ctx *next;
+};
+
+
+struct cryptocop_session{
+	cryptocop_session_id sid;
+
+	struct cryptocop_transform_ctx *tfrm_ctx;
+
+	struct cryptocop_session *next;
+};
+
+/* Priority levels for jobs sent to the cryptocop.  Checksum operations from
+   kernel have highest priority since TCPIP stack processing must not
+   be a bottleneck. */
+typedef enum {
+	cryptocop_prio_kernel_csum = 0,
+	cryptocop_prio_kernel = 1,
+	cryptocop_prio_user = 2,
+	cryptocop_prio_no_prios = 3
+} cryptocop_queue_priority;
+
+struct cryptocop_prio_queue{
+	struct list_head jobs;
+	cryptocop_queue_priority prio;
+};
+
+struct cryptocop_prio_job{
+	struct list_head node;
+	cryptocop_queue_priority prio;
+
+	struct cryptocop_operation *oper;
+	struct cryptocop_int_operation *iop;
+};
+
+struct ioctl_job_cb_ctx {
+	unsigned int processed:1;
+};
+
+
+static struct cryptocop_session *cryptocop_sessions = NULL;
+spinlock_t cryptocop_sessions_lock;
+
+/* Next Session ID to assign. */
+static cryptocop_session_id next_sid = 1;
+
+/* Pad for checksum. */
+static const char csum_zero_pad[1] = {0x00};
+
+/* Trash buffer for mem2mem operations. */
+#define MEM2MEM_DISCARD_BUF_LENGTH  (512)
+static unsigned char mem2mem_discard_buf[MEM2MEM_DISCARD_BUF_LENGTH];
+
+/* Descriptor pool. */
+/* FIXME Tweak this value. */
+#define CRYPTOCOP_DESCRIPTOR_POOL_SIZE   (100)
+static struct cryptocop_dma_desc descr_pool[CRYPTOCOP_DESCRIPTOR_POOL_SIZE];
+static struct cryptocop_dma_desc *descr_pool_free_list;
+static int descr_pool_no_free;
+static spinlock_t descr_pool_lock;
+
+/* Lock to stop cryptocop to start processing of a new operation. The holder
+   of this lock MUST call cryptocop_start_job() after it is unlocked. */
+spinlock_t cryptocop_process_lock;
+
+static struct cryptocop_prio_queue cryptocop_job_queues[cryptocop_prio_no_prios];
+static spinlock_t cryptocop_job_queue_lock;
+static struct cryptocop_prio_job *cryptocop_running_job = NULL;
+static spinlock_t running_job_lock;
+
+/* The interrupt handler appends completed jobs to this list. The scehduled
+ * tasklet removes them upon sending the response to the crypto consumer. */
+static struct list_head cryptocop_completed_jobs;
+static spinlock_t cryptocop_completed_jobs_lock;
+
+DECLARE_WAIT_QUEUE_HEAD(cryptocop_ioc_process_wq);
+
+
+/** Local functions. **/
+
+static int cryptocop_open(struct inode *, struct file *);
+
+static int cryptocop_release(struct inode *, struct file *);
+
+static int cryptocop_ioctl(struct inode *inode, struct file *file,
+			   unsigned int cmd, unsigned long arg);
+
+static void cryptocop_start_job(void);
+
+static int cryptocop_job_queue_insert(cryptocop_queue_priority prio, struct cryptocop_operation *operation);
+static int cryptocop_job_setup(struct cryptocop_prio_job **pj, struct cryptocop_operation *operation);
+
+static int cryptocop_job_queue_init(void);
+static void cryptocop_job_queue_close(void);
+
+static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length);
+
+static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length);
+
+static int transform_ok(struct cryptocop_transform_init *tinit);
+
+static struct cryptocop_session *get_session(cryptocop_session_id sid);
+
+static struct cryptocop_transform_ctx *get_transform_ctx(struct cryptocop_session *sess, cryptocop_tfrm_id tid);
+
+static void delete_internal_operation(struct cryptocop_int_operation *iop);
+
+static void get_aes_decrypt_key(unsigned char *dec_key, const unsigned  char *key, unsigned int keylength);
+
+static int init_stream_coprocessor(void);
+
+static void __exit exit_stream_coprocessor(void);
+
+/*#define LDEBUG*/
+#ifdef LDEBUG
+#define DEBUG(s) s
+#define DEBUG_API(s) s
+static void print_cryptocop_operation(struct cryptocop_operation *cop);
+static void print_dma_descriptors(struct cryptocop_int_operation *iop);
+static void print_strcop_crypto_op(struct strcop_crypto_op *cop);
+static void print_lock_status(void);
+static void print_user_dma_lists(struct cryptocop_dma_list_operation *dma_op);
+#define assert(s) do{if (!(s)) panic(#s);} while(0);
+#else
+#define DEBUG(s)
+#define DEBUG_API(s)
+#define assert(s)
+#endif
+
+
+/* Transform constants. */
+#define DES_BLOCK_LENGTH   (8)
+#define AES_BLOCK_LENGTH   (16)
+#define MD5_BLOCK_LENGTH   (64)
+#define SHA1_BLOCK_LENGTH  (64)
+#define CSUM_BLOCK_LENGTH  (2)
+#define MD5_STATE_LENGTH   (16)
+#define SHA1_STATE_LENGTH  (20)
+
+/* The device number. */
+#define CRYPTOCOP_MAJOR    (254)
+#define CRYPTOCOP_MINOR    (0)
+
+
+
+struct file_operations cryptocop_fops = {
+	owner: THIS_MODULE,
+	open: cryptocop_open,
+	release: cryptocop_release,
+	ioctl: cryptocop_ioctl
+};
+
+
+static void free_cdesc(struct cryptocop_dma_desc *cdesc)
+{
+	DEBUG(printk("free_cdesc: cdesc 0x%p, from_pool=%d\n", cdesc, cdesc->from_pool));
+	if (cdesc->free_buf) kfree(cdesc->free_buf);
+
+	if (cdesc->from_pool) {
+		unsigned long int flags;
+		spin_lock_irqsave(&descr_pool_lock, flags);
+		cdesc->next = descr_pool_free_list;
+		descr_pool_free_list = cdesc;
+		++descr_pool_no_free;
+		spin_unlock_irqrestore(&descr_pool_lock, flags);
+	} else {
+		kfree(cdesc);
+	}
+}
+
+
+static struct cryptocop_dma_desc *alloc_cdesc(int alloc_flag)
+{
+	int use_pool = (alloc_flag & GFP_ATOMIC) ? 1 : 0;
+	struct cryptocop_dma_desc *cdesc;
+
+	if (use_pool) {
+		unsigned long int flags;
+		spin_lock_irqsave(&descr_pool_lock, flags);
+		if (!descr_pool_free_list) {
+			spin_unlock_irqrestore(&descr_pool_lock, flags);
+			DEBUG_API(printk("alloc_cdesc: pool is empty\n"));
+			return NULL;
+		}
+		cdesc = descr_pool_free_list;
+		descr_pool_free_list = descr_pool_free_list->next;
+		--descr_pool_no_free;
+		spin_unlock_irqrestore(&descr_pool_lock, flags);
+		cdesc->from_pool = 1;
+	} else {
+		cdesc = kmalloc(sizeof(struct cryptocop_dma_desc), alloc_flag);
+		if (!cdesc) {
+			DEBUG_API(printk("alloc_cdesc: kmalloc\n"));
+			return NULL;
+		}
+		cdesc->from_pool = 0;
+	}
+	cdesc->dma_descr = (dma_descr_data*)(((unsigned long int)cdesc + offsetof(struct cryptocop_dma_desc, dma_descr_buf) + DESCR_ALLOC_PAD) & ~0x0000001F);
+
+	cdesc->next = NULL;
+
+	cdesc->free_buf = NULL;
+	cdesc->dma_descr->out_eop = 0;
+	cdesc->dma_descr->in_eop = 0;
+	cdesc->dma_descr->intr = 0;
+	cdesc->dma_descr->eol = 0;
+	cdesc->dma_descr->wait = 0;
+	cdesc->dma_descr->buf = NULL;
+	cdesc->dma_descr->after = NULL;
+
+	DEBUG_API(printk("alloc_cdesc: return 0x%p, cdesc->dma_descr=0x%p, from_pool=%d\n", cdesc, cdesc->dma_descr, cdesc->from_pool));
+	return cdesc;
+}
+
+
+static void setup_descr_chain(struct cryptocop_dma_desc *cd)
+{
+	DEBUG(printk("setup_descr_chain: entering\n"));
+	while (cd) {
+		if (cd->next) {
+			cd->dma_descr->next = (dma_descr_data*)virt_to_phys(cd->next->dma_descr);
+		} else {
+			cd->dma_descr->next = NULL;
+		}
+		cd = cd->next;
+	}
+	DEBUG(printk("setup_descr_chain: exit\n"));
+}
+
+
+/* Create a pad descriptor for the transform.
+ * Return -1 for error, 0 if pad created. */
+static int create_pad_descriptor(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **pad_desc, int alloc_flag)
+{
+	struct cryptocop_dma_desc        *cdesc = NULL;
+	int                              error = 0;
+	struct strcop_meta_out           mo = {
+		.ciphsel = src_none,
+		.hashsel = src_none,
+		.csumsel = src_none
+	};
+	char                             *pad;
+	size_t                           plen;
+
+	DEBUG(printk("create_pad_descriptor: start.\n"));
+	/* Setup pad descriptor. */
+
+	DEBUG(printk("create_pad_descriptor: setting up padding.\n"));
+	cdesc = alloc_cdesc(alloc_flag);
+	if (!cdesc){
+		DEBUG_API(printk("create_pad_descriptor: alloc pad desc\n"));
+		goto error_cleanup;
+	}
+	switch (tc->unit_no) {
+	case src_md5:
+		error = create_md5_pad(alloc_flag, tc->consumed, &pad, &plen);
+		if (error){
+			DEBUG_API(printk("create_pad_descriptor: create_md5_pad_failed\n"));
+			goto error_cleanup;
+		}
+		cdesc->free_buf = pad;
+		mo.hashsel = src_dma;
+		mo.hashconf = tc->hash_conf;
+		mo.hashmode = tc->hash_mode;
+		break;
+	case src_sha1:
+		error = create_sha1_pad(alloc_flag, tc->consumed, &pad, &plen);
+		if (error){
+			DEBUG_API(printk("create_pad_descriptor: create_sha1_pad_failed\n"));
+			goto error_cleanup;
+		}
+		cdesc->free_buf = pad;
+		mo.hashsel = src_dma;
+		mo.hashconf = tc->hash_conf;
+		mo.hashmode = tc->hash_mode;
+		break;
+	case src_csum:
+		if (tc->consumed % tc->blocklength){
+			pad = (char*)csum_zero_pad;
+			plen = 1;
+		} else {
+			pad = (char*)cdesc; /* Use any pointer. */
+			plen = 0;
+		}
+		mo.csumsel = src_dma;
+		break;
+	}
+	cdesc->dma_descr->wait = 1;
+	cdesc->dma_descr->out_eop = 1; /* Since this is a pad output is pushed.  EOP is ok here since the padded unit is the only one active. */
+	cdesc->dma_descr->buf = (char*)virt_to_phys((char*)pad);
+	cdesc->dma_descr->after = cdesc->dma_descr->buf + plen;
+
+	cdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo);
+	*pad_desc = cdesc;
+
+	return 0;
+
+ error_cleanup:
+	if (cdesc) free_cdesc(cdesc);
+	return -1;
+}
+
+
+static int setup_key_dl_desc(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **kd, int alloc_flag)
+{
+	struct cryptocop_dma_desc  *key_desc = alloc_cdesc(alloc_flag);
+	struct strcop_meta_out     mo = {0};
+
+	DEBUG(printk("setup_key_dl_desc\n"));
+
+	if (!key_desc) {
+		DEBUG_API(printk("setup_key_dl_desc: failed descriptor allocation.\n"));
+		return -ENOMEM;
+	}
+
+	/* Download key. */
+	if ((tc->tctx->init.alg == cryptocop_alg_aes) && (tc->tcfg->flags & CRYPTOCOP_DECRYPT)) {
+		/* Precook the AES decrypt key. */
+		if (!tc->tctx->dec_key_set){
+			get_aes_decrypt_key(tc->tctx->dec_key, tc->tctx->init.key, tc->tctx->init.keylen);
+			tc->tctx->dec_key_set = 1;
+		}
+		key_desc->dma_descr->buf = (char*)virt_to_phys(tc->tctx->dec_key);
+		key_desc->dma_descr->after = key_desc->dma_descr->buf + tc->tctx->init.keylen/8;
+	} else {
+		key_desc->dma_descr->buf = (char*)virt_to_phys(tc->tctx->init.key);
+		key_desc->dma_descr->after = key_desc->dma_descr->buf + tc->tctx->init.keylen/8;
+	}
+	/* Setup metadata. */
+	mo.dlkey = 1;
+	switch (tc->tctx->init.keylen) {
+	case 64:
+		mo.decrypt = 0;
+		mo.hashmode = 0;
+		break;
+	case 128:
+		mo.decrypt = 0;
+		mo.hashmode = 1;
+		break;
+	case 192:
+		mo.decrypt = 1;
+		mo.hashmode = 0;
+		break;
+	case 256:
+		mo.decrypt = 1;
+		mo.hashmode = 1;
+		break;
+	default:
+		break;
+	}
+	mo.ciphsel = mo.hashsel = mo.csumsel = src_none;
+	key_desc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo);
+
+	key_desc->dma_descr->out_eop = 1;
+	key_desc->dma_descr->wait = 1;
+	key_desc->dma_descr->intr = 0;
+
+	*kd = key_desc;
+	return 0;
+}
+
+static int setup_cipher_iv_desc(struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **id, int alloc_flag)
+{
+	struct cryptocop_dma_desc  *iv_desc = alloc_cdesc(alloc_flag);
+	struct strcop_meta_out     mo = {0};
+
+	DEBUG(printk("setup_cipher_iv_desc\n"));
+
+	if (!iv_desc) {
+		DEBUG_API(printk("setup_cipher_iv_desc: failed CBC IV descriptor allocation.\n"));
+		return -ENOMEM;
+	}
+	/* Download IV. */
+	iv_desc->dma_descr->buf = (char*)virt_to_phys(tc->tcfg->iv);
+	iv_desc->dma_descr->after = iv_desc->dma_descr->buf + tc->blocklength;
+
+	/* Setup metadata. */
+	mo.hashsel = mo.csumsel = src_none;
+	mo.ciphsel = src_dma;
+	mo.ciphconf = tc->ciph_conf;
+	mo.cbcmode = tc->cbcmode;
+
+	iv_desc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, mo);
+
+	iv_desc->dma_descr->out_eop = 0;
+	iv_desc->dma_descr->wait = 1;
+	iv_desc->dma_descr->intr = 0;
+
+	*id = iv_desc;
+	return 0;
+}
+
+/* Map the ouput length of the transform to operation output starting on the inject index. */
+static int create_input_descriptors(struct cryptocop_operation *operation, struct cryptocop_tfrm_ctx *tc, struct cryptocop_dma_desc **id, int alloc_flag)
+{
+	int                        err = 0;
+	struct cryptocop_dma_desc  head = {0};
+	struct cryptocop_dma_desc  *outdesc = &head;
+	size_t                     iov_offset = 0;
+	size_t                     out_ix = 0;
+	int                        outiov_ix = 0;
+	struct strcop_meta_in      mi = {0};
+
+	size_t                     out_length = tc->produced;
+	int                        rem_length;
+	int                        dlength;
+
+	assert(out_length != 0);
+	if (((tc->produced + tc->tcfg->inject_ix) > operation->tfrm_op.outlen) || (tc->produced && (operation->tfrm_op.outlen == 0))) {
+		DEBUG_API(printk("create_input_descriptors: operation outdata too small\n"));
+		return -EINVAL;
+	}
+	/* Traverse the out iovec until the result inject index is reached. */
+	while ((outiov_ix < operation->tfrm_op.outcount) && ((out_ix + operation->tfrm_op.outdata[outiov_ix].iov_len) <= tc->tcfg->inject_ix)){
+		out_ix += operation->tfrm_op.outdata[outiov_ix].iov_len;
+		outiov_ix++;
+	}
+	if (outiov_ix >= operation->tfrm_op.outcount){
+		DEBUG_API(printk("create_input_descriptors: operation outdata too small\n"));
+		return -EINVAL;
+	}
+	iov_offset = tc->tcfg->inject_ix - out_ix;
+	mi.dmasel = tc->unit_no;
+
+	/* Setup the output descriptors. */
+	while ((out_length > 0) && (outiov_ix < operation->tfrm_op.outcount)) {
+		outdesc->next = alloc_cdesc(alloc_flag);
+		if (!outdesc->next) {
+			DEBUG_API(printk("create_input_descriptors: alloc_cdesc\n"));
+			err = -ENOMEM;
+			goto error_cleanup;
+		}
+		outdesc = outdesc->next;
+		rem_length = operation->tfrm_op.outdata[outiov_ix].iov_len - iov_offset;
+		dlength = (out_length < rem_length) ? out_length : rem_length;
+
+		DEBUG(printk("create_input_descriptors:\n"
+			     "outiov_ix=%d, rem_length=%d, dlength=%d\n"
+			     "iov_offset=%d, outdata[outiov_ix].iov_len=%d\n"
+			     "outcount=%d, outiov_ix=%d\n",
+			     outiov_ix, rem_length, dlength, iov_offset, operation->tfrm_op.outdata[outiov_ix].iov_len, operation->tfrm_op.outcount, outiov_ix));
+
+		outdesc->dma_descr->buf = (char*)virt_to_phys(operation->tfrm_op.outdata[outiov_ix].iov_base + iov_offset);
+		outdesc->dma_descr->after = outdesc->dma_descr->buf + dlength;
+		outdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);
+
+		out_length -= dlength;
+		iov_offset += dlength;
+		if (iov_offset >= operation->tfrm_op.outdata[outiov_ix].iov_len) {
+			iov_offset = 0;
+			++outiov_ix;
+		}
+	}
+	if (out_length > 0){
+		DEBUG_API(printk("create_input_descriptors: not enough room for output, %d remained\n", out_length));
+		err = -EINVAL;
+		goto error_cleanup;
+	}
+	/* Set sync in last descriptor. */
+	mi.sync = 1;
+	outdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);
+
+	*id = head.next;
+	return 0;
+
+ error_cleanup:
+	while (head.next) {
+		outdesc = head.next->next;
+		free_cdesc(head.next);
+		head.next = outdesc;
+	}
+	return err;
+}
+
+
+static int create_output_descriptors(struct cryptocop_operation *operation, int *iniov_ix, int *iniov_offset, size_t desc_len, struct cryptocop_dma_desc **current_out_cdesc, struct strcop_meta_out *meta_out, int alloc_flag)
+{
+	while (desc_len != 0) {
+		struct cryptocop_dma_desc  *cdesc;
+		int                        rem_length = operation->tfrm_op.indata[*iniov_ix].iov_len - *iniov_offset;
+		int                        dlength = (desc_len < rem_length) ? desc_len : rem_length;
+
+		cdesc = alloc_cdesc(alloc_flag);
+		if (!cdesc) {
+			DEBUG_API(printk("create_output_descriptors: alloc_cdesc\n"));
+			return -ENOMEM;
+		}
+		(*current_out_cdesc)->next = cdesc;
+		(*current_out_cdesc) = cdesc;
+
+		cdesc->free_buf = NULL;
+
+		cdesc->dma_descr->buf = (char*)virt_to_phys(operation->tfrm_op.indata[*iniov_ix].iov_base + *iniov_offset);
+		cdesc->dma_descr->after = cdesc->dma_descr->buf + dlength;
+
+		desc_len -= dlength;
+		*iniov_offset += dlength;
+		assert(desc_len >= 0);
+		if (*iniov_offset >= operation->tfrm_op.indata[*iniov_ix].iov_len) {
+			*iniov_offset = 0;
+			++(*iniov_ix);
+			if (*iniov_ix > operation->tfrm_op.incount) {
+				DEBUG_API(printk("create_output_descriptors: not enough indata in operation."));
+				return  -EINVAL;
+			}
+		}
+		cdesc->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, (*meta_out));
+	} /* while (desc_len != 0) */
+	/* Last DMA descriptor gets a 'wait' bit to signal expected change in metadata. */
+	(*current_out_cdesc)->dma_descr->wait = 1; /* This will set extraneous WAIT in some situations, e.g. when padding hashes and checksums. */
+
+	return 0;
+}
+
+
+static int append_input_descriptors(struct cryptocop_operation *operation, struct cryptocop_dma_desc **current_in_cdesc, struct cryptocop_dma_desc **current_out_cdesc, struct cryptocop_tfrm_ctx *tc, int alloc_flag)
+{
+	DEBUG(printk("append_input_descriptors, tc=0x%p, unit_no=%d\n", tc, tc->unit_no));
+	if (tc->tcfg) {
+		int                        failed = 0;
+		struct cryptocop_dma_desc  *idescs = NULL;
+		DEBUG(printk("append_input_descriptors: pushing output, consumed %d produced %d bytes.\n", tc->consumed, tc->produced));
+		if (tc->pad_descs) {
+			DEBUG(printk("append_input_descriptors: append pad descriptors to DMA out list.\n"));
+			while (tc->pad_descs) {
+				DEBUG(printk("append descriptor 0x%p\n", tc->pad_descs));
+				(*current_out_cdesc)->next = tc->pad_descs;
+				tc->pad_descs = tc->pad_descs->next;
+				(*current_out_cdesc) = (*current_out_cdesc)->next;
+			}
+		}
+
+		/* Setup and append output descriptors to DMA in list. */
+		if (tc->unit_no == src_dma){
+			/* mem2mem.  Setup DMA in descriptors to discard all input prior to the requested mem2mem data. */
+			struct strcop_meta_in mi = {.sync = 0, .dmasel = src_dma};
+			unsigned int start_ix = tc->start_ix;
+			while (start_ix){
+				unsigned int desclen = start_ix < MEM2MEM_DISCARD_BUF_LENGTH ? start_ix : MEM2MEM_DISCARD_BUF_LENGTH;
+				(*current_in_cdesc)->next = alloc_cdesc(alloc_flag);
+				if (!(*current_in_cdesc)->next){
+					DEBUG_API(printk("append_input_descriptors: alloc_cdesc mem2mem discard failed\n"));
+					return -ENOMEM;
+				}
+				(*current_in_cdesc) = (*current_in_cdesc)->next;
+				(*current_in_cdesc)->dma_descr->buf = (char*)virt_to_phys(mem2mem_discard_buf);
+				(*current_in_cdesc)->dma_descr->after = (*current_in_cdesc)->dma_descr->buf + desclen;
+				(*current_in_cdesc)->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);
+				start_ix -= desclen;
+			}
+			mi.sync = 1;
+			(*current_in_cdesc)->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_in, mi);
+		}
+
+		failed = create_input_descriptors(operation, tc, &idescs, alloc_flag);
+		if (failed){
+			DEBUG_API(printk("append_input_descriptors: output descriptor setup failed\n"));
+			return failed;
+		}
+		DEBUG(printk("append_input_descriptors: append output descriptors to DMA in list.\n"));
+		while (idescs) {
+			DEBUG(printk("append descriptor 0x%p\n", idescs));
+			(*current_in_cdesc)->next = idescs;
+			idescs = idescs->next;
+			(*current_in_cdesc) = (*current_in_cdesc)->next;
+		}
+	}
+	return 0;
+}
+
+
+
+static int cryptocop_setup_dma_list(struct cryptocop_operation *operation, struct cryptocop_int_operation **int_op, int alloc_flag)
+{
+	struct cryptocop_session *sess;
+	struct cryptocop_transform_ctx *tctx;
+
+	struct cryptocop_tfrm_ctx digest_ctx = {
+		.previous_src = src_none,
+		.current_src = src_none,
+		.start_ix = 0,
+		.requires_padding = 1,
+		.strict_block_length = 0,
+		.hash_conf = 0,
+		.hash_mode = 0,
+		.ciph_conf = 0,
+		.cbcmode = 0,
+		.decrypt = 0,
+		.consumed = 0,
+		.produced = 0,
+		.pad_descs = NULL,
+		.active = 0,
+		.done = 0,
+		.prev_src = NULL,
+		.curr_src = NULL,
+		.tcfg = NULL};
+	struct cryptocop_tfrm_ctx cipher_ctx = {
+		.previous_src = src_none,
+		.current_src = src_none,
+		.start_ix = 0,
+		.requires_padding = 0,
+		.strict_block_length = 1,
+		.hash_conf = 0,
+		.hash_mode = 0,
+		.ciph_conf = 0,
+		.cbcmode = 0,
+		.decrypt = 0,
+		.consumed = 0,
+		.produced = 0,
+		.pad_descs = NULL,
+		.active = 0,
+		.done = 0,
+		.prev_src = NULL,
+		.curr_src = NULL,
+		.tcfg = NULL};
+	struct cryptocop_tfrm_ctx csum_ctx = {
+		.previous_src = src_none,
+		.current_src = src_none,
+		.start_ix = 0,
+		.blocklength = 2,
+		.requires_padding = 1,
+		.strict_block_length = 0,
+		.hash_conf = 0,
+		.hash_mode = 0,
+		.ciph_conf = 0,
+		.cbcmode = 0,
+		.decrypt = 0,
+		.consumed = 0,
+		.produced = 0,
+		.pad_descs = NULL,
+		.active = 0,
+		.done = 0,
+		.tcfg = NULL,
+		.prev_src = NULL,
+		.curr_src = NULL,
+		.unit_no = src_csum};
+	struct cryptocop_tfrm_cfg *tcfg = operation->tfrm_op.tfrm_cfg;
+
+	unsigned int indata_ix = 0;
+
+	/* iovec accounting. */
+	int iniov_ix = 0;
+	int iniov_offset = 0;
+
+	/* Operation descriptor cfg traversal pointer. */
+	struct cryptocop_desc *odsc;
+
+	int failed = 0;
+	/* List heads for allocated descriptors. */
+	struct cryptocop_dma_desc out_cdesc_head = {0};
+	struct cryptocop_dma_desc in_cdesc_head = {0};
+
+	struct cryptocop_dma_desc *current_out_cdesc = &out_cdesc_head;
+	struct cryptocop_dma_desc *current_in_cdesc = &in_cdesc_head;
+
+	struct cryptocop_tfrm_ctx *output_tc = NULL;
+	void                      *iop_alloc_ptr;
+
+	assert(operation != NULL);
+	assert(int_op != NULL);
+
+	DEBUG(printk("cryptocop_setup_dma_list: start\n"));
+	DEBUG(print_cryptocop_operation(operation));
+
+	sess = get_session(operation->sid);
+	if (!sess) {
+		DEBUG_API(printk("cryptocop_setup_dma_list: no session found for operation.\n"));
+		failed = -EINVAL;
+		goto error_cleanup;
+	}
+	iop_alloc_ptr = kmalloc(DESCR_ALLOC_PAD + sizeof(struct cryptocop_int_operation), alloc_flag);
+	if (!iop_alloc_ptr) {
+		DEBUG_API(printk("cryptocop_setup_dma_list:  kmalloc cryptocop_int_operation\n"));
+		failed = -ENOMEM;
+		goto error_cleanup;
+	}
+	(*int_op) = (struct cryptocop_int_operation*)(((unsigned long int)(iop_alloc_ptr + DESCR_ALLOC_PAD + offsetof(struct cryptocop_int_operation, ctx_out)) & ~0x0000001F) - offsetof(struct cryptocop_int_operation, ctx_out));
+	DEBUG(memset((*int_op), 0xff, sizeof(struct cryptocop_int_operation)));
+	(*int_op)->alloc_ptr = iop_alloc_ptr;
+	DEBUG(printk("cryptocop_setup_dma_list: *int_op=0x%p, alloc_ptr=0x%p\n", *int_op, (*int_op)->alloc_ptr));
+
+	(*int_op)->sid = operation->sid;
+	(*int_op)->cdesc_out = NULL;
+	(*int_op)->cdesc_in = NULL;
+	(*int_op)->tdes_mode = cryptocop_3des_ede;
+	(*int_op)->csum_mode = cryptocop_csum_le;
+	(*int_op)->ddesc_out = NULL;
+	(*int_op)->ddesc_in = NULL;
+
+	/* Scan operation->tfrm_op.tfrm_cfg for bad configuration and set up the local contexts. */
+	if (!tcfg) {
+		DEBUG_API(printk("cryptocop_setup_dma_list: no configured transforms in operation.\n"));
+		failed = -EINVAL;
+		goto error_cleanup;
+	}
+	while (tcfg) {
+		tctx = get_transform_ctx(sess, tcfg->tid);
+		if (!tctx) {
+			DEBUG_API(printk("cryptocop_setup_dma_list: no transform id %d in session.\n", tcfg->tid));
+			failed = -EINVAL;
+			goto error_cleanup;
+		}
+		if (tcfg->inject_ix > operation->tfrm_op.outlen){
+			DEBUG_API(printk("cryptocop_setup_dma_list: transform id %d inject_ix (%d) > operation->tfrm_op.outlen(%d)", tcfg->tid, tcfg->inject_ix, operation->tfrm_op.outlen));
+			failed = -EINVAL;
+			goto error_cleanup;
+		}
+		switch (tctx->init.alg){
+		case cryptocop_alg_mem2mem:
+			if (cipher_ctx.tcfg != NULL){
+				DEBUG_API(printk("cryptocop_setup_dma_list: multiple ciphers in operation.\n"));
+				failed = -EINVAL;
+				goto error_cleanup;
+			}
+			/* mem2mem is handled as a NULL cipher. */
+			cipher_ctx.cbcmode = 0;
+			cipher_ctx.decrypt = 0;
+			cipher_ctx.blocklength = 1;
+			cipher_ctx.ciph_conf = 0;
+			cipher_ctx.unit_no = src_dma;
+			cipher_ctx.tcfg = tcfg;
+			cipher_ctx.tctx = tctx;
+			break;
+		case cryptocop_alg_des:
+		case cryptocop_alg_3des:
+		case cryptocop_alg_aes:
+			/* cipher */
+			if (cipher_ctx.tcfg != NULL){
+				DEBUG_API(printk("cryptocop_setup_dma_list: multiple ciphers in operation.\n"));
+				failed = -EINVAL;
+				goto error_cleanup;
+			}
+			cipher_ctx.tcfg = tcfg;
+			cipher_ctx.tctx = tctx;
+			if (cipher_ctx.tcfg->flags & CRYPTOCOP_DECRYPT){
+				cipher_ctx.decrypt = 1;
+			}
+			switch (tctx->init.cipher_mode) {
+			case cryptocop_cipher_mode_ecb:
+				cipher_ctx.cbcmode = 0;
+				break;
+			case cryptocop_cipher_mode_cbc:
+				cipher_ctx.cbcmode = 1;
+				break;
+			default:
+				DEBUG_API(printk("cryptocop_setup_dma_list: cipher_ctx, bad cipher mode==%d\n", tctx->init.cipher_mode));
+				failed = -EINVAL;
+				goto error_cleanup;
+			}
+			DEBUG(printk("cryptocop_setup_dma_list: cipher_ctx, set CBC mode==%d\n", cipher_ctx.cbcmode));
+			switch (tctx->init.alg){
+			case cryptocop_alg_des:
+				cipher_ctx.ciph_conf = 0;
+				cipher_ctx.unit_no = src_des;
+				cipher_ctx.blocklength = DES_BLOCK_LENGTH;
+				break;
+			case cryptocop_alg_3des:
+				cipher_ctx.ciph_conf = 1;
+				cipher_ctx.unit_no = src_des;
+				cipher_ctx.blocklength = DES_BLOCK_LENGTH;
+				break;
+			case cryptocop_alg_aes:
+				cipher_ctx.ciph_conf = 2;
+				cipher_ctx.unit_no = src_aes;
+				cipher_ctx.blocklength = AES_BLOCK_LENGTH;
+				break;
+			default:
+				panic("cryptocop_setup_dma_list: impossible algorithm %d\n", tctx->init.alg);
+			}
+			(*int_op)->tdes_mode = tctx->init.tdes_mode;
+			break;
+		case cryptocop_alg_md5:
+		case cryptocop_alg_sha1:
+			/* digest */
+			if (digest_ctx.tcfg != NULL){
+				DEBUG_API(printk("cryptocop_setup_dma_list: multiple digests in operation.\n"));
+				failed = -EINVAL;
+				goto error_cleanup;
+			}
+			digest_ctx.tcfg = tcfg;
+			digest_ctx.tctx = tctx;
+			digest_ctx.hash_mode = 0; /* Don't use explicit IV in this API. */
+			switch (tctx->init.alg){
+			case cryptocop_alg_md5:
+				digest_ctx.blocklength = MD5_BLOCK_LENGTH;
+				digest_ctx.unit_no = src_md5;
+				digest_ctx.hash_conf = 1; /* 1 => MD-5 */
+				break;
+			case cryptocop_alg_sha1:
+				digest_ctx.blocklength = SHA1_BLOCK_LENGTH;
+				digest_ctx.unit_no = src_sha1;
+				digest_ctx.hash_conf = 0; /* 0 => SHA-1 */
+				break;
+			default:
+				panic("cryptocop_setup_dma_list: impossible digest algorithm\n");
+			}
+			break;
+		case cryptocop_alg_csum:
+			/* digest */
+			if (csum_ctx.tcfg != NULL){
+				DEBUG_API(printk("cryptocop_setup_dma_list: multiple checksums in operation.\n"));
+				failed = -EINVAL;
+				goto error_cleanup;
+			}
+			(*int_op)->csum_mode = tctx->init.csum_mode;
+			csum_ctx.tcfg = tcfg;
+			csum_ctx.tctx = tctx;
+			break;
+		default:
+			/* no algorithm. */
+			DEBUG_API(printk("cryptocop_setup_dma_list: invalid algorithm %d specified in tfrm %d.\n", tctx->init.alg, tcfg->tid));
+			failed = -EINVAL;
+			goto error_cleanup;
+		}
+		tcfg = tcfg->next;
+	}
+	/* Download key if a cipher is used. */
+	if (cipher_ctx.tcfg && (cipher_ctx.tctx->init.alg != cryptocop_alg_mem2mem)){
+		struct cryptocop_dma_desc  *key_desc = NULL;
+
+		failed = setup_key_dl_desc(&cipher_ctx, &key_desc, alloc_flag);
+		if (failed) {
+			DEBUG_API(printk("cryptocop_setup_dma_list: setup key dl\n"));
+			goto error_cleanup;
+		}
+		current_out_cdesc->next = key_desc;
+		current_out_cdesc = key_desc;
+		indata_ix += (unsigned int)(key_desc->dma_descr->after - key_desc->dma_descr->buf);
+
+		/* Download explicit IV if a cipher is used and CBC mode and explicit IV selected. */
+		if ((cipher_ctx.tctx->init.cipher_mode == cryptocop_cipher_mode_cbc) && (cipher_ctx.tcfg->flags & CRYPTOCOP_EXPLICIT_IV)) {
+			struct cryptocop_dma_desc  *iv_desc = NULL;
+
+			DEBUG(printk("cryptocop_setup_dma_list: setup cipher CBC IV descriptor.\n"));
+
+			failed = setup_cipher_iv_desc(&cipher_ctx, &iv_desc, alloc_flag);
+			if (failed) {
+				DEBUG_API(printk("cryptocop_setup_dma_list: CBC IV descriptor.\n"));
+				goto error_cleanup;
+			}
+			current_out_cdesc->next = iv_desc;
+			current_out_cdesc = iv_desc;
+			indata_ix += (unsigned int)(iv_desc->dma_descr->after - iv_desc->dma_descr->buf);
+		}
+	}
+
+	/* Process descriptors. */
+	odsc = operation->tfrm_op.desc;
+	while (odsc) {
+		struct cryptocop_desc_cfg   *dcfg = odsc->cfg;
+		struct strcop_meta_out      meta_out = {0};
+		size_t                      desc_len = odsc->length;
+		int                         active_count, eop_needed_count;
+
+		output_tc = NULL;
+
+		DEBUG(printk("cryptocop_setup_dma_list: parsing an operation descriptor\n"));
+
+		while (dcfg) {
+			struct cryptocop_tfrm_ctx  *tc = NULL;
+
+			DEBUG(printk("cryptocop_setup_dma_list: parsing an operation descriptor configuration.\n"));
+			/* Get the local context for the transform and mark it as the output unit if it produces output. */
+			if (digest_ctx.tcfg && (digest_ctx.tcfg->tid == dcfg->tid)){
+				tc = &digest_ctx;
+			} else if (cipher_ctx.tcfg && (cipher_ctx.tcfg->tid == dcfg->tid)){
+				tc = &cipher_ctx;
+			} else if (csum_ctx.tcfg && (csum_ctx.tcfg->tid == dcfg->tid)){
+				tc = &csum_ctx;
+			}
+			if (!tc) {
+				DEBUG_API(printk("cryptocop_setup_dma_list: invalid transform %d specified in descriptor.\n", dcfg->tid));
+				failed = -EINVAL;
+				goto error_cleanup;
+			}
+			if (tc->done) {
+				DEBUG_API(printk("cryptocop_setup_dma_list: completed transform %d reused.\n", dcfg->tid));
+				failed = -EINVAL;
+				goto error_cleanup;
+			}
+			if (!tc->active) {
+				tc->start_ix = indata_ix;
+				tc->active = 1;
+			}
+
+			tc->previous_src = tc->current_src;
+			tc->prev_src = tc->curr_src;
+			/* Map source unit id to DMA source config. */
+			switch (dcfg->src){
+			case cryptocop_source_dma:
+				tc->current_src = src_dma;
+				break;
+			case cryptocop_source_des:
+				tc->current_src = src_des;
+				break;
+			case cryptocop_source_3des:
+				tc->current_src = src_des;
+				break;
+			case cryptocop_source_aes:
+				tc->current_src = src_aes;
+				break;
+			case cryptocop_source_md5:
+			case cryptocop_source_sha1:
+			case cryptocop_source_csum:
+			case cryptocop_source_none:
+			default:
+				/* We do not allow using accumulating style units (SHA-1, MD5, checksum) as sources to other units.
+				 */
+				DEBUG_API(printk("cryptocop_setup_dma_list: bad unit source configured %d.\n", dcfg->src));
+				failed = -EINVAL;
+				goto error_cleanup;
+			}
+			if (tc->current_src != src_dma) {
+				/* Find the unit we are sourcing from. */
+				if (digest_ctx.unit_no == tc->current_src){
+					tc->curr_src = &digest_ctx;
+				} else if (cipher_ctx.unit_no == tc->current_src){
+					tc->curr_src = &cipher_ctx;
+				} else if (csum_ctx.unit_no == tc->current_src){
+					tc->curr_src = &csum_ctx;
+				}
+				if ((tc->curr_src == tc) && (tc->unit_no != src_dma)){
+					DEBUG_API(printk("cryptocop_setup_dma_list: unit %d configured to source from itself.\n", tc->unit_no));
+					failed = -EINVAL;
+					goto error_cleanup;
+				}
+			} else {
+				tc->curr_src = NULL;
+			}
+
+			/* Detect source switch. */
+			DEBUG(printk("cryptocop_setup_dma_list: tc->active=%d tc->unit_no=%d tc->current_src=%d tc->previous_src=%d, tc->curr_src=0x%p, tc->prev_srv=0x%p\n", tc->active, tc->unit_no, tc->current_src, tc->previous_src, tc->curr_src, tc->prev_src));
+			if (tc->active && (tc->current_src != tc->previous_src)) {
+				/* Only allow source switch when both the old source unit and the new one have
+				 * no pending data to process (i.e. the consumed length must be a multiple of the
+				 * transform blocklength). */
+				/* Note: if the src == NULL we are actually sourcing from DMA out. */
+				if (((tc->prev_src != NULL) && (tc->prev_src->consumed % tc->prev_src->blocklength)) ||
+				    ((tc->curr_src != NULL) && (tc->curr_src->consumed % tc->curr_src->blocklength)))
+				{
+					DEBUG_API(printk("cryptocop_setup_dma_list: can only disconnect from or connect to a unit on a multiple of the blocklength, old: cons=%d, prod=%d, block=%d, new: cons=%d prod=%d, block=%d.\n", tc->prev_src ? tc->prev_src->consumed : INT_MIN, tc->prev_src ? tc->prev_src->produced : INT_MIN, tc->prev_src ? tc->prev_src->blocklength : INT_MIN, tc->curr_src ? tc->curr_src->consumed : INT_MIN, tc->curr_src ? tc->curr_src->produced : INT_MIN, tc->curr_src ? tc->curr_src->blocklength : INT_MIN));
+					failed = -EINVAL;
+					goto error_cleanup;
+				}
+			}
+			/* Detect unit deactivation. */
+			if (dcfg->last) {
+				/* Length check of this is handled below. */
+				tc->done = 1;
+			}
+			dcfg = dcfg->next;
+		} /* while (dcfg) */
+		DEBUG(printk("cryptocop_setup_dma_list: parsing operation descriptor configuration complete.\n"));
+
+		if (cipher_ctx.active && (cipher_ctx.curr_src != NULL) && !cipher_ctx.curr_src->active){
+			DEBUG_API(printk("cryptocop_setup_dma_list: cipher source from inactive unit %d\n", cipher_ctx.curr_src->unit_no));
+			failed = -EINVAL;
+			goto error_cleanup;
+		}
+		if (digest_ctx.active && (digest_ctx.curr_src != NULL) && !digest_ctx.curr_src->active){
+			DEBUG_API(printk("cryptocop_setup_dma_list: digest source from inactive unit %d\n", digest_ctx.curr_src->unit_no));
+			failed = -EINVAL;
+			goto error_cleanup;
+		}
+		if (csum_ctx.active && (csum_ctx.curr_src != NULL) && !csum_ctx.curr_src->active){
+			DEBUG_API(printk("cryptocop_setup_dma_list: cipher source from inactive unit %d\n", csum_ctx.curr_src->unit_no));
+			failed = -EINVAL;
+			goto error_cleanup;
+		}
+
+		/* Update consumed and produced lengths.
+
+		   The consumed length accounting here is actually cheating.  If a unit source from DMA (or any
+		   other unit that process data in blocks of one octet) it is correct, but if it source from a
+		   block processing unit, i.e. a cipher, it will be temporarily incorrect at some times.  However
+		   since it is only allowed--by the HW--to change source to or from a block processing unit at times where that
+		   unit has processed an exact multiple of its block length the end result will be correct.
+		   Beware that if the source change restriction change this code will need to be (much) reworked.
+		*/
+		DEBUG(printk("cryptocop_setup_dma_list: desc->length=%d, desc_len=%d.\n", odsc->length, desc_len));
+
+		if (csum_ctx.active) {
+			csum_ctx.consumed += desc_len;
+			if (csum_ctx.done) {
+				csum_ctx.produced = 2;
+			}
+			DEBUG(printk("cryptocop_setup_dma_list: csum_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", csum_ctx.consumed, csum_ctx.produced, csum_ctx.blocklength));
+		}
+		if (digest_ctx.active) {
+			digest_ctx.consumed += desc_len;
+			if (digest_ctx.done) {
+				if (digest_ctx.unit_no == src_md5) {
+					digest_ctx.produced = MD5_STATE_LENGTH;
+				} else {
+					digest_ctx.produced = SHA1_STATE_LENGTH;
+				}
+			}
+			DEBUG(printk("cryptocop_setup_dma_list: digest_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", digest_ctx.consumed, digest_ctx.produced, digest_ctx.blocklength));
+		}
+		if (cipher_ctx.active) {
+			/* Ciphers are allowed only to source from DMA out.  That is filtered above. */
+			assert(cipher_ctx.current_src == src_dma);
+			cipher_ctx.consumed += desc_len;
+			cipher_ctx.produced = cipher_ctx.blocklength * (cipher_ctx.consumed / cipher_ctx.blocklength);
+			if (cipher_ctx.cbcmode && !(cipher_ctx.tcfg->flags & CRYPTOCOP_EXPLICIT_IV) && cipher_ctx.produced){
+				cipher_ctx.produced -= cipher_ctx.blocklength; /* Compensate for CBC iv. */
+			}
+			DEBUG(printk("cryptocop_setup_dma_list: cipher_ctx producing: consumed=%d, produced=%d, blocklength=%d.\n", cipher_ctx.consumed, cipher_ctx.produced, cipher_ctx.blocklength));
+		}
+
+		/* Setup the DMA out descriptors. */
+		/* Configure the metadata. */
+		active_count = 0;
+		eop_needed_count = 0;
+		if (cipher_ctx.active) {
+			++active_count;
+			if (cipher_ctx.unit_no == src_dma){
+				/* mem2mem */
+				meta_out.ciphsel = src_none;
+			} else {
+				meta_out.ciphsel = cipher_ctx.current_src;
+			}
+			meta_out.ciphconf = cipher_ctx.ciph_conf;
+			meta_out.cbcmode = cipher_ctx.cbcmode;
+			meta_out.decrypt = cipher_ctx.decrypt;
+			DEBUG(printk("set ciphsel=%d ciphconf=%d cbcmode=%d decrypt=%d\n", meta_out.ciphsel, meta_out.ciphconf, meta_out.cbcmode, meta_out.decrypt));
+			if (cipher_ctx.done) ++eop_needed_count;
+		} else {
+			meta_out.ciphsel = src_none;
+		}
+
+		if (digest_ctx.active) {
+			++active_count;
+			meta_out.hashsel = digest_ctx.current_src;
+			meta_out.hashconf = digest_ctx.hash_conf;
+			meta_out.hashmode = 0; /* Explicit mode is not used here. */
+			DEBUG(printk("set hashsel=%d hashconf=%d hashmode=%d\n", meta_out.hashsel, meta_out.hashconf, meta_out.hashmode));
+			if (digest_ctx.done) {
+				assert(digest_ctx.pad_descs == NULL);
+				failed = create_pad_descriptor(&digest_ctx, &digest_ctx.pad_descs, alloc_flag);
+				if (failed) {
+					DEBUG_API(printk("cryptocop_setup_dma_list: failed digest pad creation.\n"));
+					goto error_cleanup;
+				}
+			}
+		} else {
+			meta_out.hashsel = src_none;
+		}
+
+		if (csum_ctx.active) {
+			++active_count;
+			meta_out.csumsel = csum_ctx.current_src;
+			if (csum_ctx.done) {
+				assert(csum_ctx.pad_descs == NULL);
+				failed = create_pad_descriptor(&csum_ctx, &csum_ctx.pad_descs, alloc_flag);
+				if (failed) {
+					DEBUG_API(printk("cryptocop_setup_dma_list: failed csum pad creation.\n"));
+					goto error_cleanup;
+				}
+			}
+		} else {
+			meta_out.csumsel = src_none;
+		}
+		DEBUG(printk("cryptocop_setup_dma_list: %d eop needed, %d active units\n", eop_needed_count, active_count));
+		/* Setup DMA out descriptors for the indata. */
+		failed = create_output_descriptors(operation, &iniov_ix, &iniov_offset, desc_len, &current_out_cdesc, &meta_out, alloc_flag);
+		if (failed) {
+			DEBUG_API(printk("cryptocop_setup_dma_list: create_output_descriptors %d\n", failed));
+			goto error_cleanup;
+		}
+		/* Setup out EOP.  If there are active units that are not done here they cannot get an EOP
+		 * so we ust setup a zero length descriptor to DMA to signal EOP only to done units.
+		 * If there is a pad descriptor EOP for the padded unit will be EOPed by it.
+		 */
+		assert(active_count >= eop_needed_count);
+		assert((eop_needed_count == 0) || (eop_needed_count == 1));
+		if (eop_needed_count) {
+			/* This means that the bulk operation (cipeher/m2m) is terminated. */
+			if (active_count > 1) {
+				/* Use zero length EOP descriptor. */
+				struct cryptocop_dma_desc *ed = alloc_cdesc(alloc_flag);
+				struct strcop_meta_out    ed_mo = {0};
+				if (!ed) {
+					DEBUG_API(printk("cryptocop_setup_dma_list: alloc EOP descriptor for cipher\n"));
+					failed = -ENOMEM;
+					goto error_cleanup;
+				}
+
+				assert(cipher_ctx.active && cipher_ctx.done);
+
+				if (cipher_ctx.unit_no == src_dma){
+					/* mem2mem */
+					ed_mo.ciphsel = src_none;
+				} else {
+					ed_mo.ciphsel = cipher_ctx.current_src;
+				}
+				ed_mo.ciphconf = cipher_ctx.ciph_conf;
+				ed_mo.cbcmode = cipher_ctx.cbcmode;
+				ed_mo.decrypt = cipher_ctx.decrypt;
+
+				ed->free_buf = NULL;
+				ed->dma_descr->wait = 1;
+				ed->dma_descr->out_eop = 1;
+
+				ed->dma_descr->buf = (char*)virt_to_phys(&ed); /* Use any valid physical address for zero length descriptor. */
+				ed->dma_descr->after = ed->dma_descr->buf;
+				ed->dma_descr->md = REG_TYPE_CONV(unsigned short int, struct strcop_meta_out, ed_mo);
+				current_out_cdesc->next = ed;
+				current_out_cdesc = ed;
+			} else {
+				/* Set EOP in the current out descriptor since the only active module is
+				 * the one needing the EOP. */
+
+				current_out_cdesc->dma_descr->out_eop = 1;
+			}
+		}
+
+		if (cipher_ctx.done && cipher_ctx.active) cipher_ctx.active = 0;
+		if (digest_ctx.done && digest_ctx.active) digest_ctx.active = 0;
+		if (csum_ctx.done && csum_ctx.active) csum_ctx.active = 0;
+		indata_ix += odsc->length;
+		odsc = odsc->next;
+	} /* while (odsc) */ /* Process descriptors. */
+	DEBUG(printk("cryptocop_setup_dma_list: done parsing operation descriptors\n"));
+	if (cipher_ctx.tcfg && (cipher_ctx.active || !cipher_ctx.done)){
+		DEBUG_API(printk("cryptocop_setup_dma_list: cipher operation not terminated.\n"));
+		failed = -EINVAL;
+		goto error_cleanup;
+	}
+	if (digest_ctx.tcfg && (digest_ctx.active || !digest_ctx.done)){
+		DEBUG_API(printk("cryptocop_setup_dma_list: digest operation not terminated.\n"));
+		failed = -EINVAL;
+		goto error_cleanup;
+	}
+	if (csum_ctx.tcfg && (csum_ctx.active || !csum_ctx.done)){
+		DEBUG_API(printk("cryptocop_setup_dma_list: csum operation not terminated.\n"));
+		failed = -EINVAL;
+		goto error_cleanup;
+	}
+
+	failed = append_input_descriptors(operation, &current_in_cdesc, &current_out_cdesc, &cipher_ctx, alloc_flag);
+	if (failed){
+		DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed));
+		goto error_cleanup;
+	}
+	failed = append_input_descriptors(operation, &current_in_cdesc, &current_out_cdesc, &digest_ctx, alloc_flag);
+	if (failed){
+		DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed));
+		goto error_cleanup;
+	}
+	failed = append_input_descriptors(operation, &current_in_cdesc, &current_out_cdesc, &csum_ctx, alloc_flag);
+	if (failed){
+		DEBUG_API(printk("cryptocop_setup_dma_list: append_input_descriptors cipher_ctx %d\n", failed));
+		goto error_cleanup;
+	}
+
+	DEBUG(printk("cryptocop_setup_dma_list: int_op=0x%p, *int_op=0x%p\n", int_op, *int_op));
+	(*int_op)->cdesc_out = out_cdesc_head.next;
+	(*int_op)->cdesc_in = in_cdesc_head.next;
+	DEBUG(printk("cryptocop_setup_dma_list: out_cdesc_head=0x%p in_cdesc_head=0x%p\n", (*int_op)->cdesc_out, (*int_op)->cdesc_in));
+
+	setup_descr_chain(out_cdesc_head.next);
+	setup_descr_chain(in_cdesc_head.next);
+
+	/* Last but not least: mark the last DMA in descriptor for a INTR and EOL and the the
+	 * last DMA out descriptor for EOL.
+	 */
+	current_in_cdesc->dma_descr->intr = 1;
+	current_in_cdesc->dma_descr->eol = 1;
+	current_out_cdesc->dma_descr->eol = 1;
+
+	/* Setup DMA contexts. */
+	(*int_op)->ctx_out.next = NULL;
+	(*int_op)->ctx_out.eol = 1;
+	(*int_op)->ctx_out.intr = 0;
+	(*int_op)->ctx_out.store_mode = 0;
+	(*int_op)->ctx_out.en = 0;
+	(*int_op)->ctx_out.dis = 0;
+	(*int_op)->ctx_out.md0 = 0;
+	(*int_op)->ctx_out.md1 = 0;
+	(*int_op)->ctx_out.md2 = 0;
+	(*int_op)->ctx_out.md3 = 0;
+	(*int_op)->ctx_out.md4 = 0;
+	(*int_op)->ctx_out.saved_data = (dma_descr_data*)virt_to_phys((*int_op)->cdesc_out->dma_descr);
+	(*int_op)->ctx_out.saved_data_buf = (*int_op)->cdesc_out->dma_descr->buf; /* Already physical address. */
+
+	(*int_op)->ctx_in.next = NULL;
+	(*int_op)->ctx_in.eol = 1;
+	(*int_op)->ctx_in.intr = 0;
+	(*int_op)->ctx_in.store_mode = 0;
+	(*int_op)->ctx_in.en = 0;
+	(*int_op)->ctx_in.dis = 0;
+	(*int_op)->ctx_in.md0 = 0;
+	(*int_op)->ctx_in.md1 = 0;
+	(*int_op)->ctx_in.md2 = 0;
+	(*int_op)->ctx_in.md3 = 0;
+	(*int_op)->ctx_in.md4 = 0;
+
+	(*int_op)->ctx_in.saved_data = (dma_descr_data*)virt_to_phys((*int_op)->cdesc_in->dma_descr);
+	(*int_op)->ctx_in.saved_data_buf = (*int_op)->cdesc_in->dma_descr->buf; /* Already physical address. */
+
+	DEBUG(printk("cryptocop_setup_dma_list: done\n"));
+	return 0;
+
+error_cleanup:
+	{
+		/* Free all allocated resources. */
+		struct cryptocop_dma_desc *tmp_cdesc;
+		while (digest_ctx.pad_descs){
+			tmp_cdesc = digest_ctx.pad_descs->next;
+			free_cdesc(digest_ctx.pad_descs);
+			digest_ctx.pad_descs = tmp_cdesc;
+		}
+		while (csum_ctx.pad_descs){
+			tmp_cdesc = csum_ctx.pad_descs->next;
+			free_cdesc(csum_ctx.pad_descs);
+			csum_ctx.pad_descs = tmp_cdesc;
+		}
+		assert(cipher_ctx.pad_descs == NULL); /* The ciphers are never padded. */
+
+		if (*int_op != NULL) delete_internal_operation(*int_op);
+	}
+	DEBUG_API(printk("cryptocop_setup_dma_list: done with error %d\n", failed));
+	return failed;
+}
+
+
+static void delete_internal_operation(struct cryptocop_int_operation *iop)
+{
+	void                      *ptr = iop->alloc_ptr;
+	struct cryptocop_dma_desc *cd = iop->cdesc_out;
+	struct cryptocop_dma_desc *next;
+
+	DEBUG(printk("delete_internal_operation: iop=0x%p, alloc_ptr=0x%p\n", iop, ptr));
+
+	while (cd) {
+		next = cd->next;
+		free_cdesc(cd);
+		cd = next;
+	}
+	cd = iop->cdesc_in;
+	while (cd) {
+		next = cd->next;
+		free_cdesc(cd);
+		cd = next;
+	}
+	kfree(ptr);
+}
+
+#define MD5_MIN_PAD_LENGTH (9)
+#define MD5_PAD_LENGTH_FIELD_LENGTH (8)
+
+static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length)
+{
+	size_t                  padlen = MD5_BLOCK_LENGTH - (hashed_length % MD5_BLOCK_LENGTH);
+	unsigned char           *p;
+	int                     i;
+	unsigned long long int  bit_length = hashed_length << 3;
+
+	if (padlen < MD5_MIN_PAD_LENGTH) padlen += MD5_BLOCK_LENGTH;
+
+	p = kmalloc(padlen, alloc_flag);
+	if (!pad) return -ENOMEM;
+
+	*p = 0x80;
+	memset(p+1, 0, padlen - 1);
+
+	DEBUG(printk("create_md5_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length));
+
+	i = padlen - MD5_PAD_LENGTH_FIELD_LENGTH;
+	while (bit_length != 0){
+		p[i++] = bit_length % 0x100;
+		bit_length >>= 8;
+	}
+
+	*pad = (char*)p;
+	*pad_length = padlen;
+
+	return 0;
+}
+
+#define SHA1_MIN_PAD_LENGTH (9)
+#define SHA1_PAD_LENGTH_FIELD_LENGTH (8)
+
+static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, char **pad, size_t *pad_length)
+{
+	size_t                  padlen = SHA1_BLOCK_LENGTH - (hashed_length % SHA1_BLOCK_LENGTH);
+	unsigned char           *p;
+	int                     i;
+	unsigned long long int  bit_length = hashed_length << 3;
+
+	if (padlen < SHA1_MIN_PAD_LENGTH) padlen += SHA1_BLOCK_LENGTH;
+
+	p = kmalloc(padlen, alloc_flag);
+	if (!pad) return -ENOMEM;
+
+	*p = 0x80;
+	memset(p+1, 0, padlen - 1);
+
+	DEBUG(printk("create_sha1_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length));
+
+	i = padlen - 1;
+	while (bit_length != 0){
+		p[i--] = bit_length % 0x100;
+		bit_length >>= 8;
+	}
+
+	*pad = (char*)p;
+	*pad_length = padlen;
+
+	return 0;
+}
+
+
+static int transform_ok(struct cryptocop_transform_init *tinit)
+{
+	switch (tinit->alg){
+	case cryptocop_alg_csum:
+		switch (tinit->csum_mode){
+		case cryptocop_csum_le:
+		case cryptocop_csum_be:
+			break;
+		default:
+			DEBUG_API(printk("transform_ok: Bad mode set for csum transform\n"));
+			return -EINVAL;
+		}
+	case cryptocop_alg_mem2mem:
+	case cryptocop_alg_md5:
+	case cryptocop_alg_sha1:
+		if (tinit->keylen != 0) {
+			DEBUG_API(printk("transform_ok: non-zero keylength, %d, for a digest/csum algorithm\n", tinit->keylen));
+			return -EINVAL; /* This check is a bit strict. */
+		}
+		break;
+	case cryptocop_alg_des:
+		if (tinit->keylen != 64) {
+			DEBUG_API(printk("transform_ok: keylen %d invalid for DES\n", tinit->keylen));
+			return -EINVAL;
+		}
+		break;
+	case cryptocop_alg_3des:
+		if (tinit->keylen != 192) {
+			DEBUG_API(printk("transform_ok: keylen %d invalid for 3DES\n", tinit->keylen));
+			return -EINVAL;
+		}
+		break;
+	case cryptocop_alg_aes:
+		if (tinit->keylen != 128 && tinit->keylen != 192 && tinit->keylen != 256) {
+			DEBUG_API(printk("transform_ok: keylen %d invalid for AES\n", tinit->keylen));
+			return -EINVAL;
+		}
+		break;
+	case cryptocop_no_alg:
+	default:
+		DEBUG_API(printk("transform_ok: no such algorithm %d\n", tinit->alg));
+		return -EINVAL;
+	}
+
+	switch (tinit->alg){
+	case cryptocop_alg_des:
+	case cryptocop_alg_3des:
+	case cryptocop_alg_aes:
+		if (tinit->cipher_mode != cryptocop_cipher_mode_ecb && tinit->cipher_mode != cryptocop_cipher_mode_cbc) return -EINVAL;
+	default:
+		 break;
+	}
+	return 0;
+}
+
+
+int cryptocop_new_session(cryptocop_session_id *sid, struct cryptocop_transform_init *tinit, int alloc_flag)
+{
+	struct cryptocop_session         *sess;
+	struct cryptocop_transform_init  *tfrm_in = tinit;
+	struct cryptocop_transform_init  *tmp_in;
+	int                              no_tfrms = 0;
+	int                              i;
+	unsigned long int                flags;
+
+	init_stream_coprocessor(); /* For safety if we are called early */
+
+	while (tfrm_in){
+		int err;
+		++no_tfrms;
+		if ((err = transform_ok(tfrm_in))) {
+			DEBUG_API(printk("cryptocop_new_session, bad transform\n"));
+			return err;
+		}
+		tfrm_in = tfrm_in->next;
+	}
+	if (0 == no_tfrms) {
+		DEBUG_API(printk("cryptocop_new_session, no transforms specified\n"));
+		return -EINVAL;
+	}
+
+	sess = kmalloc(sizeof(struct cryptocop_session), alloc_flag);
+	if (!sess){
+		DEBUG_API(printk("cryptocop_new_session, kmalloc cryptocop_session\n"));
+		return -ENOMEM;
+	}
+
+	sess->tfrm_ctx = kmalloc(no_tfrms * sizeof(struct cryptocop_transform_ctx), alloc_flag);
+	if (!sess->tfrm_ctx) {
+		DEBUG_API(printk("cryptocop_new_session, kmalloc cryptocop_transform_ctx\n"));
+		kfree(sess);
+		return -ENOMEM;
+	}
+
+	tfrm_in = tinit;
+	for (i = 0; i < no_tfrms; i++){
+		tmp_in = tfrm_in->next;
+		while (tmp_in){
+			if (tmp_in->tid == tfrm_in->tid) {
+				DEBUG_API(printk("cryptocop_new_session, duplicate transform ids\n"));
+				kfree(sess->tfrm_ctx);
+				kfree(sess);
+				return -EINVAL;
+			}
+			tmp_in = tmp_in->next;
+		}
+		memcpy(&sess->tfrm_ctx[i].init, tfrm_in, sizeof(struct cryptocop_transform_init));
+		sess->tfrm_ctx[i].dec_key_set = 0;
+		sess->tfrm_ctx[i].next = &sess->tfrm_ctx[i] + 1;
+
+		tfrm_in = tfrm_in->next;
+	}
+	sess->tfrm_ctx[i-1].next = NULL;
+
+	spin_lock_irqsave(&cryptocop_sessions_lock, flags);
+	sess->sid = next_sid;
+	next_sid++;
+	/* TODO If we are really paranoid we should do duplicate check to handle sid wraparound.
+	 *      OTOH 2^64 is a really large number of session. */
+	if (next_sid == 0) next_sid = 1;
+
+	/* Prepend to session list. */
+	sess->next = cryptocop_sessions;
+	cryptocop_sessions = sess;
+	spin_unlock_irqrestore(&cryptocop_sessions_lock, flags);
+	*sid = sess->sid;
+	return 0;
+}
+
+
+int cryptocop_free_session(cryptocop_session_id sid)
+{
+	struct cryptocop_transform_ctx    *tc;
+	struct cryptocop_session          *sess = NULL;
+	struct cryptocop_session          *psess = NULL;
+	unsigned long int                 flags;
+	int                               i;
+	LIST_HEAD(remove_list);
+	struct list_head                  *node, *tmp;
+	struct cryptocop_prio_job         *pj;
+
+	DEBUG(printk("cryptocop_free_session: sid=%lld\n", sid));
+
+	spin_lock_irqsave(&cryptocop_sessions_lock, flags);
+	sess = cryptocop_sessions;
+	while (sess && sess->sid != sid){
+		psess = sess;
+		sess = sess->next;
+	}
+	if (sess){
+		if (psess){
+			psess->next = sess->next;
+		} else {
+			cryptocop_sessions = sess->next;
+		}
+	}
+	spin_unlock_irqrestore(&cryptocop_sessions_lock, flags);
+
+	if (!sess) return -EINVAL;
+
+	/* Remove queued jobs. */
+	spin_lock_irqsave(&cryptocop_job_queue_lock, flags);
+
+	for (i = 0; i < cryptocop_prio_no_prios; i++){
+		if (!list_empty(&(cryptocop_job_queues[i].jobs))){
+			list_for_each_safe(node, tmp, &(cryptocop_job_queues[i].jobs)) {
+				pj = list_entry(node, struct cryptocop_prio_job, node);
+				if (pj->oper->sid == sid) {
+					list_move_tail(node, &remove_list);
+				}
+			}
+		}
+	}
+	spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);
+
+	list_for_each_safe(node, tmp, &remove_list) {
+		list_del(node);
+		pj = list_entry(node, struct cryptocop_prio_job, node);
+		pj->oper->operation_status = -EAGAIN;  /* EAGAIN is not ideal for job/session terminated but it's the best choice I know of. */
+		DEBUG(printk("cryptocop_free_session: pj=0x%p, pj->oper=0x%p, pj->iop=0x%p\n", pj, pj->oper, pj->iop));
+		pj->oper->cb(pj->oper, pj->oper->cb_data);
+		delete_internal_operation(pj->iop);
+		kfree(pj);
+	}
+
+	tc = sess->tfrm_ctx;
+	/* Erase keying data. */
+	while (tc){
+		DEBUG(printk("cryptocop_free_session: memset keys, tfrm id=%d\n", tc->init.tid));
+		memset(tc->init.key, 0xff, CRYPTOCOP_MAX_KEY_LENGTH);
+		memset(tc->dec_key, 0xff, CRYPTOCOP_MAX_KEY_LENGTH);
+		tc = tc->next;
+	}
+	kfree(sess->tfrm_ctx);
+	kfree(sess);
+
+	return 0;
+}
+
+static struct cryptocop_session *get_session(cryptocop_session_id sid)
+{
+	struct cryptocop_session    *sess;
+	unsigned long int           flags;
+
+	spin_lock_irqsave(&cryptocop_sessions_lock, flags);
+	sess = cryptocop_sessions;
+	while (sess && (sess->sid != sid)){
+		sess = sess->next;
+	}
+	spin_unlock_irqrestore(&cryptocop_sessions_lock, flags);
+
+	return sess;
+}
+
+static struct cryptocop_transform_ctx *get_transform_ctx(struct cryptocop_session *sess, cryptocop_tfrm_id tid)
+{
+	struct cryptocop_transform_ctx *tc = sess->tfrm_ctx;
+
+	DEBUG(printk("get_transform_ctx, sess=0x%p, tid=%d\n", sess, tid));
+	assert(sess != NULL);
+	while (tc && tc->init.tid != tid){
+		DEBUG(printk("tc=0x%p, tc->next=0x%p\n", tc, tc->next));
+		tc = tc->next;
+	}
+	DEBUG(printk("get_transform_ctx, returning tc=0x%p\n", tc));
+	return tc;
+}
+
+
+
+/* The AES s-transform matrix (s-box). */
+static const u8 aes_sbox[256] = {
+	99,  124, 119, 123, 242, 107, 111, 197, 48,  1,   103, 43,  254, 215, 171, 118,
+	202, 130, 201, 125, 250, 89,  71,  240, 173, 212, 162, 175, 156, 164, 114, 192,
+	183, 253, 147, 38,  54,  63,  247, 204, 52,  165, 229, 241, 113, 216, 49,  21,
+	4,   199, 35,  195, 24,  150, 5,   154, 7,   18,  128, 226, 235, 39,  178, 117,
+	9,   131, 44,  26,  27,  110, 90,  160, 82,  59,  214, 179, 41,  227, 47,  132,
+	83,  209, 0,   237, 32,  252, 177, 91,  106, 203, 190, 57,  74,  76,  88,  207,
+	208, 239, 170, 251, 67,  77,  51,  133, 69,  249, 2,   127, 80,  60,  159, 168,
+	81,  163, 64,  143, 146, 157, 56,  245, 188, 182, 218, 33,  16,  255, 243, 210,
+	205, 12,  19,  236, 95,  151, 68,  23,  196, 167, 126, 61,  100, 93,  25,  115,
+	96,  129, 79,  220, 34,  42,  144, 136, 70,  238, 184, 20,  222, 94,  11,  219,
+	224, 50,  58,  10,  73,  6,   36,  92,  194, 211, 172, 98,  145, 149, 228, 121,
+	231, 200, 55,  109, 141, 213, 78,  169, 108, 86,  244, 234, 101, 122, 174, 8,
+	186, 120, 37,  46,  28,  166, 180, 198, 232, 221, 116, 31,  75,  189, 139, 138,
+	112, 62,  181, 102, 72,  3,   246, 14,  97,  53,  87,  185, 134, 193, 29,  158,
+	225, 248, 152, 17,  105, 217, 142, 148, 155, 30,  135, 233, 206, 85,  40,  223,
+	140, 161, 137, 13,  191, 230, 66,  104, 65,  153, 45,  15,  176, 84,  187, 22
+};
+
+/* AES has a 32 bit word round constants for each round in the
+ * key schedule.  round_constant[i] is really Rcon[i+1] in FIPS187.
+ */
+static u32 round_constant[11] = {
+	0x01000000, 0x02000000, 0x04000000, 0x08000000,
+	0x10000000, 0x20000000, 0x40000000, 0x80000000,
+	0x1B000000, 0x36000000, 0x6C000000
+};
+
+/* Apply the s-box to each of the four occtets in w. */
+static u32 aes_ks_subword(const u32 w)
+{
+	u8 bytes[4];
+
+	*(u32*)(&bytes[0]) = w;
+	bytes[0] = aes_sbox[bytes[0]];
+	bytes[1] = aes_sbox[bytes[1]];
+	bytes[2] = aes_sbox[bytes[2]];
+	bytes[3] = aes_sbox[bytes[3]];
+	return *(u32*)(&bytes[0]);
+}
+
+/* The encrypt (forward) Rijndael key schedule algorithm pseudo code:
+ * (Note that AES words are 32 bit long)
+ *
+ * KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk){
+ * word temp
+ * i = 0
+ * while (i < Nk) {
+ *   w[i] = word(key[4*i, 4*i + 1, 4*i + 2, 4*i + 3])
+ *   i = i + 1
+ * }
+ * i = Nk
+ *
+ * while (i < (Nb * (Nr + 1))) {
+ *   temp = w[i - 1]
+ *   if ((i mod Nk) == 0) {
+ *     temp = SubWord(RotWord(temp)) xor Rcon[i/Nk]
+ *   }
+ *   else if ((Nk > 6) && ((i mod Nk) == 4)) {
+ *     temp = SubWord(temp)
+ *   }
+ *   w[i] = w[i - Nk] xor temp
+ * }
+ * RotWord(t) does a 8 bit cyclic shift left on a 32 bit word.
+ * SubWord(t) applies the AES s-box individually to each octet
+ * in a 32 bit word.
+ *
+ * For AES Nk can have the values 4, 6, and 8 (corresponding to
+ * values for Nr of 10, 12, and 14).  Nb is always 4.
+ *
+ * To construct w[i], w[i - 1] and w[i - Nk] must be
+ * available.  Consequently we must keep a state of the last Nk words
+ * to be able to create the last round keys.
+ */
+static void get_aes_decrypt_key(unsigned char *dec_key, const unsigned  char *key, unsigned int keylength)
+{
+	u32 temp;
+	u32 w_ring[8]; /* nk is max 8, use elements 0..(nk - 1) as a ringbuffer */
+	u8  w_last_ix;
+	int i;
+	u8  nr, nk;
+
+	switch (keylength){
+	case 128:
+		nk = 4;
+		nr = 10;
+		break;
+	case 192:
+		nk = 6;
+		nr = 12;
+		break;
+	case 256:
+		nk = 8;
+		nr = 14;
+		break;
+	default:
+		panic("stream co-processor: bad aes key length in get_aes_decrypt_key\n");
+	};
+
+	/* Need to do host byte order correction here since key is byte oriented and the
+	 * kx algorithm is word (u32) oriented. */
+	for (i = 0; i < nk; i+=1) {
+		w_ring[i] = be32_to_cpu(*(u32*)&key[4*i]);
+	}
+
+	i = (int)nk;
+	w_last_ix = i - 1;
+	while (i < (4 * (nr + 2))) {
+		temp = w_ring[w_last_ix];
+		if (!(i % nk)) {
+			/* RotWord(temp) */
+			temp = (temp << 8) | (temp >> 24);
+			temp = aes_ks_subword(temp);
+			temp ^= round_constant[i/nk - 1];
+		} else if ((nk > 6) && ((i % nk) == 4)) {
+			temp = aes_ks_subword(temp);
+		}
+		w_last_ix = (w_last_ix + 1) % nk; /* This is the same as (i-Nk) mod Nk */
+		temp ^= w_ring[w_last_ix];
+		w_ring[w_last_ix] = temp;
+
+		/* We need the round keys for round Nr+1 and Nr+2 (round key
+		 * Nr+2 is the round key beyond the last one used when
+		 * encrypting).  Rounds are numbered starting from 0, Nr=10
+		 * implies 11 rounds are used in encryption/decryption.
+		 */
+		if (i >= (4 * nr)) {
+			/* Need to do host byte order correction here, the key
+			 * is byte oriented. */
+			*(u32*)dec_key = cpu_to_be32(temp);
+			dec_key += 4;
+		}
+		++i;
+	}
+}
+
+
+/**** Job/operation management. ****/
+
+int cryptocop_job_queue_insert_csum(struct cryptocop_operation *operation)
+{
+	return cryptocop_job_queue_insert(cryptocop_prio_kernel_csum, operation);
+}
+
+int cryptocop_job_queue_insert_crypto(struct cryptocop_operation *operation)
+{
+	return cryptocop_job_queue_insert(cryptocop_prio_kernel, operation);
+}
+
+int cryptocop_job_queue_insert_user_job(struct cryptocop_operation *operation)
+{
+	return cryptocop_job_queue_insert(cryptocop_prio_user, operation);
+}
+
+static int cryptocop_job_queue_insert(cryptocop_queue_priority prio, struct cryptocop_operation *operation)
+{
+	int                           ret;
+	struct cryptocop_prio_job     *pj = NULL;
+	unsigned long int             flags;
+
+	DEBUG(printk("cryptocop_job_queue_insert(%d, 0x%p)\n", prio, operation));
+
+	if (!operation || !operation->cb){
+		DEBUG_API(printk("cryptocop_job_queue_insert oper=0x%p, NULL operation or callback\n", operation));
+		return -EINVAL;
+	}
+
+	if ((ret = cryptocop_job_setup(&pj, operation)) != 0){
+		DEBUG_API(printk("cryptocop_job_queue_insert: job setup failed\n"));
+		return ret;
+	}
+	assert(pj != NULL);
+
+	spin_lock_irqsave(&cryptocop_job_queue_lock, flags);
+	list_add_tail(&pj->node, &cryptocop_job_queues[prio].jobs);
+	spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);
+
+	/* Make sure a job is running */
+	cryptocop_start_job();
+	return 0;
+}
+
+static void cryptocop_do_tasklet(unsigned long unused);
+DECLARE_TASKLET (cryptocop_tasklet, cryptocop_do_tasklet, 0);
+
+static void cryptocop_do_tasklet(unsigned long unused)
+{
+	struct list_head             *node;
+	struct cryptocop_prio_job    *pj = NULL;
+	unsigned long                flags;
+
+	DEBUG(printk("cryptocop_do_tasklet: entering\n"));
+
+	do {
+		spin_lock_irqsave(&cryptocop_completed_jobs_lock, flags);
+		if (!list_empty(&cryptocop_completed_jobs)){
+			node = cryptocop_completed_jobs.next;
+			list_del(node);
+			pj = list_entry(node, struct cryptocop_prio_job, node);
+		} else {
+			pj = NULL;
+		}
+		spin_unlock_irqrestore(&cryptocop_completed_jobs_lock, flags);
+		if (pj) {
+			assert(pj->oper != NULL);
+
+			/* Notify consumer of operation completeness. */
+			DEBUG(printk("cryptocop_do_tasklet: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));
+
+			pj->oper->operation_status = 0; /* Job is completed. */
+			pj->oper->cb(pj->oper, pj->oper->cb_data);
+			delete_internal_operation(pj->iop);
+			kfree(pj);
+		}
+	} while (pj != NULL);
+
+	DEBUG(printk("cryptocop_do_tasklet: exiting\n"));
+}
+
+static irqreturn_t
+dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct cryptocop_prio_job *done_job;
+	reg_dma_rw_ack_intr ack_intr = {
+		.data = 1,
+	};
+
+	REG_WR (dma, regi_dma9, rw_ack_intr, ack_intr);
+
+	DEBUG(printk("cryptocop DMA done\n"));
+
+	spin_lock(&running_job_lock);
+	if (cryptocop_running_job == NULL){
+		printk("stream co-processor got interrupt when not busy\n");
+		spin_unlock(&running_job_lock);
+		return IRQ_HANDLED;
+	}
+	done_job = cryptocop_running_job;
+	cryptocop_running_job = NULL;
+	spin_unlock(&running_job_lock);
+
+	/* Start processing a job. */
+	if (!spin_trylock(&cryptocop_process_lock)){
+		DEBUG(printk("cryptocop irq handler, not starting a job\n"));
+	} else {
+		cryptocop_start_job();
+		spin_unlock(&cryptocop_process_lock);
+	}
+
+	done_job->oper->operation_status = 0; /* Job is completed. */
+	if (done_job->oper->fast_callback){
+		/* This operation wants callback from interrupt. */
+		done_job->oper->cb(done_job->oper, done_job->oper->cb_data);
+		delete_internal_operation(done_job->iop);
+		kfree(done_job);
+	} else {
+		spin_lock(&cryptocop_completed_jobs_lock);
+		list_add_tail(&(done_job->node), &cryptocop_completed_jobs);
+		spin_unlock(&cryptocop_completed_jobs_lock);
+		tasklet_schedule(&cryptocop_tasklet);
+	}
+
+	DEBUG(printk("cryptocop leave irq handler\n"));
+	return IRQ_HANDLED;
+}
+
+
+/* Setup interrupts and DMA channels. */
+static int init_cryptocop(void)
+{
+	unsigned long          flags;
+	reg_intr_vect_rw_mask  intr_mask;
+	reg_dma_rw_cfg         dma_cfg = {.en = 1};
+	reg_dma_rw_intr_mask   intr_mask_in = {.data = regk_dma_yes}; /* Only want descriptor interrupts from the DMA in channel. */
+	reg_dma_rw_ack_intr    ack_intr = {.data = 1,.in_eop = 1 };
+	reg_strcop_rw_cfg      strcop_cfg = {
+		.ipend = regk_strcop_little,
+		.td1 = regk_strcop_e,
+		.td2 = regk_strcop_d,
+		.td3 = regk_strcop_e,
+		.ignore_sync = 0,
+		.en = 1
+	};
+
+	if (request_irq(DMA9_INTR_VECT, dma_done_interrupt, 0, "stream co-processor DMA", NULL)) panic("request_irq stream co-processor irq dma9");
+
+	(void)crisv32_request_dma(8, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp);
+	(void)crisv32_request_dma(9, "strcop", DMA_PANIC_ON_ERROR, 0, dma_strp);
+
+	local_irq_save(flags);
+
+	/* Reset and enable the cryptocop. */
+	strcop_cfg.en = 0;
+	REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg);
+	strcop_cfg.en = 1;
+	REG_WR(strcop, regi_strcop, rw_cfg, strcop_cfg);
+
+	/* Enable DMA9 interrupt */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.dma9 = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+
+	/* Enable DMAs. */
+	REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */
+	REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */
+
+	/* Set up wordsize = 4 for DMAs. */
+	DMA_WR_CMD (regi_dma8, regk_dma_set_w_size4);
+	DMA_WR_CMD (regi_dma9, regk_dma_set_w_size4);
+
+	/* Enable interrupts. */
+	REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in);
+
+	/* Clear intr ack. */
+	REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr);
+
+	local_irq_restore(flags);
+
+	return 0;
+}
+
+/* Free used cryptocop hw resources (interrupt and DMA channels). */
+static void release_cryptocop(void)
+{
+	unsigned long          flags;
+	reg_intr_vect_rw_mask  intr_mask;
+	reg_dma_rw_cfg         dma_cfg = {.en = 0};
+	reg_dma_rw_intr_mask   intr_mask_in = {0};
+	reg_dma_rw_ack_intr    ack_intr = {.data = 1,.in_eop = 1 };
+
+	local_irq_save(flags);
+
+	/* Clear intr ack. */
+	REG_WR(dma, regi_dma9, rw_ack_intr, ack_intr);
+
+	/* Disable DMA9 interrupt */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.dma9 = 0;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+
+	/* Disable DMAs. */
+	REG_WR(dma, regi_dma9, rw_cfg, dma_cfg); /* input DMA */
+	REG_WR(dma, regi_dma8, rw_cfg, dma_cfg); /* output DMA */
+
+	/* Disable interrupts. */
+	REG_WR(dma, regi_dma9, rw_intr_mask, intr_mask_in);
+
+	local_irq_restore(flags);
+
+	free_irq(DMA9_INTR_VECT, NULL);
+
+	(void)crisv32_free_dma(8);
+	(void)crisv32_free_dma(9);
+}
+
+
+/* Init job queue. */
+static int cryptocop_job_queue_init(void)
+{
+	int i;
+
+	INIT_LIST_HEAD(&cryptocop_completed_jobs);
+
+	for (i = 0; i < cryptocop_prio_no_prios; i++){
+		cryptocop_job_queues[i].prio = (cryptocop_queue_priority)i;
+		INIT_LIST_HEAD(&cryptocop_job_queues[i].jobs);
+	}
+	return 0;
+}
+
+
+static void cryptocop_job_queue_close(void)
+{
+	struct list_head               *node, *tmp;
+	struct cryptocop_prio_job      *pj = NULL;
+	unsigned long int              process_flags, flags;
+	int                            i;
+
+	/* FIXME: This is as yet untested code. */
+
+	/* Stop strcop from getting an operation to process while we are closing the
+	   module. */
+	spin_lock_irqsave(&cryptocop_process_lock, process_flags);
+
+	/* Empty the job queue. */
+	spin_lock_irqsave(&cryptocop_process_lock, process_flags);
+	for (i = 0; i < cryptocop_prio_no_prios; i++){
+		if (!list_empty(&(cryptocop_job_queues[i].jobs))){
+			list_for_each_safe(node, tmp, &(cryptocop_job_queues[i].jobs)) {
+				pj = list_entry(node, struct cryptocop_prio_job, node);
+				list_del(node);
+
+				/* Call callback to notify consumer of job removal. */
+				DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));
+				pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */
+				pj->oper->cb(pj->oper, pj->oper->cb_data);
+
+				delete_internal_operation(pj->iop);
+				kfree(pj);
+			}
+		}
+	}
+	spin_unlock_irqrestore(&cryptocop_process_lock, process_flags);
+
+	/* Remove the running job, if any. */
+	spin_lock_irqsave(&running_job_lock, flags);
+	if (cryptocop_running_job){
+		reg_strcop_rw_cfg rw_cfg;
+		reg_dma_rw_cfg    dma_out_cfg, dma_in_cfg;
+
+		/* Stop DMA. */
+		dma_out_cfg = REG_RD(dma, regi_dma8, rw_cfg);
+		dma_out_cfg.en = regk_dma_no;
+		REG_WR(dma, regi_dma8, rw_cfg, dma_out_cfg);
+
+		dma_in_cfg = REG_RD(dma, regi_dma9, rw_cfg);
+		dma_in_cfg.en = regk_dma_no;
+		REG_WR(dma, regi_dma9, rw_cfg, dma_in_cfg);
+
+		/* Disble the cryptocop. */
+		rw_cfg = REG_RD(strcop, regi_strcop, rw_cfg);
+		rw_cfg.en = 0;
+		REG_WR(strcop, regi_strcop, rw_cfg, rw_cfg);
+
+		pj = cryptocop_running_job;
+		cryptocop_running_job = NULL;
+
+		/* Call callback to notify consumer of job removal. */
+		DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));
+		pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */
+		pj->oper->cb(pj->oper, pj->oper->cb_data);
+
+		delete_internal_operation(pj->iop);
+		kfree(pj);
+	}
+	spin_unlock_irqrestore(&running_job_lock, flags);
+
+	/* Remove completed jobs, if any. */
+	spin_lock_irqsave(&cryptocop_completed_jobs_lock, flags);
+
+	list_for_each_safe(node, tmp, &cryptocop_completed_jobs) {
+		pj = list_entry(node, struct cryptocop_prio_job, node);
+		list_del(node);
+		/* Call callback to notify consumer of job removal. */
+		DEBUG(printk("cryptocop_job_queue_close: callback 0x%p, data 0x%p\n", pj->oper->cb, pj->oper->cb_data));
+		pj->oper->operation_status = -EINTR; /* Job is terminated without completion. */
+		pj->oper->cb(pj->oper, pj->oper->cb_data);
+
+		delete_internal_operation(pj->iop);
+		kfree(pj);
+	}
+	spin_unlock_irqrestore(&cryptocop_completed_jobs_lock, flags);
+}
+
+
+static void cryptocop_start_job(void)
+{
+	int                          i;
+	struct cryptocop_prio_job    *pj;
+	unsigned long int            flags;
+	unsigned long int            running_job_flags;
+	reg_strcop_rw_cfg            rw_cfg = {.en = 1, .ignore_sync = 0};
+
+	DEBUG(printk("cryptocop_start_job: entering\n"));
+
+	spin_lock_irqsave(&running_job_lock, running_job_flags);
+	if (cryptocop_running_job != NULL){
+		/* Already running. */
+		DEBUG(printk("cryptocop_start_job: already running, exit\n"));
+		spin_unlock_irqrestore(&running_job_lock, running_job_flags);
+		return;
+	}
+	spin_lock_irqsave(&cryptocop_job_queue_lock, flags);
+
+	/* Check the queues in priority order. */
+	for (i = cryptocop_prio_kernel_csum; (i < cryptocop_prio_no_prios) && list_empty(&cryptocop_job_queues[i].jobs); i++);
+	if (i == cryptocop_prio_no_prios) {
+		spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);
+		spin_unlock_irqrestore(&running_job_lock, running_job_flags);
+		DEBUG(printk("cryptocop_start_job: no jobs to run\n"));
+		return; /* No jobs to run */
+	}
+	DEBUG(printk("starting job for prio %d\n", i));
+
+	/* TODO: Do not starve lower priority jobs.  Let in a lower
+	 * prio job for every N-th processed higher prio job or some
+	 * other scheduling policy.  This could reasonably be
+	 * tweakable since the optimal balance would depend on the
+	 * type of load on the system. */
+
+	/* Pull the DMA lists from the job and start the DMA client. */
+	pj = list_entry(cryptocop_job_queues[i].jobs.next, struct cryptocop_prio_job, node);
+	list_del(&pj->node);
+	spin_unlock_irqrestore(&cryptocop_job_queue_lock, flags);
+	cryptocop_running_job = pj;
+
+	/* Set config register (3DES and CSUM modes). */
+	switch (pj->iop->tdes_mode){
+	case cryptocop_3des_eee:
+		rw_cfg.td1 = regk_strcop_e;
+		rw_cfg.td2 = regk_strcop_e;
+		rw_cfg.td3 = regk_strcop_e;
+		break;
+	case cryptocop_3des_eed:
+		rw_cfg.td1 = regk_strcop_e;
+		rw_cfg.td2 = regk_strcop_e;
+		rw_cfg.td3 = regk_strcop_d;
+		break;
+	case cryptocop_3des_ede:
+		rw_cfg.td1 = regk_strcop_e;
+		rw_cfg.td2 = regk_strcop_d;
+		rw_cfg.td3 = regk_strcop_e;
+		break;
+	case cryptocop_3des_edd:
+		rw_cfg.td1 = regk_strcop_e;
+		rw_cfg.td2 = regk_strcop_d;
+		rw_cfg.td3 = regk_strcop_d;
+		break;
+	case cryptocop_3des_dee:
+		rw_cfg.td1 = regk_strcop_d;
+		rw_cfg.td2 = regk_strcop_e;
+		rw_cfg.td3 = regk_strcop_e;
+		break;
+	case cryptocop_3des_ded:
+		rw_cfg.td1 = regk_strcop_d;
+		rw_cfg.td2 = regk_strcop_e;
+		rw_cfg.td3 = regk_strcop_d;
+		break;
+	case cryptocop_3des_dde:
+		rw_cfg.td1 = regk_strcop_d;
+		rw_cfg.td2 = regk_strcop_d;
+		rw_cfg.td3 = regk_strcop_e;
+		break;
+	case cryptocop_3des_ddd:
+		rw_cfg.td1 = regk_strcop_d;
+		rw_cfg.td2 = regk_strcop_d;
+		rw_cfg.td3 = regk_strcop_d;
+		break;
+	default:
+		DEBUG(printk("cryptocop_setup_dma_list: bad 3DES mode\n"));
+	}
+	switch (pj->iop->csum_mode){
+	case cryptocop_csum_le:
+		rw_cfg.ipend = regk_strcop_little;
+		break;
+	case cryptocop_csum_be:
+		rw_cfg.ipend = regk_strcop_big;
+		break;
+	default:
+		DEBUG(printk("cryptocop_setup_dma_list: bad checksum mode\n"));
+	}
+	REG_WR(strcop, regi_strcop, rw_cfg, rw_cfg);
+
+	DEBUG(printk("cryptocop_start_job: starting DMA, new cryptocop_running_job=0x%p\n"
+		     "ctx_in: 0x%p, phys: 0x%p\n"
+		     "ctx_out: 0x%p, phys: 0x%p\n",
+		     pj,
+		     &pj->iop->ctx_in, (char*)virt_to_phys(&pj->iop->ctx_in),
+		     &pj->iop->ctx_out, (char*)virt_to_phys(&pj->iop->ctx_out)));
+
+	/* Start input DMA. */
+	DMA_START_CONTEXT(regi_dma9, virt_to_phys(&pj->iop->ctx_in));
+
+	/* Start output DMA. */
+	DMA_START_CONTEXT(regi_dma8, virt_to_phys(&pj->iop->ctx_out));
+
+	spin_unlock_irqrestore(&running_job_lock, running_job_flags);
+	DEBUG(printk("cryptocop_start_job: exiting\n"));
+}
+
+
+static int cryptocop_job_setup(struct cryptocop_prio_job **pj, struct cryptocop_operation *operation)
+{
+	int  err;
+	int  alloc_flag = operation->in_interrupt ? GFP_ATOMIC : GFP_KERNEL;
+	void *iop_alloc_ptr = NULL;
+
+	*pj = kmalloc(sizeof (struct cryptocop_prio_job), alloc_flag);
+	if (!*pj) return -ENOMEM;
+
+	DEBUG(printk("cryptocop_job_setup: operation=0x%p\n", operation));
+
+	(*pj)->oper = operation;
+	DEBUG(printk("cryptocop_job_setup, cb=0x%p cb_data=0x%p\n",  (*pj)->oper->cb, (*pj)->oper->cb_data));
+
+	if (operation->use_dmalists) {
+		DEBUG(print_user_dma_lists(&operation->list_op));
+		if (!operation->list_op.inlist || !operation->list_op.outlist || !operation->list_op.out_data_buf || !operation->list_op.in_data_buf){
+			DEBUG_API(printk("cryptocop_job_setup: bad indata (use_dmalists)\n"));
+			kfree(*pj);
+			return -EINVAL;
+		}
+		iop_alloc_ptr = kmalloc(DESCR_ALLOC_PAD + sizeof(struct cryptocop_int_operation), alloc_flag);
+		if (!iop_alloc_ptr) {
+			DEBUG_API(printk("cryptocop_job_setup: kmalloc cryptocop_int_operation\n"));
+			kfree(*pj);
+			return -ENOMEM;
+		}
+		(*pj)->iop = (struct cryptocop_int_operation*)(((unsigned long int)(iop_alloc_ptr + DESCR_ALLOC_PAD + offsetof(struct cryptocop_int_operation, ctx_out)) & ~0x0000001F) - offsetof(struct cryptocop_int_operation, ctx_out));
+		DEBUG(memset((*pj)->iop, 0xff, sizeof(struct cryptocop_int_operation)));
+		(*pj)->iop->alloc_ptr = iop_alloc_ptr;
+		(*pj)->iop->sid = operation->sid;
+		(*pj)->iop->cdesc_out = NULL;
+		(*pj)->iop->cdesc_in = NULL;
+		(*pj)->iop->tdes_mode = operation->list_op.tdes_mode;
+		(*pj)->iop->csum_mode = operation->list_op.csum_mode;
+		(*pj)->iop->ddesc_out = operation->list_op.outlist;
+		(*pj)->iop->ddesc_in = operation->list_op.inlist;
+
+		/* Setup DMA contexts. */
+		(*pj)->iop->ctx_out.next = NULL;
+		(*pj)->iop->ctx_out.eol = 1;
+		(*pj)->iop->ctx_out.saved_data = operation->list_op.outlist;
+		(*pj)->iop->ctx_out.saved_data_buf = operation->list_op.out_data_buf;
+
+		(*pj)->iop->ctx_in.next = NULL;
+		(*pj)->iop->ctx_in.eol = 1;
+		(*pj)->iop->ctx_in.saved_data = operation->list_op.inlist;
+		(*pj)->iop->ctx_in.saved_data_buf = operation->list_op.in_data_buf;
+	} else {
+		if ((err = cryptocop_setup_dma_list(operation, &(*pj)->iop, alloc_flag))) {
+			DEBUG_API(printk("cryptocop_job_setup: cryptocop_setup_dma_list failed %d\n", err));
+			kfree(*pj);
+			return err;
+		}
+	}
+	DEBUG(print_dma_descriptors((*pj)->iop));
+
+	DEBUG(printk("cryptocop_job_setup, DMA list setup successful\n"));
+
+	return 0;
+}
+
+
+static int cryptocop_open(struct inode *inode, struct file *filp)
+{
+	int p = MINOR(inode->i_rdev);
+
+	if (p != CRYPTOCOP_MINOR) return -EINVAL;
+
+	filp->private_data = NULL;
+	return 0;
+}
+
+
+static int cryptocop_release(struct inode *inode, struct file *filp)
+{
+	struct cryptocop_private *dev = filp->private_data;
+	struct cryptocop_private *dev_next;
+
+	while (dev){
+		dev_next = dev->next;
+		if (dev->sid != CRYPTOCOP_SESSION_ID_NONE) {
+			(void)cryptocop_free_session(dev->sid);
+		}
+		kfree(dev);
+		dev = dev_next;
+	}
+
+	return 0;
+}
+
+
+static int cryptocop_ioctl_close_session(struct inode *inode, struct file *filp,
+					 unsigned int cmd, unsigned long arg)
+{
+	struct cryptocop_private  *dev = filp->private_data;
+	struct cryptocop_private  *prev_dev = NULL;
+	struct strcop_session_op  *sess_op = (struct strcop_session_op *)arg;
+	struct strcop_session_op  sop;
+	int                       err;
+
+	DEBUG(printk("cryptocop_ioctl_close_session\n"));
+
+	if (!access_ok(VERIFY_READ, sess_op, sizeof(struct strcop_session_op)))
+		return -EFAULT;
+	err = copy_from_user(&sop, sess_op, sizeof(struct strcop_session_op));
+	if (err) return -EFAULT;
+
+	while (dev && (dev->sid != sop.ses_id)) {
+		prev_dev = dev;
+		dev = dev->next;
+	}
+	if (dev){
+		if (prev_dev){
+			prev_dev->next = dev->next;
+		} else {
+			filp->private_data = dev->next;
+		}
+		err = cryptocop_free_session(dev->sid);
+		if (err) return -EFAULT;
+	} else {
+		DEBUG_API(printk("cryptocop_ioctl_close_session: session %lld not found\n", sop.ses_id));
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+static void ioctl_process_job_callback(struct cryptocop_operation *op, void*cb_data)
+{
+	struct ioctl_job_cb_ctx *jc = (struct ioctl_job_cb_ctx *)cb_data;
+
+	DEBUG(printk("ioctl_process_job_callback: op=0x%p, cb_data=0x%p\n", op, cb_data));
+
+	jc->processed = 1;
+	wake_up(&cryptocop_ioc_process_wq);
+}
+
+
+#define CRYPTOCOP_IOCTL_CIPHER_TID  (1)
+#define CRYPTOCOP_IOCTL_DIGEST_TID  (2)
+#define CRYPTOCOP_IOCTL_CSUM_TID    (3)
+
+static size_t first_cfg_change_ix(struct strcop_crypto_op *crp_op)
+{
+	size_t ch_ix = 0;
+
+	if (crp_op->do_cipher) ch_ix = crp_op->cipher_start;
+	if (crp_op->do_digest && (crp_op->digest_start < ch_ix)) ch_ix = crp_op->digest_start;
+	if (crp_op->do_csum && (crp_op->csum_start < ch_ix)) ch_ix = crp_op->csum_start;
+
+	DEBUG(printk("first_cfg_change_ix: ix=%d\n", ch_ix));
+	return ch_ix;
+}
+
+
+static size_t next_cfg_change_ix(struct strcop_crypto_op *crp_op, size_t ix)
+{
+	size_t ch_ix = INT_MAX;
+	size_t tmp_ix = 0;
+
+	if (crp_op->do_cipher && ((crp_op->cipher_start + crp_op->cipher_len) > ix)){
+		if (crp_op->cipher_start > ix) {
+			ch_ix = crp_op->cipher_start;
+		} else {
+			ch_ix = crp_op->cipher_start + crp_op->cipher_len;
+		}
+	}
+	if (crp_op->do_digest && ((crp_op->digest_start + crp_op->digest_len) > ix)){
+		if (crp_op->digest_start > ix) {
+			tmp_ix = crp_op->digest_start;
+		} else {
+			tmp_ix = crp_op->digest_start + crp_op->digest_len;
+		}
+		if (tmp_ix < ch_ix) ch_ix = tmp_ix;
+	}
+	if (crp_op->do_csum && ((crp_op->csum_start + crp_op->csum_len) > ix)){
+		if (crp_op->csum_start > ix) {
+			tmp_ix = crp_op->csum_start;
+		} else {
+			tmp_ix = crp_op->csum_start + crp_op->csum_len;
+		}
+		if (tmp_ix < ch_ix) ch_ix = tmp_ix;
+	}
+	if (ch_ix == INT_MAX) ch_ix = ix;
+	DEBUG(printk("next_cfg_change_ix prev ix=%d, next ix=%d\n", ix, ch_ix));
+	return ch_ix;
+}
+
+
+/* Map map_length bytes from the pages starting on *pageix and *pageoffset to iovecs starting on *iovix.
+ * Return -1 for ok, 0 for fail. */
+static int map_pages_to_iovec(struct iovec *iov, int iovlen, int *iovix, struct page **pages, int nopages, int *pageix, int *pageoffset, int map_length )
+{
+	int tmplen;
+
+	assert(iov != NULL);
+	assert(iovix != NULL);
+	assert(pages != NULL);
+	assert(pageix != NULL);
+	assert(pageoffset != NULL);
+
+	DEBUG(printk("map_pages_to_iovec, map_length=%d, iovlen=%d, *iovix=%d, nopages=%d, *pageix=%d, *pageoffset=%d\n", map_length, iovlen, *iovix, nopages, *pageix, *pageoffset));
+
+	while (map_length > 0){
+		DEBUG(printk("map_pages_to_iovec, map_length=%d, iovlen=%d, *iovix=%d, nopages=%d, *pageix=%d, *pageoffset=%d\n", map_length, iovlen, *iovix, nopages, *pageix, *pageoffset));
+		if (*iovix >= iovlen){
+			DEBUG_API(printk("map_page_to_iovec: *iovix=%d >= iovlen=%d\n", *iovix, iovlen));
+			return 0;
+		}
+		if (*pageix >= nopages){
+			DEBUG_API(printk("map_page_to_iovec: *pageix=%d >= nopages=%d\n", *pageix, nopages));
+			return 0;
+		}
+		iov[*iovix].iov_base = (unsigned char*)page_address(pages[*pageix]) + *pageoffset;
+		tmplen = PAGE_SIZE - *pageoffset;
+		if (tmplen < map_length){
+			(*pageoffset) = 0;
+			(*pageix)++;
+		} else {
+			tmplen = map_length;
+			(*pageoffset) += map_length;
+		}
+		DEBUG(printk("mapping %d bytes from page %d (or %d) to iovec %d\n", tmplen, *pageix, *pageix-1, *iovix));
+		iov[*iovix].iov_len = tmplen;
+		map_length -= tmplen;
+		(*iovix)++;
+	}
+	DEBUG(printk("map_page_to_iovec, exit, *iovix=%d\n", *iovix));
+	return -1;
+}
+
+
+
+static int cryptocop_ioctl_process(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	int                             i;
+	struct cryptocop_private        *dev = filp->private_data;
+	struct strcop_crypto_op         *crp_oper = (struct strcop_crypto_op *)arg;
+	struct strcop_crypto_op         oper = {0};
+	int                             err = 0;
+	struct cryptocop_operation      *cop = NULL;
+
+	struct ioctl_job_cb_ctx         *jc = NULL;
+
+	struct page                     **inpages = NULL;
+	struct page                     **outpages = NULL;
+	int                             noinpages = 0;
+	int                             nooutpages = 0;
+
+	struct cryptocop_desc           descs[5]; /* Max 5 descriptors are needed, there are three transforms that
+						   * can get connected/disconnected on different places in the indata. */
+	struct cryptocop_desc_cfg       dcfgs[5*3];
+	int                             desc_ix = 0;
+	int                             dcfg_ix = 0;
+	struct cryptocop_tfrm_cfg       ciph_tcfg = {0};
+	struct cryptocop_tfrm_cfg       digest_tcfg = {0};
+	struct cryptocop_tfrm_cfg       csum_tcfg = {0};
+
+	unsigned char                   *digest_result = NULL;
+	int                             digest_length = 0;
+	int                             cblocklen = 0;
+	unsigned char                   csum_result[CSUM_BLOCK_LENGTH];
+	struct cryptocop_session        *sess;
+
+	int    iovlen = 0;
+	int    iovix = 0;
+	int    pageix = 0;
+	int    pageoffset = 0;
+
+	size_t prev_ix = 0;
+	size_t next_ix;
+
+	int    cipher_active, digest_active, csum_active;
+	int    end_digest, end_csum;
+	int    digest_done = 0;
+	int    cipher_done = 0;
+	int    csum_done = 0;
+
+	DEBUG(printk("cryptocop_ioctl_process\n"));
+
+	if (!access_ok(VERIFY_WRITE, crp_oper, sizeof(struct strcop_crypto_op))){
+		DEBUG_API(printk("cryptocop_ioctl_process: !access_ok crp_oper!\n"));
+		return -EFAULT;
+	}
+	if (copy_from_user(&oper, crp_oper, sizeof(struct strcop_crypto_op))) {
+		DEBUG_API(printk("cryptocop_ioctl_process: copy_from_user\n"));
+		return -EFAULT;
+	}
+	DEBUG(print_strcop_crypto_op(&oper));
+
+	while (dev && dev->sid != oper.ses_id) dev = dev->next;
+	if (!dev){
+		DEBUG_API(printk("cryptocop_ioctl_process: session %lld not found\n", oper.ses_id));
+		return -EINVAL;
+	}
+
+	/* Check buffers. */
+	if (((oper.indata + oper.inlen) < oper.indata) || ((oper.cipher_outdata + oper.cipher_outlen) < oper.cipher_outdata)){
+		DEBUG_API(printk("cryptocop_ioctl_process: user buffers wrapped around, bad user!\n"));
+		return -EINVAL;
+	}
+
+	if (!access_ok(VERIFY_WRITE, oper.cipher_outdata, oper.cipher_outlen)){
+		DEBUG_API(printk("cryptocop_ioctl_process: !access_ok out data!\n"));
+		return -EFAULT;
+	}
+	if (!access_ok(VERIFY_READ, oper.indata, oper.inlen)){
+		DEBUG_API(printk("cryptocop_ioctl_process: !access_ok in data!\n"));
+		return -EFAULT;
+	}
+
+	cop = kmalloc(sizeof(struct cryptocop_operation), GFP_KERNEL);
+	if (!cop) {
+		DEBUG_API(printk("cryptocop_ioctl_process: kmalloc\n"));
+		return -ENOMEM;
+	}
+	jc = kmalloc(sizeof(struct ioctl_job_cb_ctx), GFP_KERNEL);
+	if (!jc) {
+		DEBUG_API(printk("cryptocop_ioctl_process: kmalloc\n"));
+		err = -ENOMEM;
+		goto error_cleanup;
+	}
+	jc->processed = 0;
+
+	cop->cb_data = jc;
+	cop->cb = ioctl_process_job_callback;
+	cop->operation_status = 0;
+	cop->use_dmalists = 0;
+	cop->in_interrupt = 0;
+	cop->fast_callback = 0;
+	cop->tfrm_op.tfrm_cfg = NULL;
+	cop->tfrm_op.desc = NULL;
+	cop->tfrm_op.indata = NULL;
+	cop->tfrm_op.incount = 0;
+	cop->tfrm_op.inlen = 0;
+	cop->tfrm_op.outdata = NULL;
+	cop->tfrm_op.outcount = 0;
+	cop->tfrm_op.outlen = 0;
+
+	sess = get_session(oper.ses_id);
+	if (!sess){
+		DEBUG_API(printk("cryptocop_ioctl_process: bad session id.\n"));
+		kfree(cop);
+		kfree(jc);
+		return -EINVAL;
+	}
+
+	if (oper.do_cipher) {
+		unsigned int                    cipher_outlen = 0;
+		struct cryptocop_transform_ctx  *tc = get_transform_ctx(sess, CRYPTOCOP_IOCTL_CIPHER_TID);
+		if (!tc) {
+			DEBUG_API(printk("cryptocop_ioctl_process: no cipher transform in session.\n"));
+			err = -EINVAL;
+			goto error_cleanup;
+		}
+		ciph_tcfg.tid = CRYPTOCOP_IOCTL_CIPHER_TID;
+		ciph_tcfg.inject_ix = 0;
+		ciph_tcfg.flags = 0;
+		if ((oper.cipher_start < 0) || (oper.cipher_len <= 0) || (oper.cipher_start > oper.inlen) || ((oper.cipher_start + oper.cipher_len) > oper.inlen)){
+			DEBUG_API(printk("cryptocop_ioctl_process: bad cipher length\n"));
+			kfree(cop);
+			kfree(jc);
+			return -EINVAL;
+		}
+		cblocklen = tc->init.alg == cryptocop_alg_aes ? AES_BLOCK_LENGTH : DES_BLOCK_LENGTH;
+		if (oper.cipher_len % cblocklen) {
+			kfree(cop);
+			kfree(jc);
+			DEBUG_API(printk("cryptocop_ioctl_process: cipher inlength not multiple of block length.\n"));
+			return -EINVAL;
+		}
+		cipher_outlen = oper.cipher_len;
+		if (tc->init.cipher_mode == cryptocop_cipher_mode_cbc){
+			if (oper.cipher_explicit) {
+				ciph_tcfg.flags |= CRYPTOCOP_EXPLICIT_IV;
+				memcpy(ciph_tcfg.iv, oper.cipher_iv, cblocklen);
+			} else {
+				cipher_outlen = oper.cipher_len - cblocklen;
+			}
+		} else {
+			if (oper.cipher_explicit){
+				kfree(cop);
+				kfree(jc);
+				DEBUG_API(printk("cryptocop_ioctl_process: explicit_iv when not CBC mode\n"));
+				return -EINVAL;
+			}
+		}
+		if (oper.cipher_outlen != cipher_outlen) {
+			kfree(cop);
+			kfree(jc);
+			DEBUG_API(printk("cryptocop_ioctl_process: cipher_outlen incorrect, should be %d not %d.\n", cipher_outlen, oper.cipher_outlen));
+			return -EINVAL;
+		}
+
+		if (oper.decrypt){
+			ciph_tcfg.flags |= CRYPTOCOP_DECRYPT;
+		} else {
+			ciph_tcfg.flags |= CRYPTOCOP_ENCRYPT;
+		}
+		ciph_tcfg.next = cop->tfrm_op.tfrm_cfg;
+		cop->tfrm_op.tfrm_cfg = &ciph_tcfg;
+	}
+	if (oper.do_digest){
+		struct cryptocop_transform_ctx *tc = get_transform_ctx(sess, CRYPTOCOP_IOCTL_DIGEST_TID);
+		if (!tc) {
+			DEBUG_API(printk("cryptocop_ioctl_process: no digest transform in session.\n"));
+			err = -EINVAL;
+			goto error_cleanup;
+		}
+		digest_length = tc->init.alg == cryptocop_alg_md5 ? 16 : 20;
+		digest_result = kmalloc(digest_length, GFP_KERNEL);
+		if (!digest_result) {
+			DEBUG_API(printk("cryptocop_ioctl_process: kmalloc digest_result\n"));
+			err = -EINVAL;
+			goto error_cleanup;
+		}
+		DEBUG(memset(digest_result, 0xff, digest_length));
+
+		digest_tcfg.tid = CRYPTOCOP_IOCTL_DIGEST_TID;
+		digest_tcfg.inject_ix = 0;
+		ciph_tcfg.inject_ix += digest_length;
+		if ((oper.digest_start < 0) || (oper.digest_len <= 0) || (oper.digest_start > oper.inlen) || ((oper.digest_start + oper.digest_len) > oper.inlen)){
+			DEBUG_API(printk("cryptocop_ioctl_process: bad digest length\n"));
+			err = -EINVAL;
+			goto error_cleanup;
+		}
+
+		digest_tcfg.next = cop->tfrm_op.tfrm_cfg;
+		cop->tfrm_op.tfrm_cfg = &digest_tcfg;
+	}
+	if (oper.do_csum){
+		csum_tcfg.tid = CRYPTOCOP_IOCTL_CSUM_TID;
+		csum_tcfg.inject_ix = digest_length;
+		ciph_tcfg.inject_ix += 2;
+
+		if ((oper.csum_start < 0) || (oper.csum_len <= 0) || (oper.csum_start > oper.inlen) || ((oper.csum_start + oper.csum_len) > oper.inlen)){
+			DEBUG_API(printk("cryptocop_ioctl_process: bad csum length\n"));
+			kfree(cop);
+			kfree(jc);
+			return -EINVAL;
+		}
+
+		csum_tcfg.next = cop->tfrm_op.tfrm_cfg;
+		cop->tfrm_op.tfrm_cfg = &csum_tcfg;
+	}
+
+	prev_ix = first_cfg_change_ix(&oper);
+	if (prev_ix > oper.inlen) {
+		DEBUG_API(printk("cryptocop_ioctl_process: length mismatch\n"));
+		nooutpages = noinpages = 0;
+		err = -EINVAL;
+		goto error_cleanup;
+	}
+	DEBUG(printk("cryptocop_ioctl_process: inlen=%d, cipher_outlen=%d\n", oper.inlen, oper.cipher_outlen));
+
+	/* Map user pages for in and out data of the operation. */
+	noinpages = (((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK) + oper.inlen - 1 - prev_ix + ~PAGE_MASK) >> PAGE_SHIFT;
+	DEBUG(printk("cryptocop_ioctl_process: noinpages=%d\n", noinpages));
+	inpages = kmalloc(noinpages * sizeof(struct page*), GFP_KERNEL);
+	if (!inpages){
+		DEBUG_API(printk("cryptocop_ioctl_process: kmalloc inpages\n"));
+		nooutpages = noinpages = 0;
+		err = -ENOMEM;
+		goto error_cleanup;
+	}
+	if (oper.do_cipher){
+		nooutpages = (((unsigned long int)oper.cipher_outdata & ~PAGE_MASK) + oper.cipher_outlen - 1 + ~PAGE_MASK) >> PAGE_SHIFT;
+		DEBUG(printk("cryptocop_ioctl_process: nooutpages=%d\n", nooutpages));
+		outpages = kmalloc(nooutpages * sizeof(struct page*), GFP_KERNEL);
+		if (!outpages){
+			DEBUG_API(printk("cryptocop_ioctl_process: kmalloc outpages\n"));
+			nooutpages = noinpages = 0;
+			err = -ENOMEM;
+			goto error_cleanup;
+		}
+	}
+
+	/* Acquire the mm page semaphore. */
+	down_read(&current->mm->mmap_sem);
+
+	err = get_user_pages(current,
+			     current->mm,
+			     (unsigned long int)(oper.indata + prev_ix),
+			     noinpages,
+			     0,  /* read access only for in data */
+			     0, /* no force */
+			     inpages,
+			     NULL);
+
+	if (err < 0) {
+		up_read(&current->mm->mmap_sem);
+		nooutpages = noinpages = 0;
+		DEBUG_API(printk("cryptocop_ioctl_process: get_user_pages indata\n"));
+		goto error_cleanup;
+	}
+	noinpages = err;
+	if (oper.do_cipher){
+		err = get_user_pages(current,
+				     current->mm,
+				     (unsigned long int)oper.cipher_outdata,
+				     nooutpages,
+				     1, /* write access for out data */
+				     0, /* no force */
+				     outpages,
+				     NULL);
+		up_read(&current->mm->mmap_sem);
+		if (err < 0) {
+			nooutpages = 0;
+			DEBUG_API(printk("cryptocop_ioctl_process: get_user_pages outdata\n"));
+			goto error_cleanup;
+		}
+		nooutpages = err;
+	} else {
+		up_read(&current->mm->mmap_sem);
+	}
+
+	/* Add 6 to nooutpages to make room for possibly inserted buffers for storing digest and
+	 * csum output and splits when units are (dis-)connected. */
+	cop->tfrm_op.indata = kmalloc((noinpages) * sizeof(struct iovec), GFP_KERNEL);
+	cop->tfrm_op.outdata = kmalloc((6 + nooutpages) * sizeof(struct iovec), GFP_KERNEL);
+	if (!cop->tfrm_op.indata || !cop->tfrm_op.outdata) {
+		DEBUG_API(printk("cryptocop_ioctl_process: kmalloc iovecs\n"));
+		err = -ENOMEM;
+		goto error_cleanup;
+	}
+
+	cop->tfrm_op.inlen = oper.inlen - prev_ix;
+	cop->tfrm_op.outlen = 0;
+	if (oper.do_cipher) cop->tfrm_op.outlen += oper.cipher_outlen;
+	if (oper.do_digest) cop->tfrm_op.outlen += digest_length;
+	if (oper.do_csum) cop->tfrm_op.outlen += 2;
+
+	/* Setup the in iovecs. */
+	cop->tfrm_op.incount = noinpages;
+	if (noinpages > 1){
+		size_t tmplen = cop->tfrm_op.inlen;
+
+		cop->tfrm_op.indata[0].iov_len = PAGE_SIZE - ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK);
+		cop->tfrm_op.indata[0].iov_base = (unsigned char*)page_address(inpages[0]) + ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK);
+		tmplen -= cop->tfrm_op.indata[0].iov_len;
+		for (i = 1; i<noinpages; i++){
+			cop->tfrm_op.indata[i].iov_len = tmplen < PAGE_SIZE ? tmplen : PAGE_SIZE;
+			cop->tfrm_op.indata[i].iov_base = (unsigned char*)page_address(inpages[i]);
+			tmplen -= PAGE_SIZE;
+		}
+	} else {
+		cop->tfrm_op.indata[0].iov_len = oper.inlen - prev_ix;
+		cop->tfrm_op.indata[0].iov_base = (unsigned char*)page_address(inpages[0]) + ((unsigned long int)(oper.indata + prev_ix) & ~PAGE_MASK);
+	}
+
+	iovlen = nooutpages + 6;
+	pageoffset = oper.do_cipher ? ((unsigned long int)oper.cipher_outdata & ~PAGE_MASK) : 0;
+
+	next_ix = next_cfg_change_ix(&oper, prev_ix);
+	if (prev_ix == next_ix){
+		DEBUG_API(printk("cryptocop_ioctl_process: length configuration broken.\n"));
+		err = -EINVAL;  /* This should be impossible barring bugs. */
+		goto error_cleanup;
+	}
+	while (prev_ix != next_ix){
+		end_digest = end_csum = cipher_active = digest_active = csum_active = 0;
+		descs[desc_ix].cfg = NULL;
+		descs[desc_ix].length = next_ix - prev_ix;
+
+		if (oper.do_cipher && (oper.cipher_start < next_ix) && (prev_ix < (oper.cipher_start + oper.cipher_len))) {
+			dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_CIPHER_TID;
+			dcfgs[dcfg_ix].src = cryptocop_source_dma;
+			cipher_active = 1;
+
+			if (next_ix == (oper.cipher_start + oper.cipher_len)){
+				cipher_done = 1;
+				dcfgs[dcfg_ix].last = 1;
+			} else {
+				dcfgs[dcfg_ix].last = 0;
+			}
+			dcfgs[dcfg_ix].next = descs[desc_ix].cfg;
+			descs[desc_ix].cfg = &dcfgs[dcfg_ix];
+			++dcfg_ix;
+		}
+		if (oper.do_digest && (oper.digest_start < next_ix) && (prev_ix < (oper.digest_start + oper.digest_len))) {
+			digest_active = 1;
+			dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_DIGEST_TID;
+			dcfgs[dcfg_ix].src = cryptocop_source_dma;
+			if (next_ix == (oper.digest_start + oper.digest_len)){
+				assert(!digest_done);
+				digest_done = 1;
+				dcfgs[dcfg_ix].last = 1;
+			} else {
+				dcfgs[dcfg_ix].last = 0;
+			}
+			dcfgs[dcfg_ix].next = descs[desc_ix].cfg;
+			descs[desc_ix].cfg = &dcfgs[dcfg_ix];
+			++dcfg_ix;
+		}
+		if (oper.do_csum && (oper.csum_start < next_ix) && (prev_ix < (oper.csum_start + oper.csum_len))){
+			csum_active = 1;
+			dcfgs[dcfg_ix].tid = CRYPTOCOP_IOCTL_CSUM_TID;
+			dcfgs[dcfg_ix].src = cryptocop_source_dma;
+			if (next_ix == (oper.csum_start + oper.csum_len)){
+				csum_done = 1;
+				dcfgs[dcfg_ix].last = 1;
+			} else {
+				dcfgs[dcfg_ix].last = 0;
+			}
+			dcfgs[dcfg_ix].next = descs[desc_ix].cfg;
+			descs[desc_ix].cfg = &dcfgs[dcfg_ix];
+			++dcfg_ix;
+		}
+		if (!descs[desc_ix].cfg){
+			DEBUG_API(printk("cryptocop_ioctl_process: data segment %d (%d to %d) had no active transforms\n", desc_ix, prev_ix, next_ix));
+			err = -EINVAL;
+			goto error_cleanup;
+		}
+		descs[desc_ix].next = &(descs[desc_ix]) + 1;
+		++desc_ix;
+		prev_ix = next_ix;
+		next_ix = next_cfg_change_ix(&oper, prev_ix);
+	}
+	if (desc_ix > 0){
+		descs[desc_ix-1].next = NULL;
+	} else {
+		descs[0].next = NULL;
+	}
+	if (oper.do_digest) {
+		DEBUG(printk("cryptocop_ioctl_process: mapping %d byte digest output to iovec %d\n", digest_length, iovix));
+		/* Add outdata iovec, length == <length of type of digest> */
+		cop->tfrm_op.outdata[iovix].iov_base = digest_result;
+		cop->tfrm_op.outdata[iovix].iov_len = digest_length;
+		++iovix;
+	}
+	if (oper.do_csum) {
+		/* Add outdata iovec, length == 2, the length of csum. */
+		DEBUG(printk("cryptocop_ioctl_process: mapping 2 byte csum output to iovec %d\n", iovix));
+		/* Add outdata iovec, length == <length of type of digest> */
+		cop->tfrm_op.outdata[iovix].iov_base = csum_result;
+		cop->tfrm_op.outdata[iovix].iov_len = 2;
+		++iovix;
+	}
+	if (oper.do_cipher) {
+		if (!map_pages_to_iovec(cop->tfrm_op.outdata, iovlen, &iovix, outpages, nooutpages, &pageix, &pageoffset, oper.cipher_outlen)){
+			DEBUG_API(printk("cryptocop_ioctl_process: failed to map pages to iovec.\n"));
+			err = -ENOSYS; /* This should be impossible barring bugs. */
+			goto error_cleanup;
+		}
+	}
+	DEBUG(printk("cryptocop_ioctl_process: setting cop->tfrm_op.outcount %d\n", iovix));
+	cop->tfrm_op.outcount = iovix;
+	assert(iovix <= (nooutpages + 6));
+
+	cop->sid = oper.ses_id;
+	cop->tfrm_op.desc = &descs[0];
+
+	DEBUG(printk("cryptocop_ioctl_process: inserting job, cb_data=0x%p\n", cop->cb_data));
+
+	if ((err = cryptocop_job_queue_insert_user_job(cop)) != 0) {
+		DEBUG_API(printk("cryptocop_ioctl_process: insert job %d\n", err));
+		err = -EINVAL;
+		goto error_cleanup;
+	}
+
+	DEBUG(printk("cryptocop_ioctl_process: begin wait for result\n"));
+
+	wait_event(cryptocop_ioc_process_wq, (jc->processed != 0));
+	DEBUG(printk("cryptocop_ioctl_process: end wait for result\n"));
+        if (!jc->processed){
+		printk(KERN_WARNING "cryptocop_ioctl_process: job not processed at completion\n");
+		err = -EIO;
+		goto error_cleanup;
+	}
+
+	/* Job process done.  Cipher output should already be correct in job so no post processing of outdata. */
+	DEBUG(printk("cryptocop_ioctl_process: operation_status = %d\n", cop->operation_status));
+	if (cop->operation_status == 0){
+		if (oper.do_digest){
+			DEBUG(printk("cryptocop_ioctl_process: copy %d bytes digest to user\n", digest_length));
+			err = copy_to_user((unsigned char*)crp_oper + offsetof(struct strcop_crypto_op, digest), digest_result, digest_length);
+			if (0 != err){
+				DEBUG_API(printk("cryptocop_ioctl_process: copy_to_user, digest length %d, err %d\n", digest_length, err));
+				err = -EFAULT;
+				goto error_cleanup;
+			}
+		}
+		if (oper.do_csum){
+			DEBUG(printk("cryptocop_ioctl_process: copy 2 bytes checksum to user\n"));
+			err = copy_to_user((unsigned char*)crp_oper + offsetof(struct strcop_crypto_op, csum), csum_result, 2);
+			if (0 != err){
+				DEBUG_API(printk("cryptocop_ioctl_process: copy_to_user, csum, err %d\n", err));
+				err = -EFAULT;
+				goto error_cleanup;
+			}
+		}
+		err = 0;
+	} else {
+		DEBUG(printk("cryptocop_ioctl_process: returning err = operation_status = %d\n", cop->operation_status));
+		err = cop->operation_status;
+	}
+
+ error_cleanup:
+	/* Release page caches. */
+	for (i = 0; i < noinpages; i++){
+		put_page(inpages[i]);
+	}
+	for (i = 0; i < nooutpages; i++){
+		int spdl_err;
+		/* Mark output pages dirty. */
+		spdl_err = set_page_dirty_lock(outpages[i]);
+		DEBUG(if (spdl_err)printk("cryptocop_ioctl_process: set_page_dirty_lock returned %d\n", spdl_err));
+	}
+	for (i = 0; i < nooutpages; i++){
+		put_page(outpages[i]);
+	}
+
+	if (digest_result) kfree(digest_result);
+	if (inpages) kfree(inpages);
+	if (outpages) kfree(outpages);
+	if (cop){
+		if (cop->tfrm_op.indata) kfree(cop->tfrm_op.indata);
+		if (cop->tfrm_op.outdata) kfree(cop->tfrm_op.outdata);
+		kfree(cop);
+	}
+	if (jc) kfree(jc);
+
+	DEBUG(print_lock_status());
+
+	return err;
+}
+
+
+static int cryptocop_ioctl_create_session(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	cryptocop_session_id             sid;
+	int                              err;
+	struct cryptocop_private         *dev;
+	struct strcop_session_op         *sess_op = (struct strcop_session_op *)arg;
+	struct strcop_session_op         sop;
+	struct cryptocop_transform_init  *tis = NULL;
+	struct cryptocop_transform_init  ti_cipher = {0};
+	struct cryptocop_transform_init  ti_digest = {0};
+	struct cryptocop_transform_init  ti_csum = {0};
+
+	if (!access_ok(VERIFY_WRITE, sess_op, sizeof(struct strcop_session_op)))
+		return -EFAULT;
+	err = copy_from_user(&sop, sess_op, sizeof(struct strcop_session_op));
+	if (err) return -EFAULT;
+	if (sop.cipher != cryptocop_cipher_none) {
+		if (!access_ok(VERIFY_READ, sop.key, sop.keylen)) return -EFAULT;
+	}
+	DEBUG(printk("cryptocop_ioctl_create_session, sess_op:\n"));
+
+	DEBUG(printk("\tcipher:%d\n"
+		     "\tcipher_mode:%d\n"
+		     "\tdigest:%d\n"
+		     "\tcsum:%d\n",
+		     (int)sop.cipher,
+		     (int)sop.cmode,
+		     (int)sop.digest,
+		     (int)sop.csum));
+
+	if (sop.cipher != cryptocop_cipher_none){
+		/* Init the cipher. */
+		switch (sop.cipher){
+		case cryptocop_cipher_des:
+			ti_cipher.alg = cryptocop_alg_des;
+			break;
+		case cryptocop_cipher_3des:
+			ti_cipher.alg = cryptocop_alg_3des;
+			break;
+		case cryptocop_cipher_aes:
+			ti_cipher.alg = cryptocop_alg_aes;
+			break;
+		default:
+			DEBUG_API(printk("create session, bad cipher algorithm %d\n", sop.cipher));
+			return -EINVAL;
+		};
+		DEBUG(printk("setting cipher transform %d\n", ti_cipher.alg));
+		copy_from_user(ti_cipher.key, sop.key, sop.keylen/8);
+		ti_cipher.keylen = sop.keylen;
+		switch (sop.cmode){
+		case cryptocop_cipher_mode_cbc:
+		case cryptocop_cipher_mode_ecb:
+			ti_cipher.cipher_mode = sop.cmode;
+			break;
+		default:
+			DEBUG_API(printk("create session, bad cipher mode %d\n", sop.cmode));
+			return -EINVAL;
+		}
+		DEBUG(printk("cryptocop_ioctl_create_session: setting CBC mode %d\n", ti_cipher.cipher_mode));
+		switch (sop.des3_mode){
+		case cryptocop_3des_eee:
+		case cryptocop_3des_eed:
+		case cryptocop_3des_ede:
+		case cryptocop_3des_edd:
+		case cryptocop_3des_dee:
+		case cryptocop_3des_ded:
+		case cryptocop_3des_dde:
+		case cryptocop_3des_ddd:
+			ti_cipher.tdes_mode = sop.des3_mode;
+			break;
+		default:
+			DEBUG_API(printk("create session, bad 3DES mode %d\n", sop.des3_mode));
+			return -EINVAL;
+		}
+		ti_cipher.tid = CRYPTOCOP_IOCTL_CIPHER_TID;
+		ti_cipher.next = tis;
+		tis = &ti_cipher;
+	} /* if (sop.cipher != cryptocop_cipher_none) */
+	if (sop.digest != cryptocop_digest_none){
+		DEBUG(printk("setting digest transform\n"));
+		switch (sop.digest){
+		case cryptocop_digest_md5:
+			ti_digest.alg = cryptocop_alg_md5;
+			break;
+		case cryptocop_digest_sha1:
+			ti_digest.alg = cryptocop_alg_sha1;
+			break;
+		default:
+			DEBUG_API(printk("create session, bad digest algorithm %d\n", sop.digest));
+			return -EINVAL;
+		}
+		ti_digest.tid = CRYPTOCOP_IOCTL_DIGEST_TID;
+		ti_digest.next = tis;
+		tis = &ti_digest;
+	} /* if (sop.digest != cryptocop_digest_none) */
+	if (sop.csum != cryptocop_csum_none){
+		DEBUG(printk("setting csum transform\n"));
+		switch (sop.csum){
+		case cryptocop_csum_le:
+		case cryptocop_csum_be:
+			ti_csum.csum_mode = sop.csum;
+			break;
+		default:
+			DEBUG_API(printk("create session, bad checksum algorithm %d\n", sop.csum));
+			return -EINVAL;
+		}
+		ti_csum.alg = cryptocop_alg_csum;
+		ti_csum.tid = CRYPTOCOP_IOCTL_CSUM_TID;
+		ti_csum.next = tis;
+		tis = &ti_csum;
+	} /* (sop.csum != cryptocop_csum_none) */
+	dev = kmalloc(sizeof(struct cryptocop_private), GFP_KERNEL);
+	if (!dev){
+		DEBUG_API(printk("create session, alloc dev\n"));
+		return -ENOMEM;
+	}
+
+	err = cryptocop_new_session(&sid, tis, GFP_KERNEL);
+	DEBUG({ if (err) printk("create session, cryptocop_new_session %d\n", err);});
+
+	if (err) {
+		kfree(dev);
+		return err;
+	}
+	sess_op->ses_id = sid;
+	dev->sid = sid;
+	dev->next = filp->private_data;
+	filp->private_data = dev;
+
+	return 0;
+}
+
+static int cryptocop_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	int err = 0;
+	if (_IOC_TYPE(cmd) != ETRAXCRYPTOCOP_IOCTYPE) {
+		DEBUG_API(printk("cryptocop_ioctl: wrong type\n"));
+		return -ENOTTY;
+	}
+	if (_IOC_NR(cmd) > CRYPTOCOP_IO_MAXNR){
+		return -ENOTTY;
+	}
+	/* Access check of the argument.  Some commands, e.g. create session and process op,
+	   needs additional checks.  Those are handled in the command handling functions. */
+	if (_IOC_DIR(cmd) & _IOC_READ)
+		err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
+	else if (_IOC_DIR(cmd) & _IOC_WRITE)
+		err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
+	if (err) return -EFAULT;
+
+	switch (cmd) {
+	case CRYPTOCOP_IO_CREATE_SESSION:
+		return cryptocop_ioctl_create_session(inode, filp, cmd, arg);
+	case CRYPTOCOP_IO_CLOSE_SESSION:
+		return cryptocop_ioctl_close_session(inode, filp, cmd, arg);
+	case CRYPTOCOP_IO_PROCESS_OP:
+		return cryptocop_ioctl_process(inode, filp, cmd, arg);
+	default:
+		DEBUG_API(printk("cryptocop_ioctl: unknown command\n"));
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+
+#ifdef LDEBUG
+static void print_dma_descriptors(struct cryptocop_int_operation *iop)
+{
+	struct cryptocop_dma_desc *cdesc_out = iop->cdesc_out;
+	struct cryptocop_dma_desc *cdesc_in = iop->cdesc_in;
+	int                       i;
+
+	printk("print_dma_descriptors start\n");
+
+	printk("iop:\n");
+	printk("\tsid: 0x%lld\n", iop->sid);
+
+	printk("\tcdesc_out: 0x%p\n", iop->cdesc_out);
+	printk("\tcdesc_in: 0x%p\n", iop->cdesc_in);
+	printk("\tddesc_out: 0x%p\n", iop->ddesc_out);
+	printk("\tddesc_in: 0x%p\n", iop->ddesc_in);
+
+	printk("\niop->ctx_out: 0x%p phys: 0x%p\n", &iop->ctx_out, (char*)virt_to_phys(&iop->ctx_out));
+	printk("\tnext: 0x%p\n"
+	       "\tsaved_data: 0x%p\n"
+	       "\tsaved_data_buf: 0x%p\n",
+	       iop->ctx_out.next,
+	       iop->ctx_out.saved_data,
+	       iop->ctx_out.saved_data_buf);
+
+	printk("\niop->ctx_in: 0x%p phys: 0x%p\n", &iop->ctx_in, (char*)virt_to_phys(&iop->ctx_in));
+	printk("\tnext: 0x%p\n"
+	       "\tsaved_data: 0x%p\n"
+	       "\tsaved_data_buf: 0x%p\n",
+	       iop->ctx_in.next,
+	       iop->ctx_in.saved_data,
+	       iop->ctx_in.saved_data_buf);
+
+	i = 0;
+	while (cdesc_out) {
+		dma_descr_data *td;
+		printk("cdesc_out %d, desc=0x%p\n", i, cdesc_out->dma_descr);
+		printk("\n\tvirt_to_phys(desc): 0x%p\n", (char*)virt_to_phys(cdesc_out->dma_descr));
+		td = cdesc_out->dma_descr;
+		printk("\n\tbuf: 0x%p\n"
+		       "\tafter: 0x%p\n"
+		       "\tmd: 0x%04x\n"
+		       "\tnext: 0x%p\n",
+		       td->buf,
+		       td->after,
+		       td->md,
+		       td->next);
+		printk("flags:\n"
+		       "\twait:\t%d\n"
+		       "\teol:\t%d\n"
+		       "\touteop:\t%d\n"
+		       "\tineop:\t%d\n"
+		       "\tintr:\t%d\n",
+		       td->wait,
+		       td->eol,
+		       td->out_eop,
+		       td->in_eop,
+		       td->intr);
+		cdesc_out = cdesc_out->next;
+		i++;
+	}
+	i = 0;
+	while (cdesc_in) {
+		dma_descr_data *td;
+		printk("cdesc_in %d, desc=0x%p\n", i, cdesc_in->dma_descr);
+		printk("\n\tvirt_to_phys(desc): 0x%p\n", (char*)virt_to_phys(cdesc_in->dma_descr));
+		td = cdesc_in->dma_descr;
+		printk("\n\tbuf: 0x%p\n"
+		       "\tafter: 0x%p\n"
+		       "\tmd: 0x%04x\n"
+		       "\tnext: 0x%p\n",
+		       td->buf,
+		       td->after,
+		       td->md,
+		       td->next);
+		printk("flags:\n"
+		       "\twait:\t%d\n"
+		       "\teol:\t%d\n"
+		       "\touteop:\t%d\n"
+		       "\tineop:\t%d\n"
+		       "\tintr:\t%d\n",
+		       td->wait,
+		       td->eol,
+		       td->out_eop,
+		       td->in_eop,
+		       td->intr);
+		cdesc_in = cdesc_in->next;
+		i++;
+	}
+
+	printk("print_dma_descriptors end\n");
+}
+
+
+static void print_strcop_crypto_op(struct strcop_crypto_op *cop)
+{
+	printk("print_strcop_crypto_op, 0x%p\n", cop);
+
+	/* Indata. */
+	printk("indata=0x%p\n"
+	       "inlen=%d\n"
+	       "do_cipher=%d\n"
+	       "decrypt=%d\n"
+	       "cipher_explicit=%d\n"
+	       "cipher_start=%d\n"
+	       "cipher_len=%d\n"
+	       "outdata=0x%p\n"
+	       "outlen=%d\n",
+	       cop->indata,
+	       cop->inlen,
+	       cop->do_cipher,
+	       cop->decrypt,
+	       cop->cipher_explicit,
+	       cop->cipher_start,
+	       cop->cipher_len,
+	       cop->cipher_outdata,
+	       cop->cipher_outlen);
+
+	printk("do_digest=%d\n"
+	       "digest_start=%d\n"
+	       "digest_len=%d\n",
+	       cop->do_digest,
+	       cop->digest_start,
+	       cop->digest_len);
+
+	printk("do_csum=%d\n"
+	       "csum_start=%d\n"
+	       "csum_len=%d\n",
+	       cop->do_csum,
+	       cop->csum_start,
+	       cop->csum_len);
+}
+
+static void print_cryptocop_operation(struct cryptocop_operation *cop)
+{
+	struct cryptocop_desc      *d;
+	struct cryptocop_tfrm_cfg  *tc;
+	struct cryptocop_desc_cfg  *dc;
+	int                        i;
+
+	printk("print_cryptocop_operation, cop=0x%p\n\n", cop);
+	printk("sid: %lld\n", cop->sid);
+	printk("operation_status=%d\n"
+	       "use_dmalists=%d\n"
+	       "in_interrupt=%d\n"
+	       "fast_callback=%d\n",
+	       cop->operation_status,
+	       cop->use_dmalists,
+	       cop->in_interrupt,
+	       cop->fast_callback);
+
+	if (cop->use_dmalists){
+		print_user_dma_lists(&cop->list_op);
+	} else {
+		printk("cop->tfrm_op\n"
+		       "tfrm_cfg=0x%p\n"
+		       "desc=0x%p\n"
+		       "indata=0x%p\n"
+		       "incount=%d\n"
+		       "inlen=%d\n"
+		       "outdata=0x%p\n"
+		       "outcount=%d\n"
+		       "outlen=%d\n\n",
+		       cop->tfrm_op.tfrm_cfg,
+		       cop->tfrm_op.desc,
+		       cop->tfrm_op.indata,
+		       cop->tfrm_op.incount,
+		       cop->tfrm_op.inlen,
+		       cop->tfrm_op.outdata,
+		       cop->tfrm_op.outcount,
+		       cop->tfrm_op.outlen);
+
+		tc = cop->tfrm_op.tfrm_cfg;
+		while (tc){
+			printk("tfrm_cfg, 0x%p\n"
+			       "tid=%d\n"
+			       "flags=%d\n"
+			       "inject_ix=%d\n"
+			       "next=0x%p\n",
+			       tc,
+			       tc->tid,
+			       tc->flags,
+			       tc->inject_ix,
+			       tc->next);
+			tc = tc->next;
+		}
+		d = cop->tfrm_op.desc;
+		while (d){
+			printk("\n======================desc, 0x%p\n"
+			       "length=%d\n"
+			       "cfg=0x%p\n"
+			       "next=0x%p\n",
+			       d,
+			       d->length,
+			       d->cfg,
+			       d->next);
+			dc = d->cfg;
+			while (dc){
+				printk("=========desc_cfg, 0x%p\n"
+				       "tid=%d\n"
+				       "src=%d\n"
+				       "last=%d\n"
+				       "next=0x%p\n",
+				       dc,
+				       dc->tid,
+				       dc->src,
+				       dc->last,
+				       dc->next);
+				dc = dc->next;
+			}
+			d = d->next;
+		}
+		printk("\n====iniov\n");
+		for (i = 0; i < cop->tfrm_op.incount; i++){
+			printk("indata[%d]\n"
+			       "base=0x%p\n"
+			       "len=%d\n",
+			       i,
+			       cop->tfrm_op.indata[i].iov_base,
+			       cop->tfrm_op.indata[i].iov_len);
+		}
+		printk("\n====outiov\n");
+		for (i = 0; i < cop->tfrm_op.outcount; i++){
+			printk("outdata[%d]\n"
+			       "base=0x%p\n"
+			       "len=%d\n",
+			       i,
+			       cop->tfrm_op.outdata[i].iov_base,
+			       cop->tfrm_op.outdata[i].iov_len);
+		}
+	}
+	printk("------------end print_cryptocop_operation\n");
+}
+
+
+static void print_user_dma_lists(struct cryptocop_dma_list_operation *dma_op)
+{
+	dma_descr_data *dd;
+	int i;
+
+	printk("print_user_dma_lists, dma_op=0x%p\n", dma_op);
+
+	printk("out_data_buf = 0x%p, phys_to_virt(out_data_buf) = 0x%p\n", dma_op->out_data_buf, phys_to_virt((unsigned long int)dma_op->out_data_buf));
+	printk("in_data_buf = 0x%p, phys_to_virt(in_data_buf) = 0x%p\n", dma_op->in_data_buf, phys_to_virt((unsigned long int)dma_op->in_data_buf));
+
+	printk("##############outlist\n");
+	dd = phys_to_virt((unsigned long int)dma_op->outlist);
+	i = 0;
+	while (dd != NULL) {
+		printk("#%d phys_to_virt(desc) 0x%p\n", i, dd);
+		printk("\n\tbuf: 0x%p\n"
+		       "\tafter: 0x%p\n"
+		       "\tmd: 0x%04x\n"
+		       "\tnext: 0x%p\n",
+		       dd->buf,
+		       dd->after,
+		       dd->md,
+		       dd->next);
+		printk("flags:\n"
+		       "\twait:\t%d\n"
+		       "\teol:\t%d\n"
+		       "\touteop:\t%d\n"
+		       "\tineop:\t%d\n"
+		       "\tintr:\t%d\n",
+		       dd->wait,
+		       dd->eol,
+		       dd->out_eop,
+		       dd->in_eop,
+		       dd->intr);
+		if (dd->eol)
+			dd = NULL;
+		else
+			dd = phys_to_virt((unsigned long int)dd->next);
+		++i;
+	}
+
+	printk("##############inlist\n");
+	dd = phys_to_virt((unsigned long int)dma_op->inlist);
+	i = 0;
+	while (dd != NULL) {
+		printk("#%d phys_to_virt(desc) 0x%p\n", i, dd);
+		printk("\n\tbuf: 0x%p\n"
+		       "\tafter: 0x%p\n"
+		       "\tmd: 0x%04x\n"
+		       "\tnext: 0x%p\n",
+		       dd->buf,
+		       dd->after,
+		       dd->md,
+		       dd->next);
+		printk("flags:\n"
+		       "\twait:\t%d\n"
+		       "\teol:\t%d\n"
+		       "\touteop:\t%d\n"
+		       "\tineop:\t%d\n"
+		       "\tintr:\t%d\n",
+		       dd->wait,
+		       dd->eol,
+		       dd->out_eop,
+		       dd->in_eop,
+		       dd->intr);
+		if (dd->eol)
+			dd = NULL;
+		else
+			dd = phys_to_virt((unsigned long int)dd->next);
+		++i;
+	}
+}
+
+
+static void print_lock_status(void)
+{
+	printk("**********************print_lock_status\n");
+	printk("cryptocop_completed_jobs_lock %d\n", spin_is_locked(&cryptocop_completed_jobs_lock));
+	printk("cryptocop_job_queue_lock %d\n", spin_is_locked(&cryptocop_job_queue_lock));
+	printk("descr_pool_lock %d\n", spin_is_locked(&descr_pool_lock));
+	printk("cryptocop_sessions_lock %d\n", spin_is_locked(cryptocop_sessions_lock));
+	printk("running_job_lock %d\n", spin_is_locked(running_job_lock));
+	printk("cryptocop_process_lock %d\n", spin_is_locked(cryptocop_process_lock));
+}
+#endif /* LDEBUG */
+
+
+static const char cryptocop_name[] = "ETRAX FS stream co-processor";
+
+static int init_stream_coprocessor(void)
+{
+	int err;
+	int i;
+	static int initialized = 0;
+
+	if (initialized)
+		return 0;
+
+	initialized = 1;
+
+	printk("ETRAX FS stream co-processor driver v0.01, (c) 2003 Axis Communications AB\n");
+
+	err = register_chrdev(CRYPTOCOP_MAJOR, cryptocop_name, &cryptocop_fops);
+	if (err < 0) {
+		printk(KERN_ERR "stream co-processor: could not get major number.\n");
+		return err;
+	}
+
+	err = init_cryptocop();
+	if (err) {
+		(void)unregister_chrdev(CRYPTOCOP_MAJOR, cryptocop_name);
+		return err;
+	}
+	err = cryptocop_job_queue_init();
+	if (err) {
+		release_cryptocop();
+		(void)unregister_chrdev(CRYPTOCOP_MAJOR, cryptocop_name);
+		return err;
+	}
+	/* Init the descriptor pool. */
+	for (i = 0; i < CRYPTOCOP_DESCRIPTOR_POOL_SIZE - 1; i++) {
+		descr_pool[i].from_pool = 1;
+		descr_pool[i].next = &descr_pool[i + 1];
+	}
+	descr_pool[i].from_pool = 1;
+	descr_pool[i].next = NULL;
+	descr_pool_free_list = &descr_pool[0];
+	descr_pool_no_free = CRYPTOCOP_DESCRIPTOR_POOL_SIZE;
+
+	spin_lock_init(&cryptocop_completed_jobs_lock);
+	spin_lock_init(&cryptocop_job_queue_lock);
+	spin_lock_init(&descr_pool_lock);
+	spin_lock_init(&cryptocop_sessions_lock);
+	spin_lock_init(&running_job_lock);
+	spin_lock_init(&cryptocop_process_lock);
+
+	cryptocop_sessions = NULL;
+	next_sid = 1;
+
+	cryptocop_running_job = NULL;
+
+	printk("stream co-processor: init done.\n");
+	return 0;
+}
+
+static void __exit exit_stream_coprocessor(void)
+{
+	release_cryptocop();
+	cryptocop_job_queue_close();
+}
+
+module_init(init_stream_coprocessor);
+module_exit(exit_stream_coprocessor);
+

+ 766 - 0
arch/cris/arch-v32/drivers/gpio.c

@@ -0,0 +1,766 @@
+/* $Id: gpio.c,v 1.16 2005/06/19 17:06:49 starvik Exp $
+ *
+ * ETRAX CRISv32 general port I/O device
+ *
+ * Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
+ *
+ * Authors:    Bjorn Wesen      (initial version)
+ *             Ola Knutsson     (LED handling)
+ *             Johan Adolfsson  (read/set directions, write, port G,
+ *                               port to ETRAX FS.
+ *
+ * $Log: gpio.c,v $
+ * Revision 1.16  2005/06/19 17:06:49  starvik
+ * Merge of Linux 2.6.12.
+ *
+ * Revision 1.15  2005/05/25 08:22:20  starvik
+ * Changed GPIO port order to fit packages/devices/axis-2.4.
+ *
+ * Revision 1.14  2005/04/24 18:35:08  starvik
+ * Updated with final register headers.
+ *
+ * Revision 1.13  2005/03/15 15:43:00  starvik
+ * dev_id needs to be supplied for shared IRQs.
+ *
+ * Revision 1.12  2005/03/10 17:12:00  starvik
+ * Protect alarm list with spinlock.
+ *
+ * Revision 1.11  2005/01/05 06:08:59  starvik
+ * No need to do local_irq_disable after local_irq_save.
+ *
+ * Revision 1.10  2004/11/19 08:38:31  starvik
+ * Removed old crap.
+ *
+ * Revision 1.9  2004/05/14 07:58:02  starvik
+ * Merge of changes from 2.4
+ *
+ * Revision 1.8  2003/09/11 07:29:50  starvik
+ * Merge of Linux 2.6.0-test5
+ *
+ * Revision 1.7  2003/07/10 13:25:46  starvik
+ * Compiles for 2.5.74
+ * Lindented ethernet.c
+ *
+ * Revision 1.6  2003/07/04 08:27:46  starvik
+ * Merge of Linux 2.5.74
+ *
+ * Revision 1.5  2003/06/10 08:26:37  johana
+ * Etrax -> ETRAX CRISv32
+ *
+ * Revision 1.4  2003/06/05 14:22:48  johana
+ * Initialise some_alarms.
+ *
+ * Revision 1.3  2003/06/05 10:15:46  johana
+ * New INTR_VECT macros.
+ * Enable interrupts in global config.
+ *
+ * Revision 1.2  2003/06/03 15:52:50  johana
+ * Initial CRIS v32 version.
+ *
+ * Revision 1.1  2003/06/03 08:53:15  johana
+ * Copy of os/lx25/arch/cris/arch-v10/drivers/gpio.c version 1.7.
+ *
+ */
+
+#include <linux/config.h>
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+
+#include <asm/etraxgpio.h>
+#include <asm/arch/hwregs/reg_map.h>
+#include <asm/arch/hwregs/reg_rdwr.h>
+#include <asm/arch/hwregs/gio_defs.h>
+#include <asm/arch/hwregs/intr_vect_defs.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+/* The following gio ports on ETRAX FS is available:
+ * pa  8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge
+ * pb 18 bits
+ * pc 18 bits
+ * pd 18 bits
+ * pe 18 bits
+ * each port has a rw_px_dout, r_px_din and rw_px_oe register.
+ */
+
+#define GPIO_MAJOR 120  /* experimental MAJOR number */
+
+#define D(x)
+
+#if 0
+static int dp_cnt;
+#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0)
+#else
+#define DP(x)
+#endif
+
+static char gpio_name[] = "etrax gpio";
+
+#if 0
+static wait_queue_head_t *gpio_wq;
+#endif
+
+static int gpio_ioctl(struct inode *inode, struct file *file,
+		      unsigned int cmd, unsigned long arg);
+static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
+                          loff_t *off);
+static int gpio_open(struct inode *inode, struct file *filp);
+static int gpio_release(struct inode *inode, struct file *filp);
+static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait);
+
+/* private data per open() of this driver */
+
+struct gpio_private {
+	struct gpio_private *next;
+	/* The IO_CFG_WRITE_MODE_VALUE only support 8 bits: */
+	unsigned char clk_mask;
+	unsigned char data_mask;
+	unsigned char write_msb;
+	unsigned char pad1;
+	/* These fields are generic */
+	unsigned long highalarm, lowalarm;
+	wait_queue_head_t alarm_wq;
+	int minor;
+};
+
+/* linked list of alarms to check for */
+
+static struct gpio_private *alarmlist = 0;
+
+static int gpio_some_alarms = 0; /* Set if someone uses alarm */
+static unsigned long gpio_pa_high_alarms = 0;
+static unsigned long gpio_pa_low_alarms = 0;
+
+static DEFINE_SPINLOCK(alarm_lock);
+
+#define NUM_PORTS (GPIO_MINOR_LAST+1)
+#define GIO_REG_RD_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg )
+#define GIO_REG_WR_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg )
+unsigned long led_dummy;
+
+static volatile unsigned long *data_out[NUM_PORTS] = {
+	GIO_REG_WR_ADDR(rw_pa_dout),
+	GIO_REG_WR_ADDR(rw_pb_dout),
+	&led_dummy,
+	GIO_REG_WR_ADDR(rw_pc_dout),
+	GIO_REG_WR_ADDR(rw_pd_dout),
+	GIO_REG_WR_ADDR(rw_pe_dout),
+};
+
+static volatile unsigned long *data_in[NUM_PORTS] = {
+	GIO_REG_RD_ADDR(r_pa_din),
+	GIO_REG_RD_ADDR(r_pb_din),
+	&led_dummy,
+	GIO_REG_RD_ADDR(r_pc_din),
+	GIO_REG_RD_ADDR(r_pd_din),
+	GIO_REG_RD_ADDR(r_pe_din),
+};
+
+static unsigned long changeable_dir[NUM_PORTS] = {
+	CONFIG_ETRAX_PA_CHANGEABLE_DIR,
+	CONFIG_ETRAX_PB_CHANGEABLE_DIR,
+	0,
+	CONFIG_ETRAX_PC_CHANGEABLE_DIR,
+	CONFIG_ETRAX_PD_CHANGEABLE_DIR,
+	CONFIG_ETRAX_PE_CHANGEABLE_DIR,
+};
+
+static unsigned long changeable_bits[NUM_PORTS] = {
+	CONFIG_ETRAX_PA_CHANGEABLE_BITS,
+	CONFIG_ETRAX_PB_CHANGEABLE_BITS,
+	0,
+	CONFIG_ETRAX_PC_CHANGEABLE_BITS,
+	CONFIG_ETRAX_PD_CHANGEABLE_BITS,
+	CONFIG_ETRAX_PE_CHANGEABLE_BITS,
+};
+
+static volatile unsigned long *dir_oe[NUM_PORTS] = {
+	GIO_REG_WR_ADDR(rw_pa_oe),
+	GIO_REG_WR_ADDR(rw_pb_oe),
+	&led_dummy,
+	GIO_REG_WR_ADDR(rw_pc_oe),
+	GIO_REG_WR_ADDR(rw_pd_oe),
+	GIO_REG_WR_ADDR(rw_pe_oe),
+};
+
+
+
+static unsigned int
+gpio_poll(struct file *file,
+	  poll_table *wait)
+{
+	unsigned int mask = 0;
+	struct gpio_private *priv = (struct gpio_private *)file->private_data;
+	unsigned long data;
+	poll_wait(file, &priv->alarm_wq, wait);
+	if (priv->minor == GPIO_MINOR_A) {
+		reg_gio_rw_intr_cfg intr_cfg;
+		unsigned long tmp;
+		unsigned long flags;
+
+		local_irq_save(flags);
+		data = REG_TYPE_CONV(unsigned long, reg_gio_r_pa_din, REG_RD(gio, regi_gio, r_pa_din));
+		/* PA has support for interrupt
+		 * lets activate high for those low and with highalarm set
+		 */
+		intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg);
+
+		tmp = ~data & priv->highalarm & 0xFF;
+                if (tmp & (1 << 0)) {
+			intr_cfg.pa0 = regk_gio_hi;
+		}
+                if (tmp & (1 << 1)) {
+			intr_cfg.pa1 = regk_gio_hi;
+		}
+                if (tmp & (1 << 2)) {
+			intr_cfg.pa2 = regk_gio_hi;
+		}
+                if (tmp & (1 << 3)) {
+			intr_cfg.pa3 = regk_gio_hi;
+		}
+                if (tmp & (1 << 4)) {
+			intr_cfg.pa4 = regk_gio_hi;
+		}
+                if (tmp & (1 << 5)) {
+			intr_cfg.pa5 = regk_gio_hi;
+		}
+                if (tmp & (1 << 6)) {
+			intr_cfg.pa6 = regk_gio_hi;
+		}
+                if (tmp & (1 << 7)) {
+			intr_cfg.pa7 = regk_gio_hi;
+		}
+		/*
+		 * lets activate low for those high and with lowalarm set
+		 */
+		tmp = data & priv->lowalarm & 0xFF;
+                if (tmp & (1 << 0)) {
+			intr_cfg.pa0 = regk_gio_lo;
+		}
+                if (tmp & (1 << 1)) {
+			intr_cfg.pa1 = regk_gio_lo;
+		}
+                if (tmp & (1 << 2)) {
+			intr_cfg.pa2 = regk_gio_lo;
+		}
+                if (tmp & (1 << 3)) {
+			intr_cfg.pa3 = regk_gio_lo;
+		}
+                if (tmp & (1 << 4)) {
+			intr_cfg.pa4 = regk_gio_lo;
+		}
+                if (tmp & (1 << 5)) {
+			intr_cfg.pa5 = regk_gio_lo;
+		}
+                if (tmp & (1 << 6)) {
+			intr_cfg.pa6 = regk_gio_lo;
+		}
+                if (tmp & (1 << 7)) {
+			intr_cfg.pa7 = regk_gio_lo;
+		}
+
+		REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg);
+		local_irq_restore(flags);
+	} else if (priv->minor <= GPIO_MINOR_E)
+		data = *data_in[priv->minor];
+	else
+		return 0;
+
+	if ((data & priv->highalarm) ||
+	    (~data & priv->lowalarm)) {
+		mask = POLLIN|POLLRDNORM;
+	}
+
+	DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
+	return mask;
+}
+
+int etrax_gpio_wake_up_check(void)
+{
+	struct gpio_private *priv = alarmlist;
+	unsigned long data = 0;
+        int ret = 0;
+	while (priv) {
+		data = *data_in[priv->minor];
+		if ((data & priv->highalarm) ||
+		    (~data & priv->lowalarm)) {
+			DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
+			wake_up_interruptible(&priv->alarm_wq);
+                        ret = 1;
+		}
+		priv = priv->next;
+	}
+        return ret;
+}
+
+static irqreturn_t
+gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	if (gpio_some_alarms) {
+		return IRQ_RETVAL(etrax_gpio_wake_up_check());
+	}
+        return IRQ_NONE;
+}
+
+static irqreturn_t
+gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	reg_gio_rw_intr_mask intr_mask;
+	reg_gio_r_masked_intr masked_intr;
+	reg_gio_rw_ack_intr ack_intr;
+	unsigned long tmp;
+	unsigned long tmp2;
+
+	/* Find what PA interrupts are active */
+	masked_intr = REG_RD(gio, regi_gio, r_masked_intr);
+	tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr);
+
+	/* Find those that we have enabled */
+	spin_lock(&alarm_lock);
+	tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms);
+	spin_unlock(&alarm_lock);
+
+	/* Ack them */
+	ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp);
+	REG_WR(gio, regi_gio, rw_ack_intr, ack_intr);
+
+	/* Disable those interrupts.. */
+	intr_mask = REG_RD(gio, regi_gio, rw_intr_mask);
+	tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask);
+	tmp2 &= ~tmp;
+	intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2);
+	REG_WR(gio, regi_gio, rw_intr_mask, intr_mask);
+
+	if (gpio_some_alarms) {
+		return IRQ_RETVAL(etrax_gpio_wake_up_check());
+	}
+        return IRQ_NONE;
+}
+
+
+static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
+                          loff_t *off)
+{
+	struct gpio_private *priv = (struct gpio_private *)file->private_data;
+	unsigned char data, clk_mask, data_mask, write_msb;
+	unsigned long flags;
+	unsigned long shadow;
+	volatile unsigned long *port;
+	ssize_t retval = count;
+	/* Only bits 0-7 may be used for write operations but allow all
+	   devices except leds... */
+	if (priv->minor == GPIO_MINOR_LEDS) {
+		return -EFAULT;
+	}
+
+	if (!access_ok(VERIFY_READ, buf, count)) {
+		return -EFAULT;
+	}
+	clk_mask = priv->clk_mask;
+	data_mask = priv->data_mask;
+	/* It must have been configured using the IO_CFG_WRITE_MODE */
+	/* Perhaps a better error code? */
+	if (clk_mask == 0 || data_mask == 0) {
+		return -EPERM;
+	}
+	write_msb = priv->write_msb;
+	D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb));
+	port = data_out[priv->minor];
+
+	while (count--) {
+		int i;
+		data = *buf++;
+		if (priv->write_msb) {
+			for (i = 7; i >= 0;i--) {
+				local_irq_save(flags);
+				shadow = *port;
+				*port = shadow &= ~clk_mask;
+				if (data & 1<<i)
+					*port = shadow |= data_mask;
+				else
+					*port = shadow &= ~data_mask;
+			/* For FPGA: min 5.0ns (DCC) before CCLK high */
+				*port = shadow |= clk_mask;
+				local_irq_restore(flags);
+			}
+		} else {
+			for (i = 0; i <= 7;i++) {
+				local_irq_save(flags);
+				shadow = *port;
+				*port = shadow &= ~clk_mask;
+				if (data & 1<<i)
+					*port = shadow |= data_mask;
+				else
+					*port = shadow &= ~data_mask;
+			/* For FPGA: min 5.0ns (DCC) before CCLK high */
+				*port = shadow |= clk_mask;
+				local_irq_restore(flags);
+			}
+		}
+	}
+	return retval;
+}
+
+
+
+static int
+gpio_open(struct inode *inode, struct file *filp)
+{
+	struct gpio_private *priv;
+	int p = MINOR(inode->i_rdev);
+
+	if (p > GPIO_MINOR_LAST)
+		return -EINVAL;
+
+	priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private),
+					      GFP_KERNEL);
+
+	if (!priv)
+		return -ENOMEM;
+
+	priv->minor = p;
+
+	/* initialize the io/alarm struct and link it into our alarmlist */
+
+	priv->next = alarmlist;
+	alarmlist = priv;
+	priv->clk_mask = 0;
+	priv->data_mask = 0;
+	priv->highalarm = 0;
+	priv->lowalarm = 0;
+	init_waitqueue_head(&priv->alarm_wq);
+
+	filp->private_data = (void *)priv;
+
+	return 0;
+}
+
+static int
+gpio_release(struct inode *inode, struct file *filp)
+{
+	struct gpio_private *p = alarmlist;
+	struct gpio_private *todel = (struct gpio_private *)filp->private_data;
+	/* local copies while updating them: */
+	unsigned long a_high, a_low;
+	unsigned long some_alarms;
+
+	/* unlink from alarmlist and free the private structure */
+
+	if (p == todel) {
+		alarmlist = todel->next;
+	} else {
+		while (p->next != todel)
+			p = p->next;
+		p->next = todel->next;
+	}
+
+	kfree(todel);
+	/* Check if there are still any alarms set */
+	p = alarmlist;
+        some_alarms = 0;
+	a_high = 0;
+	a_low = 0;
+	while (p) {
+		if (p->minor == GPIO_MINOR_A) {
+			a_high |= p->highalarm;
+			a_low |= p->lowalarm;
+		}
+
+		if (p->highalarm | p->lowalarm) {
+			some_alarms = 1;
+		}
+		p = p->next;
+	}
+
+	spin_lock(&alarm_lock);
+	gpio_some_alarms = some_alarms;
+	gpio_pa_high_alarms = a_high;
+	gpio_pa_low_alarms = a_low;
+	spin_unlock(&alarm_lock);
+
+	return 0;
+}
+
+/* Main device API. ioctl's to read/set/clear bits, as well as to
+ * set alarms to wait for using a subsequent select().
+ */
+
+unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
+{
+	/* Set direction 0=unchanged 1=input,
+	 * return mask with 1=input
+	 */
+	unsigned long flags;
+	unsigned long dir_shadow;
+
+	local_irq_save(flags);
+	dir_shadow = *dir_oe[priv->minor];
+	dir_shadow &= ~(arg & changeable_dir[priv->minor]);
+	*dir_oe[priv->minor] = dir_shadow;
+	local_irq_restore(flags);
+
+	if (priv->minor == GPIO_MINOR_A)
+		dir_shadow ^= 0xFF;    /* Only 8 bits */
+	else
+		dir_shadow ^= 0x3FFFF; /* Only 18 bits */
+	return dir_shadow;
+
+} /* setget_input */
+
+unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
+{
+	unsigned long flags;
+	unsigned long dir_shadow;
+
+	local_irq_save(flags);
+	dir_shadow = *dir_oe[priv->minor];
+	dir_shadow |=  (arg & changeable_dir[priv->minor]);
+	*dir_oe[priv->minor] = dir_shadow;
+	local_irq_restore(flags);
+	return dir_shadow;
+} /* setget_output */
+
+static int
+gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
+
+static int
+gpio_ioctl(struct inode *inode, struct file *file,
+	   unsigned int cmd, unsigned long arg)
+{
+	unsigned long flags;
+	unsigned long val;
+	unsigned long shadow;
+	struct gpio_private *priv = (struct gpio_private *)file->private_data;
+	if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
+		return -EINVAL;
+	}
+
+	switch (_IOC_NR(cmd)) {
+	case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
+		// read the port
+		return *data_in[priv->minor];
+		break;
+	case IO_SETBITS:
+		local_irq_save(flags);
+                if (arg & 0x04)
+                  printk("GPIO SET 2\n");
+		// set changeable bits with a 1 in arg
+		shadow = *data_out[priv->minor];
+		shadow |=  (arg & changeable_bits[priv->minor]);
+		*data_out[priv->minor] = shadow;
+		local_irq_restore(flags);
+		break;
+	case IO_CLRBITS:
+		local_irq_save(flags);
+                if (arg & 0x04)
+                  printk("GPIO CLR 2\n");
+		// clear changeable bits with a 1 in arg
+		shadow = *data_out[priv->minor];
+		shadow &=  ~(arg & changeable_bits[priv->minor]);
+		*data_out[priv->minor] = shadow;
+		local_irq_restore(flags);
+		break;
+	case IO_HIGHALARM:
+		// set alarm when bits with 1 in arg go high
+		priv->highalarm |= arg;
+		spin_lock(&alarm_lock);
+		gpio_some_alarms = 1;
+		if (priv->minor == GPIO_MINOR_A) {
+			gpio_pa_high_alarms |= arg;
+		}
+		spin_unlock(&alarm_lock);
+		break;
+	case IO_LOWALARM:
+		// set alarm when bits with 1 in arg go low
+		priv->lowalarm |= arg;
+		spin_lock(&alarm_lock);
+		gpio_some_alarms = 1;
+		if (priv->minor == GPIO_MINOR_A) {
+			gpio_pa_low_alarms |= arg;
+		}
+		spin_unlock(&alarm_lock);
+		break;
+	case IO_CLRALARM:
+		// clear alarm for bits with 1 in arg
+		priv->highalarm &= ~arg;
+		priv->lowalarm  &= ~arg;
+		spin_lock(&alarm_lock);
+		if (priv->minor == GPIO_MINOR_A) {
+			if (gpio_pa_high_alarms & arg ||
+			    gpio_pa_low_alarms & arg) {
+				/* Must update the gpio_pa_*alarms masks */
+			}
+		}
+		spin_unlock(&alarm_lock);
+		break;
+	case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
+		/* Read direction 0=input 1=output */
+		return *dir_oe[priv->minor];
+	case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
+		/* Set direction 0=unchanged 1=input,
+		 * return mask with 1=input
+		 */
+		return setget_input(priv, arg);
+		break;
+	case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
+		/* Set direction 0=unchanged 1=output,
+		 * return mask with 1=output
+		 */
+		return setget_output(priv, arg);
+
+	case IO_CFG_WRITE_MODE:
+	{
+		unsigned long dir_shadow;
+		dir_shadow = *dir_oe[priv->minor];
+
+		priv->clk_mask = arg & 0xFF;
+		priv->data_mask = (arg >> 8) & 0xFF;
+		priv->write_msb = (arg >> 16) & 0x01;
+		/* Check if we're allowed to change the bits and
+		 * the direction is correct
+		 */
+		if (!((priv->clk_mask & changeable_bits[priv->minor]) &&
+		      (priv->data_mask & changeable_bits[priv->minor]) &&
+		      (priv->clk_mask & dir_shadow) &&
+		      (priv->data_mask & dir_shadow)))
+		{
+			priv->clk_mask = 0;
+			priv->data_mask = 0;
+			return -EPERM;
+		}
+		break;
+	}
+	case IO_READ_INBITS:
+		/* *arg is result of reading the input pins */
+		val = *data_in[priv->minor];
+		if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
+			return -EFAULT;
+		return 0;
+		break;
+	case IO_READ_OUTBITS:
+		 /* *arg is result of reading the output shadow */
+		val = *data_out[priv->minor];
+		if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
+			return -EFAULT;
+		break;
+	case IO_SETGET_INPUT:
+		/* bits set in *arg is set to input,
+		 * *arg updated with current input pins.
+		 */
+		if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
+			return -EFAULT;
+		val = setget_input(priv, val);
+		if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
+			return -EFAULT;
+		break;
+	case IO_SETGET_OUTPUT:
+		/* bits set in *arg is set to output,
+		 * *arg updated with current output pins.
+		 */
+		if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
+			return -EFAULT;
+		val = setget_output(priv, val);
+		if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
+			return -EFAULT;
+		break;
+	default:
+		if (priv->minor == GPIO_MINOR_LEDS)
+			return gpio_leds_ioctl(cmd, arg);
+		else
+			return -EINVAL;
+	} /* switch */
+
+	return 0;
+}
+
+static int
+gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
+{
+	unsigned char green;
+	unsigned char red;
+
+	switch (_IOC_NR(cmd)) {
+	case IO_LEDACTIVE_SET:
+		green = ((unsigned char) arg) & 1;
+		red   = (((unsigned char) arg) >> 1) & 1;
+		LED_ACTIVE_SET_G(green);
+		LED_ACTIVE_SET_R(red);
+		break;
+
+	default:
+		return -EINVAL;
+	} /* switch */
+
+	return 0;
+}
+
+struct file_operations gpio_fops = {
+	.owner       = THIS_MODULE,
+	.poll        = gpio_poll,
+	.ioctl       = gpio_ioctl,
+	.write       = gpio_write,
+	.open        = gpio_open,
+	.release     = gpio_release,
+};
+
+
+/* main driver initialization routine, called from mem.c */
+
+static __init int
+gpio_init(void)
+{
+	int res;
+	reg_intr_vect_rw_mask intr_mask;
+
+	/* do the formalities */
+
+	res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
+	if (res < 0) {
+		printk(KERN_ERR "gpio: couldn't get a major number.\n");
+		return res;
+	}
+
+	/* Clear all leds */
+	LED_NETWORK_SET(0);
+	LED_ACTIVE_SET(0);
+	LED_DISK_READ(0);
+	LED_DISK_WRITE(0);
+
+	printk("ETRAX FS GPIO driver v2.5, (c) 2003-2005 Axis Communications AB\n");
+	/* We call etrax_gpio_wake_up_check() from timer interrupt and
+	 * from cpu_idle() in kernel/process.c
+	 * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
+	 * in some tests.
+	 */
+	if (request_irq(TIMER_INTR_VECT, gpio_poll_timer_interrupt,
+			SA_SHIRQ | SA_INTERRUPT,"gpio poll", &alarmlist)) {
+		printk("err: timer0 irq for gpio\n");
+	}
+	if (request_irq(GEN_IO_INTR_VECT, gpio_pa_interrupt,
+			SA_SHIRQ | SA_INTERRUPT,"gpio PA", &alarmlist)) {
+		printk("err: PA irq for gpio\n");
+	}
+	/* enable the gio and timer irq in global config */
+	intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+	intr_mask.timer = 1;
+	intr_mask.gen_io = 1;
+	REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+
+	return res;
+}
+
+/* this makes sure that gpio_init is called during kernel boot */
+
+module_init(gpio_init);

+ 611 - 0
arch/cris/arch-v32/drivers/i2c.c

@@ -0,0 +1,611 @@
+/*!***************************************************************************
+*!
+*! FILE NAME  : i2c.c
+*!
+*! DESCRIPTION: implements an interface for IIC/I2C, both directly from other
+*!              kernel modules (i2c_writereg/readreg) and from userspace using
+*!              ioctl()'s
+*!
+*! Nov 30 1998  Torbjorn Eliasson  Initial version.
+*!              Bjorn Wesen        Elinux kernel version.
+*! Jan 14 2000  Johan Adolfsson    Fixed PB shadow register stuff -
+*!                                 don't use PB_I2C if DS1302 uses same bits,
+*!                                 use PB.
+*| June 23 2003 Pieter Grimmerink  Added 'i2c_sendnack'. i2c_readreg now
+*|                                 generates nack on last received byte,
+*|                                 instead of ack.
+*|                                 i2c_getack changed data level while clock
+*|                                 was high, causing DS75 to see  a stop condition
+*!
+*! ---------------------------------------------------------------------------
+*!
+*! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
+*!
+*!***************************************************************************/
+/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */
+/****************** INCLUDE FILES SECTION ***********************************/
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/config.h>
+
+#include <asm/etraxi2c.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include "i2c.h"
+
+/****************** I2C DEFINITION SECTION *************************/
+
+#define D(x)
+
+#define I2C_MAJOR 123  /* LOCAL/EXPERIMENTAL */
+static const char i2c_name[] = "i2c";
+
+#define CLOCK_LOW_TIME            8
+#define CLOCK_HIGH_TIME           8
+#define START_CONDITION_HOLD_TIME 8
+#define STOP_CONDITION_HOLD_TIME  8
+#define ENABLE_OUTPUT 0x01
+#define ENABLE_INPUT 0x00
+#define I2C_CLOCK_HIGH 1
+#define I2C_CLOCK_LOW 0
+#define I2C_DATA_HIGH 1
+#define I2C_DATA_LOW 0
+
+#define i2c_enable()
+#define i2c_disable()
+
+/* enable or disable output-enable, to select output or input on the i2c bus */
+
+#define i2c_dir_out() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_out)
+#define i2c_dir_in() crisv32_io_set_dir(&cris_i2c_data, crisv32_io_dir_in)
+
+/* control the i2c clock and data signals */
+
+#define i2c_clk(x) crisv32_io_set(&cris_i2c_clk, x)
+#define i2c_data(x) crisv32_io_set(&cris_i2c_data, x)
+
+/* read a bit from the i2c interface */
+
+#define i2c_getbit() crisv32_io_rd(&cris_i2c_data)
+
+#define i2c_delay(usecs) udelay(usecs)
+
+/****************** VARIABLE SECTION ************************************/
+
+static struct crisv32_iopin cris_i2c_clk;
+static struct crisv32_iopin cris_i2c_data;
+
+/****************** FUNCTION DEFINITION SECTION *************************/
+
+
+/* generate i2c start condition */
+
+void
+i2c_start(void)
+{
+	/*
+	 * SCL=1 SDA=1
+	 */
+	i2c_dir_out();
+	i2c_delay(CLOCK_HIGH_TIME/6);
+	i2c_data(I2C_DATA_HIGH);
+	i2c_clk(I2C_CLOCK_HIGH);
+	i2c_delay(CLOCK_HIGH_TIME);
+	/*
+	 * SCL=1 SDA=0
+	 */
+	i2c_data(I2C_DATA_LOW);
+	i2c_delay(START_CONDITION_HOLD_TIME);
+	/*
+	 * SCL=0 SDA=0
+	 */
+	i2c_clk(I2C_CLOCK_LOW);
+	i2c_delay(CLOCK_LOW_TIME);
+}
+
+/* generate i2c stop condition */
+
+void
+i2c_stop(void)
+{
+	i2c_dir_out();
+
+	/*
+	 * SCL=0 SDA=0
+	 */
+	i2c_clk(I2C_CLOCK_LOW);
+	i2c_data(I2C_DATA_LOW);
+	i2c_delay(CLOCK_LOW_TIME*2);
+	/*
+	 * SCL=1 SDA=0
+	 */
+	i2c_clk(I2C_CLOCK_HIGH);
+	i2c_delay(CLOCK_HIGH_TIME*2);
+	/*
+	 * SCL=1 SDA=1
+	 */
+	i2c_data(I2C_DATA_HIGH);
+	i2c_delay(STOP_CONDITION_HOLD_TIME);
+
+	i2c_dir_in();
+}
+
+/* write a byte to the i2c interface */
+
+void
+i2c_outbyte(unsigned char x)
+{
+	int i;
+
+	i2c_dir_out();
+
+	for (i = 0; i < 8; i++) {
+		if (x & 0x80) {
+			i2c_data(I2C_DATA_HIGH);
+		} else {
+			i2c_data(I2C_DATA_LOW);
+		}
+
+		i2c_delay(CLOCK_LOW_TIME/2);
+		i2c_clk(I2C_CLOCK_HIGH);
+		i2c_delay(CLOCK_HIGH_TIME);
+		i2c_clk(I2C_CLOCK_LOW);
+		i2c_delay(CLOCK_LOW_TIME/2);
+		x <<= 1;
+	}
+	i2c_data(I2C_DATA_LOW);
+	i2c_delay(CLOCK_LOW_TIME/2);
+
+	/*
+	 * enable input
+	 */
+	i2c_dir_in();
+}
+
+/* read a byte from the i2c interface */
+
+unsigned char
+i2c_inbyte(void)
+{
+	unsigned char aBitByte = 0;
+	int i;
+
+	/* Switch off I2C to get bit */
+	i2c_disable();
+	i2c_dir_in();
+	i2c_delay(CLOCK_HIGH_TIME/2);
+
+	/* Get bit */
+	aBitByte |= i2c_getbit();
+
+	/* Enable I2C */
+	i2c_enable();
+	i2c_delay(CLOCK_LOW_TIME/2);
+
+	for (i = 1; i < 8; i++) {
+		aBitByte <<= 1;
+		/* Clock pulse */
+		i2c_clk(I2C_CLOCK_HIGH);
+		i2c_delay(CLOCK_HIGH_TIME);
+		i2c_clk(I2C_CLOCK_LOW);
+		i2c_delay(CLOCK_LOW_TIME);
+
+		/* Switch off I2C to get bit */
+		i2c_disable();
+		i2c_dir_in();
+		i2c_delay(CLOCK_HIGH_TIME/2);
+
+		/* Get bit */
+		aBitByte |= i2c_getbit();
+
+		/* Enable I2C */
+		i2c_enable();
+		i2c_delay(CLOCK_LOW_TIME/2);
+	}
+	i2c_clk(I2C_CLOCK_HIGH);
+	i2c_delay(CLOCK_HIGH_TIME);
+
+	/*
+	 * we leave the clock low, getbyte is usually followed
+	 * by sendack/nack, they assume the clock to be low
+	 */
+	i2c_clk(I2C_CLOCK_LOW);
+	return aBitByte;
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: i2c_getack
+*#
+*# DESCRIPTION  : checks if ack was received from ic2
+*#
+*#--------------------------------------------------------------------------*/
+
+int
+i2c_getack(void)
+{
+	int ack = 1;
+	/*
+	 * enable output
+	 */
+	i2c_dir_out();
+	/*
+	 * Release data bus by setting
+	 * data high
+	 */
+	i2c_data(I2C_DATA_HIGH);
+	/*
+	 * enable input
+	 */
+	i2c_dir_in();
+	i2c_delay(CLOCK_HIGH_TIME/4);
+	/*
+	 * generate ACK clock pulse
+	 */
+	i2c_clk(I2C_CLOCK_HIGH);
+	/*
+	 * Use PORT PB instead of I2C
+	 * for input. (I2C not working)
+	 */
+	i2c_clk(1);
+	i2c_data(1);
+	/*
+	 * switch off I2C
+	 */
+	i2c_data(1);
+	i2c_disable();
+	i2c_dir_in();
+	/*
+	 * now wait for ack
+	 */
+	i2c_delay(CLOCK_HIGH_TIME/2);
+	/*
+	 * check for ack
+	 */
+	if(i2c_getbit())
+		ack = 0;
+	i2c_delay(CLOCK_HIGH_TIME/2);
+	if(!ack){
+		if(!i2c_getbit()) /* receiver pulld SDA low */
+			ack = 1;
+		i2c_delay(CLOCK_HIGH_TIME/2);
+	}
+
+   /*
+    * our clock is high now, make sure data is low
+    * before we enable our output. If we keep data high
+    * and enable output, we would generate a stop condition.
+    */
+   i2c_data(I2C_DATA_LOW);
+
+	/*
+	 * end clock pulse
+	 */
+	i2c_enable();
+	i2c_dir_out();
+	i2c_clk(I2C_CLOCK_LOW);
+	i2c_delay(CLOCK_HIGH_TIME/4);
+	/*
+	 * enable output
+	 */
+	i2c_dir_out();
+	/*
+	 * remove ACK clock pulse
+	 */
+	i2c_data(I2C_DATA_HIGH);
+	i2c_delay(CLOCK_LOW_TIME/2);
+	return ack;
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: I2C::sendAck
+*#
+*# DESCRIPTION  : Send ACK on received data
+*#
+*#--------------------------------------------------------------------------*/
+void
+i2c_sendack(void)
+{
+	/*
+	 * enable output
+	 */
+	i2c_delay(CLOCK_LOW_TIME);
+	i2c_dir_out();
+	/*
+	 * set ack pulse high
+	 */
+	i2c_data(I2C_DATA_LOW);
+	/*
+	 * generate clock pulse
+	 */
+	i2c_delay(CLOCK_HIGH_TIME/6);
+	i2c_clk(I2C_CLOCK_HIGH);
+	i2c_delay(CLOCK_HIGH_TIME);
+	i2c_clk(I2C_CLOCK_LOW);
+	i2c_delay(CLOCK_LOW_TIME/6);
+	/*
+	 * reset data out
+	 */
+	i2c_data(I2C_DATA_HIGH);
+	i2c_delay(CLOCK_LOW_TIME);
+
+	i2c_dir_in();
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: i2c_sendnack
+*#
+*# DESCRIPTION  : Sends NACK on received data
+*#
+*#--------------------------------------------------------------------------*/
+void
+i2c_sendnack(void)
+{
+	/*
+	 * enable output
+	 */
+	i2c_delay(CLOCK_LOW_TIME);
+	i2c_dir_out();
+	/*
+	 * set data high
+	 */
+	i2c_data(I2C_DATA_HIGH);
+	/*
+	 * generate clock pulse
+	 */
+	i2c_delay(CLOCK_HIGH_TIME/6);
+	i2c_clk(I2C_CLOCK_HIGH);
+	i2c_delay(CLOCK_HIGH_TIME);
+	i2c_clk(I2C_CLOCK_LOW);
+	i2c_delay(CLOCK_LOW_TIME);
+
+	i2c_dir_in();
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: i2c_writereg
+*#
+*# DESCRIPTION  : Writes a value to an I2C device
+*#
+*#--------------------------------------------------------------------------*/
+int
+i2c_writereg(unsigned char theSlave, unsigned char theReg,
+	     unsigned char theValue)
+{
+	int error, cntr = 3;
+	unsigned long flags;
+
+	do {
+		error = 0;
+		/*
+		 * we don't like to be interrupted
+		 */
+                local_irq_save(flags);
+
+		i2c_start();
+		/*
+		 * send slave address
+		 */
+		i2c_outbyte((theSlave & 0xfe));
+		/*
+		 * wait for ack
+		 */
+		if(!i2c_getack())
+			error = 1;
+		/*
+		 * now select register
+		 */
+		i2c_dir_out();
+		i2c_outbyte(theReg);
+		/*
+		 * now it's time to wait for ack
+		 */
+		if(!i2c_getack())
+			error |= 2;
+		/*
+		 * send register register data
+		 */
+		i2c_outbyte(theValue);
+		/*
+		 * now it's time to wait for ack
+		 */
+		if(!i2c_getack())
+			error |= 4;
+		/*
+		 * end byte stream
+		 */
+		i2c_stop();
+		/*
+		 * enable interrupt again
+		 */
+		local_irq_restore(flags);
+
+	} while(error && cntr--);
+
+	i2c_delay(CLOCK_LOW_TIME);
+
+	return -error;
+}
+
+/*#---------------------------------------------------------------------------
+*#
+*# FUNCTION NAME: i2c_readreg
+*#
+*# DESCRIPTION  : Reads a value from the decoder registers.
+*#
+*#--------------------------------------------------------------------------*/
+unsigned char
+i2c_readreg(unsigned char theSlave, unsigned char theReg)
+{
+	unsigned char b = 0;
+	int error, cntr = 3;
+	unsigned long flags;
+
+	do {
+		error = 0;
+		/*
+		 * we don't like to be interrupted
+		 */
+                local_irq_save(flags);
+		/*
+		 * generate start condition
+		 */
+		i2c_start();
+
+		/*
+		 * send slave address
+		 */
+		i2c_outbyte((theSlave & 0xfe));
+		/*
+		 * wait for ack
+		 */
+		if(!i2c_getack())
+			error = 1;
+		/*
+		 * now select register
+		 */
+		i2c_dir_out();
+		i2c_outbyte(theReg);
+		/*
+		 * now it's time to wait for ack
+		 */
+		if(!i2c_getack())
+			error = 1;
+		/*
+		 * repeat start condition
+		 */
+		i2c_delay(CLOCK_LOW_TIME);
+		i2c_start();
+		/*
+		 * send slave address
+		 */
+		i2c_outbyte(theSlave | 0x01);
+		/*
+		 * wait for ack
+		 */
+		if(!i2c_getack())
+			error = 1;
+		/*
+		 * fetch register
+		 */
+		b = i2c_inbyte();
+		/*
+		 * last received byte needs to be nacked
+		 * instead of acked
+		 */
+		i2c_sendnack();
+		/*
+		 * end sequence
+		 */
+		i2c_stop();
+		/*
+		 * enable interrupt again
+		 */
+		local_irq_restore(flags);
+
+	} while(error && cntr--);
+
+	return b;
+}
+
+static int
+i2c_open(struct inode *inode, struct file *filp)
+{
+	return 0;
+}
+
+static int
+i2c_release(struct inode *inode, struct file *filp)
+{
+	return 0;
+}
+
+/* Main device API. ioctl's to write or read to/from i2c registers.
+ */
+
+static int
+i2c_ioctl(struct inode *inode, struct file *file,
+	  unsigned int cmd, unsigned long arg)
+{
+	if(_IOC_TYPE(cmd) != ETRAXI2C_IOCTYPE) {
+		return -EINVAL;
+	}
+
+	switch (_IOC_NR(cmd)) {
+		case I2C_WRITEREG:
+			/* write to an i2c slave */
+			D(printk("i2cw %d %d %d\n",
+				 I2C_ARGSLAVE(arg),
+				 I2C_ARGREG(arg),
+				 I2C_ARGVALUE(arg)));
+
+			return i2c_writereg(I2C_ARGSLAVE(arg),
+					    I2C_ARGREG(arg),
+					    I2C_ARGVALUE(arg));
+		case I2C_READREG:
+		{
+			unsigned char val;
+			/* read from an i2c slave */
+			D(printk("i2cr %d %d ",
+				I2C_ARGSLAVE(arg),
+				I2C_ARGREG(arg)));
+			val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));
+			D(printk("= %d\n", val));
+			return val;
+		}
+		default:
+			return -EINVAL;
+
+	}
+
+	return 0;
+}
+
+static struct file_operations i2c_fops = {
+	owner:    THIS_MODULE,
+	ioctl:    i2c_ioctl,
+	open:     i2c_open,
+	release:  i2c_release,
+};
+
+int __init
+i2c_init(void)
+{
+	int res;
+
+	/* Setup and enable the Port B I2C interface */
+
+        crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT);
+        crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT);
+
+	/* register char device */
+
+	res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops);
+	if(res < 0) {
+		printk(KERN_ERR "i2c: couldn't get a major number.\n");
+		return res;
+	}
+
+	printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
+
+	return 0;
+}
+
+/* this makes sure that i2c_init is called during boot */
+
+module_init(i2c_init);
+
+/****************** END OF FILE i2c.c ********************************/

+ 15 - 0
arch/cris/arch-v32/drivers/i2c.h

@@ -0,0 +1,15 @@
+
+#include <linux/init.h>
+
+/* High level I2C actions */
+int __init i2c_init(void);
+int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
+unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg);
+
+/* Low level I2C */
+void i2c_start(void);
+void i2c_stop(void);
+void i2c_outbyte(unsigned char x);
+unsigned char i2c_inbyte(void);
+int i2c_getack(void);
+void i2c_sendack(void);

+ 219 - 0
arch/cris/arch-v32/drivers/iop_fw_load.c

@@ -0,0 +1,219 @@
+/* $Id: iop_fw_load.c,v 1.4 2005/04/07 09:27:46 larsv Exp $
+ *
+ * Firmware loader for ETRAX FS IO-Processor
+ *
+ * Copyright (C) 2004  Axis Communications AB
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include <asm/arch/hwregs/reg_map.h>
+#include <asm/arch/hwregs/iop/iop_reg_space.h>
+#include <asm/arch/hwregs/iop/iop_mpu_macros.h>
+#include <asm/arch/hwregs/iop/iop_mpu_defs.h>
+#include <asm/arch/hwregs/iop/iop_spu_defs.h>
+#include <asm/arch/hwregs/iop/iop_sw_cpu_defs.h>
+
+#define IOP_TIMEOUT 100
+
+static struct device iop_spu_device[2] = {
+	{ .bus_id =     "iop-spu0", },
+	{ .bus_id =     "iop-spu1", },
+};
+
+static struct device iop_mpu_device = {
+	.bus_id =       "iop-mpu",
+};
+
+static int wait_mpu_idle(void)
+{
+	reg_iop_mpu_r_stat mpu_stat;
+	unsigned int timeout = IOP_TIMEOUT;
+
+	do {
+		mpu_stat = REG_RD(iop_mpu, regi_iop_mpu, r_stat);
+	} while (mpu_stat.instr_reg_busy == regk_iop_mpu_yes && --timeout > 0);
+	if (timeout == 0) {
+		printk(KERN_ERR "Timeout waiting for MPU to be idle\n");
+		return -EBUSY;
+	}
+	return 0;
+}
+
+int iop_fw_load_spu(const unsigned char *fw_name, unsigned int spu_inst)
+{
+	reg_iop_sw_cpu_rw_mc_ctrl mc_ctrl = {
+		.wr_spu0_mem =    regk_iop_sw_cpu_no,
+		.wr_spu1_mem =    regk_iop_sw_cpu_no,
+		.size =           4,
+		.cmd =            regk_iop_sw_cpu_reg_copy,
+		.keep_owner =     regk_iop_sw_cpu_yes
+	};
+	reg_iop_spu_rw_ctrl spu_ctrl = {
+		.en  =            regk_iop_spu_no,
+		.fsm =            regk_iop_spu_no,
+	};
+	reg_iop_sw_cpu_r_mc_stat mc_stat;
+        const struct firmware *fw_entry;
+	u32 *data;
+	unsigned int timeout;
+	int retval, i;
+
+	if (spu_inst > 1)
+		return -ENODEV;
+
+	/* get firmware */
+	retval = request_firmware(&fw_entry,
+				  fw_name,
+				  &iop_spu_device[spu_inst]);
+	if (retval != 0)
+	{
+		printk(KERN_ERR
+		       "iop_load_spu: Failed to load firmware \"%s\"\n",
+		       fw_name);
+		return retval;
+	}
+	data = (u32 *) fw_entry->data;
+
+	/* acquire ownership of memory controller */
+	switch (spu_inst) {
+	case 0:
+		mc_ctrl.wr_spu0_mem = regk_iop_sw_cpu_yes;
+		REG_WR(iop_spu, regi_iop_spu0, rw_ctrl, spu_ctrl);
+		break;
+	case 1:
+		mc_ctrl.wr_spu1_mem = regk_iop_sw_cpu_yes;
+		REG_WR(iop_spu, regi_iop_spu1, rw_ctrl, spu_ctrl);
+		break;
+	}
+	timeout = IOP_TIMEOUT;
+	do {
+		REG_WR(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_ctrl, mc_ctrl);
+		mc_stat = REG_RD(iop_sw_cpu, regi_iop_sw_cpu, r_mc_stat);
+	} while (mc_stat.owned_by_cpu == regk_iop_sw_cpu_no && --timeout > 0);
+	if (timeout == 0) {
+		printk(KERN_ERR "Timeout waiting to acquire MC\n");
+		retval = -EBUSY;
+		goto out;
+	}
+
+	/* write to SPU memory */
+	for (i = 0; i < (fw_entry->size/4); i++) {
+		switch (spu_inst) {
+		case 0:
+			REG_WR_INT(iop_spu, regi_iop_spu0, rw_seq_pc, (i*4));
+			break;
+		case 1:
+			REG_WR_INT(iop_spu, regi_iop_spu1, rw_seq_pc, (i*4));
+			break;
+		}
+		REG_WR_INT(iop_sw_cpu, regi_iop_sw_cpu, rw_mc_data, *data);
+		data++;
+	}
+
+	/* release ownership of memory controller */
+	(void) REG_RD(iop_sw_cpu, regi_iop_sw_cpu, rs_mc_data);
+
+ out:
+	release_firmware(fw_entry);
+	return retval;
+}
+
+int iop_fw_load_mpu(unsigned char *fw_name)
+{
+	const unsigned int start_addr = 0;
+	reg_iop_mpu_rw_ctrl mpu_ctrl;
+        const struct firmware *fw_entry;
+	u32 *data;
+	int retval, i;
+
+	/* get firmware */
+	retval = request_firmware(&fw_entry, fw_name, &iop_mpu_device);
+	if (retval != 0)
+	{
+		printk(KERN_ERR
+		       "iop_load_spu: Failed to load firmware \"%s\"\n",
+		       fw_name);
+		return retval;
+	}
+	data = (u32 *) fw_entry->data;
+
+	/* disable MPU */
+	mpu_ctrl.en = regk_iop_mpu_no;
+	REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
+	/* put start address in R0 */
+	REG_WR_VECT(iop_mpu, regi_iop_mpu, rw_r, 0, start_addr);
+	/* write to memory by executing 'SWX i, 4, R0' for each word */
+	if ((retval = wait_mpu_idle()) != 0)
+		goto out;
+	REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_SWX_IIR_INSTR(0, 4, 0));
+	for (i = 0; i < (fw_entry->size / 4); i++) {
+		REG_WR_INT(iop_mpu, regi_iop_mpu, rw_immediate, *data);
+		if ((retval = wait_mpu_idle()) != 0)
+			goto out;
+		data++;
+	}
+
+ out:
+	release_firmware(fw_entry);
+	return retval;
+}
+
+int iop_start_mpu(unsigned int start_addr)
+{
+	reg_iop_mpu_rw_ctrl mpu_ctrl = { .en = regk_iop_mpu_yes };
+	int retval;
+
+	/* disable MPU */
+	if ((retval = wait_mpu_idle()) != 0)
+		goto out;
+	REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_HALT());
+	if ((retval = wait_mpu_idle()) != 0)
+		goto out;
+	/* set PC and wait for it to bite */
+	if ((retval = wait_mpu_idle()) != 0)
+		goto out;
+	REG_WR_INT(iop_mpu, regi_iop_mpu, rw_instr, MPU_BA_I(start_addr));
+	if ((retval = wait_mpu_idle()) != 0)
+		goto out;
+	/* make sure the MPU starts executing with interrupts disabled */
+	REG_WR(iop_mpu, regi_iop_mpu, rw_instr, MPU_DI());
+	if ((retval = wait_mpu_idle()) != 0)
+		goto out;
+	/* enable MPU */
+	REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
+ out:
+	return retval;
+}
+
+static int __init iop_fw_load_init(void)
+{
+	device_initialize(&iop_spu_device[0]);
+	kobject_set_name(&iop_spu_device[0].kobj, "iop-spu0");
+	kobject_add(&iop_spu_device[0].kobj);
+	device_initialize(&iop_spu_device[1]);
+	kobject_set_name(&iop_spu_device[1].kobj, "iop-spu1");
+	kobject_add(&iop_spu_device[1].kobj);
+	device_initialize(&iop_mpu_device);
+	kobject_set_name(&iop_mpu_device.kobj, "iop-mpu");
+	kobject_add(&iop_mpu_device.kobj);
+	return 0;
+}
+
+static void __exit iop_fw_load_exit(void)
+{
+}
+
+module_init(iop_fw_load_init);
+module_exit(iop_fw_load_exit);
+
+MODULE_DESCRIPTION("ETRAX FS IO-Processor Firmware Loader");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(iop_fw_load_spu);
+EXPORT_SYMBOL(iop_fw_load_mpu);
+EXPORT_SYMBOL(iop_start_mpu);

+ 157 - 0
arch/cris/arch-v32/drivers/nandflash.c

@@ -0,0 +1,157 @@
+/*
+ *  arch/cris/arch-v32/drivers/nandflash.c
+ *
+ *  Copyright (c) 2004
+ *
+ *  Derived from drivers/mtd/nand/spia.c
+ * 	  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+ * $Id: nandflash.c,v 1.3 2005/06/01 10:57:12 starvik Exp $
+ *
+ * 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 <linux/version.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/arch/memmap.h>
+#include <asm/arch/hwregs/reg_map.h>
+#include <asm/arch/hwregs/reg_rdwr.h>
+#include <asm/arch/hwregs/gio_defs.h>
+#include <asm/arch/hwregs/bif_core_defs.h>
+#include <asm/io.h>
+
+#define CE_BIT 4
+#define CLE_BIT 5
+#define ALE_BIT 6
+#define BY_BIT 7
+
+static struct mtd_info *crisv32_mtd = NULL;
+/*
+ *	hardware specific access to control-lines
+*/
+static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+	unsigned long flags;
+	reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout);
+
+	local_irq_save(flags);
+	switch(cmd){
+		case NAND_CTL_SETCLE:
+		     dout.data |= (1<<CLE_BIT);
+		     break;
+		case NAND_CTL_CLRCLE:
+		     dout.data &= ~(1<<CLE_BIT);
+		     break;
+		case NAND_CTL_SETALE:
+		     dout.data |= (1<<ALE_BIT);
+		     break;
+		case NAND_CTL_CLRALE:
+		     dout.data &= ~(1<<ALE_BIT);
+		     break;
+		case NAND_CTL_SETNCE:
+		     dout.data |= (1<<CE_BIT);
+		     break;
+		case NAND_CTL_CLRNCE:
+		     dout.data &= ~(1<<CE_BIT);
+		     break;
+	}
+	REG_WR(gio, regi_gio, rw_pa_dout, dout);
+	local_irq_restore(flags);
+}
+
+/*
+*	read device ready pin
+*/
+int crisv32_device_ready(struct mtd_info *mtd)
+{
+	reg_gio_r_pa_din din = REG_RD(gio, regi_gio, r_pa_din);
+	return ((din.data & (1 << BY_BIT)) >> BY_BIT);
+}
+
+/*
+ * Main initialization routine
+ */
+struct mtd_info* __init crisv32_nand_flash_probe (void)
+{
+	void __iomem *read_cs;
+	void __iomem *write_cs;
+
+	reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, rw_grp3_cfg);
+	reg_gio_rw_pa_oe pa_oe = REG_RD(gio, regi_gio, rw_pa_oe);
+	struct nand_chip *this;
+	int err = 0;
+
+	/* Allocate memory for MTD device structure and private data */
+	crisv32_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
+				GFP_KERNEL);
+	if (!crisv32_mtd) {
+		printk ("Unable to allocate CRISv32 NAND MTD device structure.\n");
+		err = -ENOMEM;
+		return NULL;
+	}
+
+	read_cs = ioremap(MEM_CSP0_START | MEM_NON_CACHEABLE, 8192);
+	write_cs = ioremap(MEM_CSP1_START | MEM_NON_CACHEABLE, 8192);
+
+	if (!read_cs || !write_cs) {
+		printk("CRISv32 NAND ioremap failed\n");
+		err = -EIO;
+		goto out_mtd;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&crisv32_mtd[1]);
+
+	pa_oe.oe |= 1 << CE_BIT;
+	pa_oe.oe |= 1 << ALE_BIT;
+	pa_oe.oe |= 1 << CLE_BIT;
+	pa_oe.oe &= ~ (1 << BY_BIT);
+	REG_WR(gio, regi_gio, rw_pa_oe, pa_oe);
+
+	bif_cfg.gated_csp0 = regk_bif_core_rd;
+	bif_cfg.gated_csp1 = regk_bif_core_wr;
+	REG_WR(bif_core, regi_bif_core, rw_grp3_cfg, bif_cfg);
+
+	/* Initialize structures */
+	memset((char *) crisv32_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	crisv32_mtd->priv = this;
+
+	/* Set address of NAND IO lines */
+	this->IO_ADDR_R = read_cs;
+	this->IO_ADDR_W = write_cs;
+	this->hwcontrol = crisv32_hwcontrol;
+	this->dev_ready = crisv32_device_ready;
+	/* 20 us command delay time */
+	this->chip_delay = 20;
+	this->eccmode = NAND_ECC_SOFT;
+
+	/* Enable the following for a flash based bad block table */
+	this->options = NAND_USE_FLASH_BBT;
+
+	/* Scan to find existance of the device */
+	if (nand_scan (crisv32_mtd, 1)) {
+		err = -ENXIO;
+		goto out_ior;
+	}
+
+	return crisv32_mtd;
+
+out_ior:
+	iounmap((void *)read_cs);
+	iounmap((void *)write_cs);
+out_mtd:
+	kfree (crisv32_mtd);
+        return NULL;
+}
+

+ 341 - 0
arch/cris/arch-v32/drivers/pcf8563.c

@@ -0,0 +1,341 @@
+/*
+ * PCF8563 RTC
+ *
+ * From Phillips' datasheet:
+ *
+ * The PCF8563 is a CMOS real-time clock/calendar optimized for low power
+ * consumption. A programmable clock output, interupt output and voltage
+ * low detector are also provided. All address and data are transferred
+ * serially via two-line bidirectional I2C-bus. Maximum bus speed is
+ * 400 kbits/s. The built-in word address register is incremented
+ * automatically after each written or read byte.
+ *
+ * Copyright (c) 2002-2003, Axis Communications AB
+ * All rights reserved.
+ *
+ * Author: Tobias Anderberg <tobiasa@axis.com>.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/bcd.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/rtc.h>
+
+#include "i2c.h"
+
+#define PCF8563_MAJOR	121	/* Local major number. */
+#define DEVICE_NAME	"rtc"	/* Name which is registered in /proc/devices. */
+#define PCF8563_NAME	"PCF8563"
+#define DRIVER_VERSION	"$Revision: 1.1 $"
+
+/* Two simple wrapper macros, saves a few keystrokes. */
+#define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
+#define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y)
+
+static const unsigned char days_in_month[] =
+	{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+int pcf8563_open(struct inode *, struct file *);
+int pcf8563_release(struct inode *, struct file *);
+
+static struct file_operations pcf8563_fops = {
+	owner: THIS_MODULE,
+	ioctl: pcf8563_ioctl,
+	open: pcf8563_open,
+	release: pcf8563_release,
+};
+
+unsigned char
+pcf8563_readreg(int reg)
+{
+	unsigned char res = rtc_read(reg);
+
+	/* The PCF8563 does not return 0 for unimplemented bits */
+	switch (reg) {
+		case RTC_SECONDS:
+		case RTC_MINUTES:
+			res &= 0x7F;
+			break;
+		case RTC_HOURS:
+		case RTC_DAY_OF_MONTH:
+			res &= 0x3F;
+			break;
+		case RTC_WEEKDAY:
+			res &= 0x07;
+			break;
+		case RTC_MONTH:
+			res &= 0x1F;
+			break;
+		case RTC_CONTROL1:
+			res &= 0xA8;
+			break;
+		case RTC_CONTROL2:
+			res &= 0x1F;
+			break;
+		case RTC_CLOCKOUT_FREQ:
+		case RTC_TIMER_CONTROL:
+			res &= 0x83;
+			break;
+	}
+	return res;
+}
+
+void
+pcf8563_writereg(int reg, unsigned char val)
+{
+#ifdef CONFIG_ETRAX_RTC_READONLY
+	if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR))
+		return;
+#endif
+
+	rtc_write(reg, val);
+}
+
+void
+get_rtc_time(struct rtc_time *tm)
+{
+	tm->tm_sec  = rtc_read(RTC_SECONDS);
+	tm->tm_min  = rtc_read(RTC_MINUTES);
+	tm->tm_hour = rtc_read(RTC_HOURS);
+	tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH);
+	tm->tm_wday = rtc_read(RTC_WEEKDAY);
+	tm->tm_mon  = rtc_read(RTC_MONTH);
+	tm->tm_year = rtc_read(RTC_YEAR);
+
+	if (tm->tm_sec & 0x80)
+		printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
+		       "information is no longer guaranteed!\n", PCF8563_NAME);
+
+	tm->tm_year  = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0);
+	tm->tm_sec  &= 0x7F;
+	tm->tm_min  &= 0x7F;
+	tm->tm_hour &= 0x3F;
+	tm->tm_mday &= 0x3F;
+	tm->tm_wday &= 0x07; /* Not coded in BCD. */
+	tm->tm_mon  &= 0x1F;
+
+	BCD_TO_BIN(tm->tm_sec);
+	BCD_TO_BIN(tm->tm_min);
+	BCD_TO_BIN(tm->tm_hour);
+	BCD_TO_BIN(tm->tm_mday);
+	BCD_TO_BIN(tm->tm_mon);
+	tm->tm_mon--; /* Month is 1..12 in RTC but 0..11 in linux */
+}
+
+int __init
+pcf8563_init(void)
+{
+	/* Initiate the i2c protocol. */
+	i2c_init();
+
+	/*
+	 * First of all we need to reset the chip. This is done by
+	 * clearing control1, control2 and clk freq and resetting
+	 * all alarms.
+	 */
+	if (rtc_write(RTC_CONTROL1, 0x00) < 0)
+		goto err;
+
+	if (rtc_write(RTC_CONTROL2, 0x00) < 0)
+		goto err;
+
+	if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0)
+		goto err;
+
+	if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0)
+		goto err;
+
+	/* Reset the alarms. */
+	if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0)
+		goto err;
+
+	if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0)
+		goto err;
+
+	if (rtc_write(RTC_DAY_ALARM, 0x80) < 0)
+		goto err;
+
+	if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0)
+		goto err;
+
+	if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) {
+		printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n",
+		       PCF8563_NAME, PCF8563_MAJOR);
+		return -1;
+	}
+
+	printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION);
+
+	/* Check for low voltage, and warn about it.. */
+	if (rtc_read(RTC_SECONDS) & 0x80)
+		printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
+		       "information is no longer guaranteed!\n", PCF8563_NAME);
+
+	return 0;
+
+err:
+	printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME);
+	return -1;
+}
+
+void __exit
+pcf8563_exit(void)
+{
+	if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) {
+		printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME);
+	}
+}
+
+/*
+ * ioctl calls for this driver. Why return -ENOTTY upon error? Because
+ * POSIX says so!
+ */
+int
+pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	/* Some sanity checks. */
+	if (_IOC_TYPE(cmd) != RTC_MAGIC)
+		return -ENOTTY;
+
+	if (_IOC_NR(cmd) > RTC_MAX_IOCTL)
+		return -ENOTTY;
+
+	switch (cmd) {
+		case RTC_RD_TIME:
+		{
+			struct rtc_time tm;
+
+			memset(&tm, 0, sizeof (struct rtc_time));
+			get_rtc_time(&tm);
+
+			if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) {
+				return -EFAULT;
+			}
+
+			return 0;
+		}
+
+		case RTC_SET_TIME:
+		{
+#ifdef CONFIG_ETRAX_RTC_READONLY
+			return -EPERM;
+#else
+			int leap;
+			int year;
+			int century;
+			struct rtc_time tm;
+
+			if (!capable(CAP_SYS_TIME))
+				return -EPERM;
+
+			if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm))
+				return -EFAULT;
+
+			/* Convert from struct tm to struct rtc_time. */
+			tm.tm_year += 1900;
+			tm.tm_mon += 1;
+
+			/*
+			 * Check if tm.tm_year is a leap year. A year is a leap
+			 * year if it is divisible by 4 but not 100, except
+			 * that years divisible by 400 _are_ leap years.
+			 */
+			year = tm.tm_year;
+			leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
+
+			/* Perform some sanity checks. */
+			if ((tm.tm_year < 1970) ||
+			    (tm.tm_mon > 12) ||
+			    (tm.tm_mday == 0) ||
+			    (tm.tm_mday > days_in_month[tm.tm_mon] + leap) ||
+			    (tm.tm_wday >= 7) ||
+			    (tm.tm_hour >= 24) ||
+			    (tm.tm_min >= 60) ||
+			    (tm.tm_sec >= 60))
+				return -EINVAL;
+
+			century = (tm.tm_year >= 2000) ? 0x80 : 0;
+			tm.tm_year = tm.tm_year % 100;
+
+			BIN_TO_BCD(tm.tm_year);
+			BIN_TO_BCD(tm.tm_mday);
+			BIN_TO_BCD(tm.tm_hour);
+			BIN_TO_BCD(tm.tm_min);
+			BIN_TO_BCD(tm.tm_sec);
+			tm.tm_mon |= century;
+
+			rtc_write(RTC_YEAR, tm.tm_year);
+			rtc_write(RTC_MONTH, tm.tm_mon);
+			rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */
+			rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday);
+			rtc_write(RTC_HOURS, tm.tm_hour);
+			rtc_write(RTC_MINUTES, tm.tm_min);
+			rtc_write(RTC_SECONDS, tm.tm_sec);
+
+			return 0;
+#endif /* !CONFIG_ETRAX_RTC_READONLY */
+		}
+
+		case RTC_VLOW_RD:
+		{
+			int vl_bit = 0;
+
+			if (rtc_read(RTC_SECONDS) & 0x80) {
+				vl_bit = 1;
+				printk(KERN_WARNING "%s: RTC Voltage Low - reliable "
+				       "date/time information is no longer guaranteed!\n",
+				       PCF8563_NAME);
+			}
+			if (copy_to_user((int *) arg, &vl_bit, sizeof(int)))
+				return -EFAULT;
+
+			return 0;
+		}
+
+		case RTC_VLOW_SET:
+		{
+			/* Clear the VL bit in the seconds register */
+			int ret = rtc_read(RTC_SECONDS);
+
+			rtc_write(RTC_SECONDS, (ret & 0x7F));
+
+			return 0;
+		}
+
+		default:
+			return -ENOTTY;
+	}
+
+	return 0;
+}
+
+int
+pcf8563_open(struct inode *inode, struct file *filp)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+int
+pcf8563_release(struct inode *inode, struct file *filp)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+module_init(pcf8563_init);
+module_exit(pcf8563_exit);

Някои файлове не бяха показани, защото твърде много файлове са промени