patch-2.4.15 linux/arch/cris/drivers/gpio.c

Next file: linux/arch/cris/drivers/serial.c
Previous file: linux/arch/cris/drivers/axisflashmap.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.14/linux/arch/cris/drivers/gpio.c linux/arch/cris/drivers/gpio.c
@@ -1,4 +1,4 @@
-/* $Id: gpio.c,v 1.9 2001/05/04 14:16:07 matsfg Exp $
+/* $Id: gpio.c,v 1.11 2001/10/30 14:39:12 johana Exp $
  *
  * Etrax general port I/O device
  *
@@ -6,9 +6,18 @@
  *
  * Authors:    Bjorn Wesen      (initial version)
  *             Ola Knutsson     (LED handling)
- *             Johan Adolfsson  (read/set directions)
+ *             Johan Adolfsson  (read/set directions, write)
  *
  * $Log: gpio.c,v $
+ * Revision 1.11  2001/10/30 14:39:12  johana
+ * Added D() around gpio_write printk.
+ *
+ * Revision 1.10  2001/10/25 10:24:42  johana
+ * Added IO_CFG_WRITE_MODE ioctl and write method that can do fast
+ * bittoggling in the kernel. (This speeds up programming an FPGA with 450kB
+ * from ~60 seconds to 4 seconds).
+ * Added save_flags/cli/restore_flags in ioctl.
+ *
  * Revision 1.9  2001/05/04 14:16:07  matsfg
  * Corrected spelling error
  *
@@ -69,6 +78,8 @@
 
 static int gpio_ioctl(struct inode *inode, struct file *file,
 		      unsigned int cmd, unsigned long arg);
+static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
+                          loff_t *off);
 static int gpio_open(struct inode *inode, struct file *filp);
 static int gpio_release(struct inode *inode, struct file *filp);
 static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait);
@@ -82,6 +93,9 @@
 	unsigned char changeable_dir;
 	unsigned char changeable_bits;
 	unsigned char highalarm, lowalarm;
+	unsigned char clk_mask;
+	unsigned char data_mask;
+	unsigned char write_msb;
 	wait_queue_head_t alarm_wq;
 	int minor;
 };
@@ -139,6 +153,59 @@
 	return 1;
 }
 
+static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
+                          loff_t *off)
+{
+	struct gpio_private *priv = (struct gpio_private *)file->private_data;
+	unsigned char data, clk_mask, data_mask, write_msb;
+	unsigned long flags;
+	ssize_t retval = count;
+	if (verify_area(VERIFY_READ, buf, count))
+	{
+		return -EFAULT;
+	}
+	clk_mask = priv->clk_mask;
+	data_mask = priv->data_mask;
+	/* It must have been configured using the IO_CFG_WRITE_MODE */
+	/* Perhaps a better error code? */
+	if (clk_mask == 0 || data_mask == 0) 
+	{
+		return -EPERM;
+	}
+	write_msb = priv->write_msb;
+	D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb));
+	while (count--) {
+		int i;
+		data = *buf++;
+		if (priv->write_msb) {
+			for (i = 7; i>=0;i--) {
+				save_flags(flags); cli();
+				*priv->port = *priv->shadow &= ~clk_mask;
+				if (data & 1<<i)
+					*priv->port = *priv->shadow |= data_mask;
+				else
+					*priv->port = *priv->shadow &= ~data_mask;
+			/* For FPGA: min 5.0ns (DCC) before CCLK high */
+				*priv->port = *priv->shadow |= clk_mask;
+				restore_flags(flags);
+			}
+		} else {
+			for (i = 0; i<=7;i++) {
+				save_flags(flags); cli();
+				*priv->port = *priv->shadow &= ~clk_mask;
+				if (data & 1<<i)
+					*priv->port = *priv->shadow |= data_mask;
+				else
+					*priv->port = *priv->shadow &= ~data_mask;
+			/* For FPGA: min 5.0ns (DCC) before CCLK high */
+				*priv->port = *priv->shadow |= clk_mask;
+				restore_flags(flags);
+			}
+		}
+	}
+	return retval;
+}
+
 static int
 gpio_open(struct inode *inode, struct file *filp)
 {
@@ -170,6 +237,8 @@
 
 	priv->highalarm = 0;
 	priv->lowalarm = 0;
+	priv->clk_mask = 0;
+	priv->data_mask = 0;
 	init_waitqueue_head(&priv->alarm_wq);
 
 	filp->private_data = (void *)priv;
@@ -209,8 +278,8 @@
 gpio_ioctl(struct inode *inode, struct file *file,
 	   unsigned int cmd, unsigned long arg)
 {
+	unsigned long flags;
 	struct gpio_private *priv = (struct gpio_private *)file->private_data;
-
 	if(_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
 		return -EINVAL;
 	}
@@ -220,14 +289,18 @@
 			// read the port
 			return *priv->port;
 		case IO_SETBITS:
+			save_flags(flags); cli();
 			// set changeable bits with a 1 in arg
 			*priv->port = *priv->shadow |= 
 			  ((unsigned char)arg & priv->changeable_bits);
+			restore_flags(flags);
 			break;
 		case IO_CLRBITS:
+			save_flags(flags); cli();
 			// clear changeable bits with a 1 in arg
 			*priv->port = *priv->shadow &= 
 			  ~((unsigned char)arg & priv->changeable_bits);
+			restore_flags(flags);
 			break;
 		case IO_HIGHALARM:
 			// set alarm when bits with 1 in arg go high
@@ -246,14 +319,18 @@
 			/* Read direction 0=input 1=output */
 			return *priv->dir_shadow;
 		case IO_SETINPUT:
+			save_flags(flags); cli();
 			/* Set direction 0=unchanged 1=input */
 			*priv->dir = *priv->dir_shadow &= 
 			~((unsigned char)arg & priv->changeable_dir);
+			restore_flags(flags);
 			return *priv->dir_shadow;
 		case IO_SETOUTPUT:
+			save_flags(flags); cli();
 			/* Set direction 0=unchanged 1=output */
 			*priv->dir = *priv->dir_shadow |= 
 			  ((unsigned char)arg & priv->changeable_dir);
+			restore_flags(flags);
 			return *priv->dir_shadow;
                 case IO_SHUTDOWN:
                        SOFT_SHUTDOWN();
@@ -265,6 +342,24 @@
 #else
                        return 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;
+				return -EPERM;
+			}
+			break;
 		default:
 			if(priv->minor == LEDS)
 				return gpio_leds_ioctl(cmd, arg);
@@ -301,6 +396,7 @@
 	owner:       THIS_MODULE,
 	poll:        gpio_poll,
 	ioctl:       gpio_ioctl,
+	write:       gpio_write,
 	open:        gpio_open,
 	release:     gpio_release,
 };
@@ -338,7 +434,7 @@
 
 #endif
 	
-	printk("ETRAX 100LX GPIO driver v2.1, (c) 2001 Axis Communications AB\n");
+	printk("ETRAX 100LX GPIO driver v2.2, (c) 2001 Axis Communications AB\n");
 
 	return res;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)