| /* | 
 |  * Etrax general port I/O device | 
 |  * | 
 |  * Copyright (c) 1999-2007 Axis Communications AB | 
 |  * | 
 |  * Authors:    Bjorn Wesen      (initial version) | 
 |  *             Ola Knutsson     (LED handling) | 
 |  *             Johan Adolfsson  (read/set directions, write, port G) | 
 |  */ | 
 |  | 
 |  | 
 | #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/smp_lock.h> | 
 | #include <linux/string.h> | 
 | #include <linux/poll.h> | 
 | #include <linux/init.h> | 
 | #include <linux/interrupt.h> | 
 |  | 
 | #include <asm/etraxgpio.h> | 
 | #include <arch/svinto.h> | 
 | #include <asm/io.h> | 
 | #include <asm/system.h> | 
 | #include <asm/irq.h> | 
 | #include <arch/io_interface_mux.h> | 
 |  | 
 | #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 __user *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; | 
 | 	/* These fields are for PA and PB only */ | 
 | 	volatile unsigned char *port, *shadow; | 
 | 	volatile unsigned char *dir, *dir_shadow; | 
 | 	unsigned char changeable_dir; | 
 | 	unsigned char changeable_bits; | 
 | 	unsigned char clk_mask; | 
 | 	unsigned char data_mask; | 
 | 	unsigned char write_msb; | 
 | 	unsigned char pad1, pad2, pad3; | 
 | 	/* 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; | 
 |  | 
 | static int gpio_some_alarms; /* Set if someone uses alarm */ | 
 | static unsigned long gpio_pa_irq_enabled_mask; | 
 |  | 
 | 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) | 
 |  | 
 | static volatile unsigned char *ports[NUM_PORTS] = { | 
 | 	R_PORT_PA_DATA, | 
 | 	R_PORT_PB_DATA, | 
 | }; | 
 | static volatile unsigned char *shads[NUM_PORTS] = { | 
 | 	&port_pa_data_shadow, | 
 | 	&port_pb_data_shadow | 
 | }; | 
 |  | 
 | /* What direction bits that are user changeable 1=changeable*/ | 
 | #ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR | 
 | #define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00 | 
 | #endif | 
 | #ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR | 
 | #define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00 | 
 | #endif | 
 |  | 
 | #ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS | 
 | #define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF | 
 | #endif | 
 | #ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS | 
 | #define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF | 
 | #endif | 
 |  | 
 |  | 
 | static unsigned char changeable_dir[NUM_PORTS] = { | 
 | 	CONFIG_ETRAX_PA_CHANGEABLE_DIR, | 
 | 	CONFIG_ETRAX_PB_CHANGEABLE_DIR | 
 | }; | 
 | static unsigned char changeable_bits[NUM_PORTS] = { | 
 | 	CONFIG_ETRAX_PA_CHANGEABLE_BITS, | 
 | 	CONFIG_ETRAX_PB_CHANGEABLE_BITS | 
 | }; | 
 |  | 
 | static volatile unsigned char *dir[NUM_PORTS] = { | 
 | 	R_PORT_PA_DIR, | 
 | 	R_PORT_PB_DIR | 
 | }; | 
 |  | 
 | static volatile unsigned char *dir_shadow[NUM_PORTS] = { | 
 | 	&port_pa_dir_shadow, | 
 | 	&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. */ | 
 | static unsigned long changeable_dir_g; | 
 | static unsigned long dir_g_in_bits; | 
 | static unsigned long dir_g_out_bits; | 
 | static unsigned long dir_g_shadow; /* 1=output */ | 
 |  | 
 | #define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B) | 
 |  | 
 |  | 
 | static unsigned int gpio_poll(struct file *file, poll_table *wait) | 
 | { | 
 | 	unsigned int mask = 0; | 
 | 	struct gpio_private *priv = file->private_data; | 
 | 	unsigned long data; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&gpio_lock, flags); | 
 |  | 
 | 	poll_wait(file, &priv->alarm_wq, wait); | 
 | 	if (priv->minor == GPIO_MINOR_A) { | 
 | 		unsigned long tmp; | 
 | 		data = *R_PORT_PA_DATA; | 
 | 		/* PA has support for high level interrupt - | 
 | 		 * lets activate for those low and with highalarm set | 
 | 		 */ | 
 | 		tmp = ~data & priv->highalarm & 0xFF; | 
 | 		tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR); | 
 |  | 
 | 		gpio_pa_irq_enabled_mask |= tmp; | 
 | 		*R_IRQ_MASK1_SET = tmp; | 
 | 	} else if (priv->minor == GPIO_MINOR_B) | 
 | 		data = *R_PORT_PB_DATA; | 
 | 	else if (priv->minor == GPIO_MINOR_G) | 
 | 		data = *R_PORT_G_DATA; | 
 | 	else { | 
 | 		mask = 0; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	if ((data & priv->highalarm) || | 
 | 	    (~data & priv->lowalarm)) { | 
 | 		mask = POLLIN|POLLRDNORM; | 
 | 	} | 
 |  | 
 | out: | 
 | 	spin_unlock_irqrestore(&gpio_lock, flags); | 
 | 	DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); | 
 |  | 
 | 	return mask; | 
 | } | 
 |  | 
 | int etrax_gpio_wake_up_check(void) | 
 | { | 
 | 	struct gpio_private *priv; | 
 | 	unsigned long data = 0; | 
 |         int ret = 0; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&gpio_lock, flags); | 
 | 	priv = alarmlist; | 
 | 	while (priv) { | 
 | 		if (USE_PORTS(priv)) | 
 | 			data = *priv->port; | 
 | 		else if (priv->minor == GPIO_MINOR_G) | 
 | 			data = *R_PORT_G_DATA; | 
 |  | 
 | 		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; | 
 | 	} | 
 | 	spin_unlock_irqrestore(&gpio_lock, flags); | 
 |         return ret; | 
 | } | 
 |  | 
 | static irqreturn_t | 
 | gpio_poll_timer_interrupt(int irq, void *dev_id) | 
 | { | 
 | 	if (gpio_some_alarms) { | 
 | 		etrax_gpio_wake_up_check(); | 
 |                 return IRQ_HANDLED; | 
 | 	} | 
 |         return IRQ_NONE; | 
 | } | 
 |  | 
 | static irqreturn_t | 
 | gpio_interrupt(int irq, void *dev_id) | 
 | { | 
 | 	unsigned long tmp; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&gpio_lock, flags); | 
 |  | 
 | 	/* Find what PA interrupts are active */ | 
 | 	tmp = (*R_IRQ_READ1); | 
 |  | 
 | 	/* Find those that we have enabled */ | 
 | 	tmp &= gpio_pa_irq_enabled_mask; | 
 |  | 
 | 	/* Clear them.. */ | 
 | 	*R_IRQ_MASK1_CLR = tmp; | 
 | 	gpio_pa_irq_enabled_mask &= ~tmp; | 
 |  | 
 | 	spin_unlock_irqrestore(&gpio_lock, flags); | 
 |  | 
 | 	if (gpio_some_alarms) | 
 | 		return IRQ_RETVAL(etrax_gpio_wake_up_check()); | 
 |  | 
 |         return IRQ_NONE; | 
 | } | 
 |  | 
 | static void gpio_write_bit(struct gpio_private *priv, | 
 | 	unsigned char data, int bit) | 
 | { | 
 | 	*priv->port = *priv->shadow &= ~(priv->clk_mask); | 
 | 	if (data & 1 << bit) | 
 | 		*priv->port = *priv->shadow |= priv->data_mask; | 
 | 	else | 
 | 		*priv->port = *priv->shadow &= ~(priv->data_mask); | 
 |  | 
 | 	/* For FPGA: min 5.0ns (DCC) before CCLK high */ | 
 | 	*priv->port = *priv->shadow |= priv->clk_mask; | 
 | } | 
 |  | 
 | static void gpio_write_byte(struct gpio_private *priv, unsigned char data) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	if (priv->write_msb) | 
 | 		for (i = 7; i >= 0; i--) | 
 | 			gpio_write_bit(priv, data, i); | 
 | 	else | 
 | 		for (i = 0; i <= 7; i++) | 
 | 			gpio_write_bit(priv, data, i); | 
 | } | 
 |  | 
 | static ssize_t gpio_write(struct file *file, const char __user *buf, | 
 | 	size_t count, loff_t *off) | 
 | { | 
 | 	struct gpio_private *priv = file->private_data; | 
 | 	unsigned long flags; | 
 | 	ssize_t retval = count; | 
 |  | 
 | 	if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) | 
 | 		return -EFAULT; | 
 |  | 
 | 	if (!access_ok(VERIFY_READ, buf, count)) | 
 | 		return -EFAULT; | 
 |  | 
 | 	spin_lock_irqsave(&gpio_lock, flags); | 
 |  | 
 | 	/* It must have been configured using the IO_CFG_WRITE_MODE */ | 
 | 	/* Perhaps a better error code? */ | 
 | 	if (priv->clk_mask == 0 || priv->data_mask == 0) { | 
 | 		retval = -EPERM; | 
 | 		goto out; | 
 | 	} | 
 |  | 
 | 	D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X " | 
 | 		"clk 0x%02X msb: %i\n", | 
 | 		count, priv->data_mask, priv->clk_mask, priv->write_msb)); | 
 |  | 
 | 	while (count--) | 
 | 		gpio_write_byte(priv, *buf++); | 
 |  | 
 | out: | 
 | 	spin_unlock_irqrestore(&gpio_lock, flags); | 
 | 	return retval; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | static int | 
 | gpio_open(struct inode *inode, struct file *filp) | 
 | { | 
 | 	struct gpio_private *priv; | 
 | 	int p = iminor(inode); | 
 | 	unsigned long flags; | 
 |  | 
 | 	if (p > GPIO_MINOR_LAST) | 
 | 		return -EINVAL; | 
 |  | 
 | 	priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL); | 
 |  | 
 | 	if (!priv) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	lock_kernel(); | 
 | 	priv->minor = p; | 
 |  | 
 | 	/* initialize the io/alarm struct */ | 
 |  | 
 | 	if (USE_PORTS(priv)) { /* A and B */ | 
 | 		priv->port = ports[p]; | 
 | 		priv->shadow = shads[p]; | 
 | 		priv->dir = dir[p]; | 
 | 		priv->dir_shadow = dir_shadow[p]; | 
 | 		priv->changeable_dir = changeable_dir[p]; | 
 | 		priv->changeable_bits = changeable_bits[p]; | 
 | 	} else { | 
 | 		priv->port = NULL; | 
 | 		priv->shadow = NULL; | 
 | 		priv->dir = NULL; | 
 | 		priv->dir_shadow = NULL; | 
 | 		priv->changeable_dir = 0; | 
 | 		priv->changeable_bits = 0; | 
 | 	} | 
 |  | 
 | 	priv->highalarm = 0; | 
 | 	priv->lowalarm = 0; | 
 | 	priv->clk_mask = 0; | 
 | 	priv->data_mask = 0; | 
 | 	init_waitqueue_head(&priv->alarm_wq); | 
 |  | 
 | 	filp->private_data = priv; | 
 |  | 
 | 	/* link it into our alarmlist */ | 
 | 	spin_lock_irqsave(&gpio_lock, flags); | 
 | 	priv->next = alarmlist; | 
 | 	alarmlist = priv; | 
 | 	spin_unlock_irqrestore(&gpio_lock, flags); | 
 |  | 
 | 	unlock_kernel(); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int | 
 | gpio_release(struct inode *inode, struct file *filp) | 
 | { | 
 | 	struct gpio_private *p; | 
 | 	struct gpio_private *todel; | 
 | 	unsigned long flags; | 
 |  | 
 | 	spin_lock_irqsave(&gpio_lock, flags); | 
 |  | 
 | 	p = alarmlist; | 
 | 	todel = filp->private_data; | 
 |  | 
 | 	/* 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; | 
 | 	while (p) { | 
 | 		if (p->highalarm | p->lowalarm) { | 
 | 			gpio_some_alarms = 1; | 
 | 			goto out; | 
 | 		} | 
 | 		p = p->next; | 
 | 	} | 
 | 	gpio_some_alarms = 0; | 
 | out: | 
 | 	spin_unlock_irqrestore(&gpio_lock, flags); | 
 | 	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 */ | 
 | 	if (USE_PORTS(priv)) { | 
 | 		*priv->dir = *priv->dir_shadow &= | 
 | 		~((unsigned char)arg & priv->changeable_dir); | 
 | 		return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ | 
 | 	} | 
 |  | 
 | 	if (priv->minor != GPIO_MINOR_G) | 
 | 		return 0; | 
 |  | 
 | 	/* We must fiddle with R_GEN_CONFIG to change dir */ | 
 | 	if (((arg & dir_g_in_bits) != arg) && | 
 | 	    (arg & changeable_dir_g)) { | 
 | 		arg &= changeable_dir_g; | 
 | 		/* Clear bits in genconfig to set to input */ | 
 | 		if (arg & (1<<0)) { | 
 | 			genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir); | 
 | 			dir_g_in_bits |= (1<<0); | 
 | 			dir_g_out_bits &= ~(1<<0); | 
 | 		} | 
 | 		if ((arg & 0x0000FF00) == 0x0000FF00) { | 
 | 			genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir); | 
 | 			dir_g_in_bits |= 0x0000FF00; | 
 | 			dir_g_out_bits &= ~0x0000FF00; | 
 | 		} | 
 | 		if ((arg & 0x00FF0000) == 0x00FF0000) { | 
 | 			genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir); | 
 | 			dir_g_in_bits |= 0x00FF0000; | 
 | 			dir_g_out_bits &= ~0x00FF0000; | 
 | 		} | 
 | 		if (arg & (1<<24)) { | 
 | 			genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir); | 
 | 			dir_g_in_bits |= (1<<24); | 
 | 			dir_g_out_bits &= ~(1<<24); | 
 | 		} | 
 | 		D(printk(KERN_DEBUG "gpio: SETINPUT on port G set " | 
 | 			 "genconfig to 0x%08lX " | 
 | 			 "in_bits: 0x%08lX " | 
 | 			 "out_bits: 0x%08lX\n", | 
 | 			 (unsigned long)genconfig_shadow, | 
 | 			 dir_g_in_bits, dir_g_out_bits)); | 
 | 		*R_GEN_CONFIG = genconfig_shadow; | 
 | 		/* Must be a >120 ns delay before writing this again */ | 
 |  | 
 | 	} | 
 | 	return dir_g_in_bits; | 
 | } /* setget_input */ | 
 |  | 
 | unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) | 
 | { | 
 | 	if (USE_PORTS(priv)) { | 
 | 		*priv->dir = *priv->dir_shadow |= | 
 | 			((unsigned char)arg & priv->changeable_dir); | 
 | 		return *priv->dir_shadow; | 
 | 	} | 
 | 	if (priv->minor != GPIO_MINOR_G) | 
 | 		return 0; | 
 |  | 
 | 	/* We must fiddle with R_GEN_CONFIG to change dir */ | 
 | 	if (((arg & dir_g_out_bits) != arg) && | 
 | 	    (arg & changeable_dir_g)) { | 
 | 		/* Set bits in genconfig to set to output */ | 
 | 		if (arg & (1<<0)) { | 
 | 			genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir); | 
 | 			dir_g_out_bits |= (1<<0); | 
 | 			dir_g_in_bits &= ~(1<<0); | 
 | 		} | 
 | 		if ((arg & 0x0000FF00) == 0x0000FF00) { | 
 | 			genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir); | 
 | 			dir_g_out_bits |= 0x0000FF00; | 
 | 			dir_g_in_bits &= ~0x0000FF00; | 
 | 		} | 
 | 		if ((arg & 0x00FF0000) == 0x00FF0000) { | 
 | 			genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir); | 
 | 			dir_g_out_bits |= 0x00FF0000; | 
 | 			dir_g_in_bits &= ~0x00FF0000; | 
 | 		} | 
 | 		if (arg & (1<<24)) { | 
 | 			genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir); | 
 | 			dir_g_out_bits |= (1<<24); | 
 | 			dir_g_in_bits &= ~(1<<24); | 
 | 		} | 
 | 		D(printk(KERN_INFO "gpio: SETOUTPUT on port G set " | 
 | 			 "genconfig to 0x%08lX " | 
 | 			 "in_bits: 0x%08lX " | 
 | 			 "out_bits: 0x%08lX\n", | 
 | 			 (unsigned long)genconfig_shadow, | 
 | 			 dir_g_in_bits, dir_g_out_bits)); | 
 | 		*R_GEN_CONFIG = genconfig_shadow; | 
 | 		/* Must be a >120 ns delay before writing this again */ | 
 | 	} | 
 | 	return dir_g_out_bits & 0x7FFFFFFF; | 
 | } /* 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; | 
 |         int ret = 0; | 
 |  | 
 | 	struct gpio_private *priv = file->private_data; | 
 | 	if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) | 
 | 		return -EINVAL; | 
 |  | 
 | 	spin_lock_irqsave(&gpio_lock, flags); | 
 |  | 
 | 	switch (_IOC_NR(cmd)) { | 
 | 	case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ | 
 | 		// read the port | 
 | 		if (USE_PORTS(priv)) { | 
 | 			ret =  *priv->port; | 
 | 		} else if (priv->minor == GPIO_MINOR_G) { | 
 | 			ret =  (*R_PORT_G_DATA) & 0x7FFFFFFF; | 
 | 		} | 
 | 		break; | 
 | 	case IO_SETBITS: | 
 | 		// set changeable bits with a 1 in arg | 
 | 		if (USE_PORTS(priv)) { | 
 | 			*priv->port = *priv->shadow |=  | 
 | 			  ((unsigned char)arg & priv->changeable_bits); | 
 | 		} else if (priv->minor == GPIO_MINOR_G) { | 
 | 			*R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); | 
 | 		} | 
 | 		break; | 
 | 	case IO_CLRBITS: | 
 | 		// clear changeable bits with a 1 in arg | 
 | 		if (USE_PORTS(priv)) { | 
 | 			*priv->port = *priv->shadow &=  | 
 | 			 ~((unsigned char)arg & priv->changeable_bits); | 
 | 		} else if (priv->minor == GPIO_MINOR_G) { | 
 | 			*R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); | 
 | 		} | 
 | 		break; | 
 | 	case IO_HIGHALARM: | 
 | 		// set alarm when bits with 1 in arg go high | 
 | 		priv->highalarm |= arg; | 
 | 		gpio_some_alarms = 1; | 
 | 		break; | 
 | 	case IO_LOWALARM: | 
 | 		// set alarm when bits with 1 in arg go low | 
 | 		priv->lowalarm |= arg; | 
 | 		gpio_some_alarms = 1; | 
 | 		break; | 
 | 	case IO_CLRALARM: | 
 | 		// clear alarm for bits with 1 in arg | 
 | 		priv->highalarm &= ~arg; | 
 | 		priv->lowalarm  &= ~arg; | 
 | 		{ | 
 | 			/* Must update gpio_some_alarms */ | 
 | 			struct gpio_private *p = alarmlist; | 
 | 			int some_alarms; | 
 | 			spin_lock_irq(&gpio_lock); | 
 | 			p = alarmlist; | 
 | 			some_alarms = 0; | 
 | 			while (p) { | 
 | 				if (p->highalarm | p->lowalarm) { | 
 | 					some_alarms = 1; | 
 | 					break; | 
 | 				} | 
 | 				p = p->next; | 
 | 			} | 
 | 			gpio_some_alarms = some_alarms; | 
 | 			spin_unlock_irq(&gpio_lock); | 
 | 		} | 
 | 		break; | 
 | 	case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ | 
 | 		/* Read direction 0=input 1=output */ | 
 | 		if (USE_PORTS(priv)) { | 
 | 			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. | 
 | 			 */ | 
 | 			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  | 
 | 		 */ | 
 | 		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  | 
 | 		 */ | 
 | 		ret =  setget_output(priv, arg) & 0x7FFFFFFF; | 
 | 		break; | 
 | 	case IO_SHUTDOWN: | 
 | 		SOFT_SHUTDOWN(); | 
 | 		break; | 
 | 	case IO_GET_PWR_BT: | 
 | #if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) | 
 | 		ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT)); | 
 | #else | 
 | 		ret = 0; | 
 | #endif | 
 | 		break; | 
 | 	case IO_CFG_WRITE_MODE: | 
 | 		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 & priv->changeable_bits) && | 
 | 		      (priv->data_mask & priv->changeable_bits) && | 
 | 		      (priv->clk_mask & *priv->dir_shadow) && | 
 | 		      (priv->data_mask & *priv->dir_shadow))) | 
 | 		{ | 
 | 			priv->clk_mask = 0; | 
 | 			priv->data_mask = 0; | 
 | 			ret = -EPERM; | 
 | 		} | 
 | 		break; | 
 | 	case IO_READ_INBITS:  | 
 | 		/* *arg is result of reading the input pins */ | 
 | 		if (USE_PORTS(priv)) { | 
 | 			val = *priv->port; | 
 | 		} else if (priv->minor == GPIO_MINOR_G) { | 
 | 			val = *R_PORT_G_DATA; | 
 | 		} | 
 | 		if (copy_to_user((void __user *)arg, &val, sizeof(val))) | 
 | 			ret = -EFAULT; | 
 | 		break; | 
 | 	case IO_READ_OUTBITS: | 
 | 		 /* *arg is result of reading the output shadow */ | 
 | 		if (USE_PORTS(priv)) { | 
 | 			val = *priv->shadow; | 
 | 		} else if (priv->minor == GPIO_MINOR_G) { | 
 | 			val = port_g_data_shadow; | 
 | 		} | 
 | 		if (copy_to_user((void __user *)arg, &val, sizeof(val))) | 
 | 			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, (void __user *)arg, sizeof(val))) | 
 | 		{ | 
 | 			ret = -EFAULT; | 
 | 			break; | 
 | 		} | 
 | 		val = setget_input(priv, val); | 
 | 		if (copy_to_user((void __user *)arg, &val, sizeof(val))) | 
 | 			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, (void __user *)arg, sizeof(val))) { | 
 | 			ret = -EFAULT; | 
 | 			break; | 
 | 		} | 
 | 		val = setget_output(priv, val); | 
 | 		if (copy_to_user((void __user *)arg, &val, sizeof(val))) | 
 | 			ret = -EFAULT; | 
 | 		break; | 
 | 	default: | 
 | 		if (priv->minor == GPIO_MINOR_LEDS) | 
 | 			ret = gpio_leds_ioctl(cmd, arg); | 
 | 		else | 
 | 			ret = -EINVAL; | 
 | 	} /* switch */ | 
 |  | 
 | 	spin_unlock_irqrestore(&gpio_lock, flags); | 
 | 	return ret; | 
 | } | 
 |  | 
 | 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; | 
 | 		CRIS_LED_ACTIVE_SET_G(green); | 
 | 		CRIS_LED_ACTIVE_SET_R(red); | 
 | 		break; | 
 |  | 
 | 	case IO_LED_SETBIT: | 
 | 		CRIS_LED_BIT_SET(arg); | 
 | 		break; | 
 |  | 
 | 	case IO_LED_CLRBIT: | 
 | 		CRIS_LED_BIT_CLR(arg); | 
 | 		break; | 
 |  | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} /* switch */ | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct file_operations gpio_fops = { | 
 | 	.owner       = THIS_MODULE, | 
 | 	.poll        = gpio_poll, | 
 | 	.ioctl       = gpio_ioctl, | 
 | 	.write       = gpio_write, | 
 | 	.open        = gpio_open, | 
 | 	.release     = gpio_release, | 
 | }; | 
 |  | 
 | static 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) | 
 | { | 
 | 	unsigned long int flags; | 
 |  | 
 | 	D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n")); | 
 | 	D(printk(KERN_DEBUG "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)); | 
 |  | 
 | 	spin_lock_irqsave(&gpio_lock, flags); | 
 |  | 
 | 	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 */ | 
 | 	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) | 
 | 		dir_g_shadow |= (1 << 0); | 
 | 	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out)) | 
 | 		dir_g_shadow |= 0x0000FF00; | 
 | 	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out)) | 
 | 		dir_g_shadow |= 0x00FF0000; | 
 | 	if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out)) | 
 | 		dir_g_shadow |= (1 << 24); | 
 |  | 
 | 	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 */ | 
 | 	dir_g_out_bits &= ~changeable_dir_g; | 
 | 	dir_g_out_bits |= dir_g_shadow; | 
 | 	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); | 
 | 	printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n", | 
 | 	       dir_g_shadow, changeable_dir_g); | 
 | } | 
 |  | 
 | /* main driver initialization routine, called from mem.c */ | 
 |  | 
 | static int __init gpio_init(void) | 
 | { | 
 | 	int res; | 
 | #if defined (CONFIG_ETRAX_CSP0_LEDS) | 
 | 	int i; | 
 | #endif | 
 |  | 
 | 	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 */ | 
 | #if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) | 
 | 	CRIS_LED_NETWORK_SET(0); | 
 | 	CRIS_LED_ACTIVE_SET(0); | 
 | 	CRIS_LED_DISK_READ(0); | 
 | 	CRIS_LED_DISK_WRITE(0); | 
 |  | 
 | #if defined (CONFIG_ETRAX_CSP0_LEDS) | 
 | 	for (i = 0; i < 32; i++) | 
 | 		CRIS_LED_BIT_SET(i); | 
 | #endif | 
 |  | 
 | #endif | 
 | 	/* 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-2008 " | 
 | 		"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. | 
 | 	 */ | 
 | 	res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, | 
 | 		IRQF_SHARED | IRQF_DISABLED, "gpio poll", gpio_name); | 
 | 	if (res) { | 
 | 		printk(KERN_CRIT "err: timer0 irq for gpio\n"); | 
 | 		return res; | 
 | 	} | 
 | 	res = request_irq(PA_IRQ_NBR, gpio_interrupt, | 
 | 		IRQF_SHARED | IRQF_DISABLED, "gpio PA", gpio_name); | 
 | 	if (res) | 
 | 		printk(KERN_CRIT "err: PA irq for gpio\n"); | 
 |  | 
 | 	return res; | 
 | } | 
 |  | 
 | /* this makes sure that gpio_init is called during kernel boot */ | 
 | module_init(gpio_init); | 
 |  |