[PATCH] CRIS update: I/O and DMA allocator
authorMikael Starvik <mikael.starvik@axis.com>
Wed, 27 Jul 2005 18:44:35 +0000 (11:44 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Wed, 27 Jul 2005 23:26:00 +0000 (16:26 -0700)
Added I/O and DMA allocators to be used by drivers.

Signed-off-by: Mikael Starvik <starvik@axis.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/cris/arch-v10/kernel/dma.c [new file with mode: 0644]
arch/cris/arch-v10/kernel/head.S
arch/cris/arch-v10/kernel/io_interface_mux.c [new file with mode: 0644]
arch/cris/arch-v10/kernel/shadows.c
include/asm-cris/arch-v10/dma.h
include/asm-cris/arch-v10/io.h
include/asm-cris/arch-v10/io_interface_mux.h [new file with mode: 0644]

diff --git a/arch/cris/arch-v10/kernel/dma.c b/arch/cris/arch-v10/kernel/dma.c
new file mode 100644 (file)
index 0000000..e9a0311
--- /dev/null
@@ -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);
index 2c1dd1184a8f3f8a571da59d35bb7a7777f233e6..f00c145b43f164e221c97b8593abefac3eb8d61e 100644 (file)
@@ -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
  *
 
 #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
diff --git a/arch/cris/arch-v10/kernel/io_interface_mux.c b/arch/cris/arch-v10/kernel/io_interface_mux.c
new file mode 100644 (file)
index 0000000..29d48ad
--- /dev/null
@@ -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);
index 561a890a8e4cfbc28f713d8cf4701a4bfb78747c..38fd44dfbc5b3e506f2a9c85c2d6a7e6bfb350e1 100644 (file)
@@ -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;
index 9e078b9bc93413a7ae4d692f4f5bdc7c0b82264d..ecb9dba6fa4fc462b18e8738c16da7ea5b42e2c8 100644 (file)
 #define USB_RX_DMA_NBR 9
 
 #endif
+
+enum dma_owner
+{
+  dma_eth,
+  dma_ser0,
+  dma_ser1, /* Async and sync */
+  dma_ser2,
+  dma_ser3, /* Async and sync */
+  dma_ata,
+  dma_par0,
+  dma_par1,
+  dma_ext0,
+  dma_ext1,
+  dma_int6,
+  dma_int7,
+  dma_usb,
+  dma_scsi0,
+  dma_scsi1
+};
+
+/* Masks used by cris_request_dma options: */
+#define DMA_VERBOSE_ON_ERROR    (1<<0)
+#define DMA_PANIC_ON_ERROR     ((1<<1)|DMA_VERBOSE_ON_ERROR)
+
+int cris_request_dma(unsigned int dmanr, const char * device_id,
+                     unsigned options, enum dma_owner owner);
+
+void cris_free_dma(unsigned int dmanr, const char * device_id);
index 0bc38a0313c1d1c8d5380959e276f3e842517c1b..dd39198ec67d31cea570d54e7eec84db883007f3 100644 (file)
@@ -6,6 +6,7 @@
 
 /* Etrax shadow registers - which live in arch/cris/kernel/shadows.c */
 
+extern unsigned long gen_config_ii_shadow;
 extern unsigned long port_g_data_shadow;
 extern unsigned char port_pa_dir_shadow;
 extern unsigned char port_pa_data_shadow;
diff --git a/include/asm-cris/arch-v10/io_interface_mux.h b/include/asm-cris/arch-v10/io_interface_mux.h
new file mode 100644 (file)
index 0000000..d925000
--- /dev/null
@@ -0,0 +1,75 @@
+/* IO interface mux allocator for ETRAX100LX.
+ * Copyright 2004, Axis Communications AB
+ * $Id: io_interface_mux.h,v 1.1 2004/12/13 12:21:53 starvik Exp $
+ */
+
+
+#ifndef _IO_INTERFACE_MUX_H
+#define _IO_INTERFACE_MUX_H
+
+
+/* C.f. ETRAX100LX Designer's Reference 20.9 */
+
+/* The order in enum must match the order of interfaces[] in
+ * io_interface_mux.c */
+enum cris_io_interface {
+       /* Begin Non-multiplexed interfaces */
+       if_eth = 0,
+       if_serial_0,
+       /* End Non-multiplexed interfaces */
+       if_serial_1,
+       if_serial_2,
+       if_serial_3,
+       if_sync_serial_1,
+       if_sync_serial_3,
+       if_shared_ram,
+       if_shared_ram_w,
+       if_par_0,
+       if_par_1,
+       if_par_w,
+       if_scsi8_0,
+       if_scsi8_1,
+       if_scsi_w,
+       if_ata,
+       if_csp,
+       if_i2c,
+       if_usb_1,
+       if_usb_2,
+       /* GPIO pins */
+       if_gpio_grp_a,
+       if_gpio_grp_b,
+       if_gpio_grp_c,
+       if_gpio_grp_d,
+       if_gpio_grp_e,
+       if_gpio_grp_f,
+       if_max_interfaces,
+       if_unclaimed
+};
+
+int cris_request_io_interface(enum cris_io_interface ioif, const char *device_id);
+
+void cris_free_io_interface(enum cris_io_interface ioif);
+
+/* 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);
+
+/* 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);
+
+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));
+
+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));
+
+#endif /* _IO_INTERFACE_MUX_H */