diff options
author | Mikael Starvik <mikael.starvik@axis.com> | 2005-07-27 11:44:35 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-27 16:26:00 -0700 |
commit | 63245d2cde2be64f172388c2c50862f233c05700 (patch) | |
tree | c5f41dca80044bf4820e9c65eaa9844eb89493f6 /arch/cris | |
parent | 7e9204265b4ec6680fad9abc7a78b94087983916 (diff) | |
download | op-kernel-dev-63245d2cde2be64f172388c2c50862f233c05700.zip op-kernel-dev-63245d2cde2be64f172388c2c50862f233c05700.tar.gz |
[PATCH] CRIS update: I/O and DMA allocator
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>
Diffstat (limited to 'arch/cris')
-rw-r--r-- | arch/cris/arch-v10/kernel/dma.c | 287 | ||||
-rw-r--r-- | arch/cris/arch-v10/kernel/head.S | 126 | ||||
-rw-r--r-- | arch/cris/arch-v10/kernel/io_interface_mux.c | 879 | ||||
-rw-r--r-- | arch/cris/arch-v10/kernel/shadows.c | 3 |
4 files changed, 1223 insertions, 72 deletions
diff --git a/arch/cris/arch-v10/kernel/dma.c b/arch/cris/arch-v10/kernel/dma.c new file mode 100644 index 0000000..e9a0311 --- /dev/null +++ b/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); diff --git a/arch/cris/arch-v10/kernel/head.S b/arch/cris/arch-v10/kernel/head.S index 2c1dd11..f00c145 100644 --- a/arch/cris/arch-v10/kernel/head.S +++ b/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 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 index 0000000..29d48ad --- /dev/null +++ b/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); diff --git a/arch/cris/arch-v10/kernel/shadows.c b/arch/cris/arch-v10/kernel/shadows.c index 561a890..38fd44d 100644 --- a/arch/cris/arch-v10/kernel/shadows.c +++ b/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; |