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
- Lines: 204
- Date:
Fri Nov 9 13:58:02 2001
- Orig file:
v2.4.14/linux/arch/cris/drivers/gpio.c
- Orig date:
Wed Jul 25 17:10:17 2001
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)