patch-2.4.4 linux/arch/cris/drivers/gpio.c
Next file: linux/arch/cris/drivers/i2c.c
Previous file: linux/arch/cris/drivers/examples/kiobuftest.c
Back to the patch index
Back to the overall index
- Lines: 299
- Date:
Fri Apr 6 10:42:55 2001
- Orig file:
v2.4.3/linux/arch/cris/drivers/gpio.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.3/linux/arch/cris/drivers/gpio.c linux/arch/cris/drivers/gpio.c
@@ -0,0 +1,298 @@
+/* $Id: gpio.c,v 1.4 2001/02/27 13:52:48 bjornw Exp $
+ *
+ * Etrax general port I/O device
+ *
+ * Copyright (c) 1999, 2000, 2001 Axis Communications AB
+ *
+ * Authors: Bjorn Wesen (initial version)
+ * Ola Knutsson (LED handling)
+ * Johan Adolfsson (read/set directions)
+ *
+ * $Log: gpio.c,v $
+ * Revision 1.4 2001/02/27 13:52:48 bjornw
+ * malloc.h -> slab.h
+ *
+ * Revision 1.3 2001/01/24 15:06:48 bjornw
+ * gpio_wq correct type
+ *
+ * Revision 1.2 2001/01/18 16:07:30 bjornw
+ * 2.4 port
+ *
+ * Revision 1.1 2001/01/18 15:55:16 bjornw
+ * Verbatim copy of etraxgpio.c from elinux 2.0 added
+ *
+ *
+ */
+
+#include <linux/config.h>
+#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/string.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+
+#include <asm/etraxgpio.h>
+#include <asm/svinto.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#define GPIO_MAJOR 120 /* experimental MAJOR number */
+
+#define D(x)
+
+static char gpio_name[] = "etrax gpio";
+
+static wait_queue_head_t *gpio_wq;
+
+static int gpio_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+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;
+ volatile unsigned char *port, *shadow;
+ volatile unsigned char *dir, *dir_shadow;
+ unsigned char changeable_dir;
+ unsigned char changeable_bits;
+ unsigned char highalarm, lowalarm;
+ wait_queue_head_t alarm_wq;
+ int minor;
+};
+
+/* linked list of alarms to check for */
+
+static struct gpio_private *alarmlist = 0;
+
+#define NUM_PORTS 2
+static volatile unsigned char *ports[2] = { R_PORT_PA_DATA, R_PORT_PB_DATA };
+static volatile unsigned char *shads[2] = {
+ &port_pa_data_shadow, &port_pb_data_shadow };
+
+/* What direction bits that are user changeable 1=changeable*/
+#ifndef CONFIG_PA_CHANGEABLE_DIR
+#define CONFIG_PA_CHANGEABLE_DIR 0x00
+#endif
+#ifndef CONFIG_PB_CHANGEABLE_DIR
+#define CONFIG_PB_CHANGEABLE_DIR 0x00
+#endif
+
+#ifndef CONFIG_PA_CHANGEABLE_BITS
+#define CONFIG_PA_CHANGEABLE_BITS 0xFF
+#endif
+#ifndef CONFIG_PB_CHANGEABLE_BITS
+#define CONFIG_PB_CHANGEABLE_BITS 0xFF
+#endif
+
+
+static unsigned char changeable_dir[2] = { CONFIG_PA_CHANGEABLE_DIR,
+ CONFIG_PB_CHANGEABLE_DIR };
+static unsigned char changeable_bits[2] = { CONFIG_PA_CHANGEABLE_BITS,
+ CONFIG_PB_CHANGEABLE_BITS };
+
+static volatile unsigned char *dir[2] = { R_PORT_PA_DIR, R_PORT_PB_DIR };
+
+static volatile unsigned char *dir_shadow[2] = {
+ &port_pa_dir_shadow, &port_pb_dir_shadow };
+
+#define LEDS 2
+
+static unsigned int
+gpio_poll(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ /* TODO poll on alarms! */
+#if 0
+ if(!ANYTHING_WANTED) {
+ D(printk("gpio_select sleeping task\n"));
+ select_wait(&gpio_wq, table);
+ return 0;
+ }
+ D(printk("gpio_select ready\n"));
+#endif
+ return 1;
+}
+
+static int
+gpio_open(struct inode *inode, struct file *filp)
+{
+ struct gpio_private *priv;
+ int p = MINOR(inode->i_rdev);
+
+ if(p >= NUM_PORTS && p != LEDS)
+ return -EINVAL;
+
+ priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private),
+ GFP_KERNEL);
+
+ if(!priv)
+ return -ENOMEM;
+
+ priv->minor = p;
+
+ /* initialize the io/alarm struct and link it into our alarmlist */
+
+ priv->next = alarmlist;
+ alarmlist = priv;
+ priv->port = ports[p];
+ priv->shadow = shads[p];
+
+ priv->changeable_dir = changeable_dir[p];
+ priv->changeable_bits = changeable_bits[p];
+ priv->dir = dir[p];
+ priv->dir_shadow = dir_shadow[p];
+
+ priv->highalarm = 0;
+ priv->lowalarm = 0;
+ init_waitqueue_head(&priv->alarm_wq);
+
+ filp->private_data = (void *)priv;
+
+ return 0;
+}
+
+static int
+gpio_release(struct inode *inode, struct file *filp)
+{
+ struct gpio_private *p = alarmlist;
+ struct gpio_private *todel = (struct gpio_private *)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);
+
+ 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().
+ */
+
+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)
+{
+ struct gpio_private *priv = (struct gpio_private *)file->private_data;
+
+ if(_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
+ return -EINVAL;
+ }
+
+ switch (_IOC_NR(cmd)) {
+ case IO_READBITS:
+ // read the port
+ return *priv->port;
+ case IO_SETBITS:
+ // set changeable bits with a 1 in arg
+ *priv->port = *priv->shadow |=
+ ((unsigned char)arg & priv->changeable_bits);
+ break;
+ case IO_CLRBITS:
+ // clear changeable bits with a 1 in arg
+ *priv->port = *priv->shadow &=
+ ~((unsigned char)arg & priv->changeable_bits);
+ break;
+ case IO_HIGHALARM:
+ // set alarm when bits with 1 in arg go high
+ priv->highalarm |= (unsigned char)arg;
+ break;
+ case IO_LOWALARM:
+ // set alarm when bits with 1 in arg go low
+ priv->lowalarm |= (unsigned char)arg;
+ break;
+ case IO_CLRALARM:
+ // clear alarm for bits with 1 in arg
+ priv->highalarm &= ~(unsigned char)arg;
+ priv->lowalarm &= ~(unsigned char)arg;
+ break;
+ case IO_READDIR:
+ /* Read direction 0=input 1=output */
+ return *priv->dir_shadow;
+ case IO_SETINPUT:
+ /* Set direction 0=unchanged 1=input */
+ *priv->dir = *priv->dir_shadow &=
+ ~((unsigned char)arg & priv->changeable_dir);
+ return *priv->dir_shadow;
+ case IO_SETOUTPUT:
+ /* Set direction 0=unchanged 1=output */
+ *priv->dir = *priv->dir_shadow |=
+ ((unsigned char)arg & priv->changeable_dir);
+ return *priv->dir_shadow;
+ default:
+ if(priv->minor == LEDS)
+ return gpio_leds_ioctl(cmd, arg);
+ else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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;
+ LED_ACTIVE_SET_G(green);
+ LED_ACTIVE_SET_R(red);
+ break;
+ default:
+ return -EINVAL;
+ }
+}
+
+struct file_operations gpio_fops = {
+ owner: THIS_MODULE,
+ poll: gpio_poll,
+ ioctl: gpio_ioctl,
+ open: gpio_open,
+ release: gpio_release,
+};
+
+/* main driver initialization routine, called from mem.c */
+
+static __init int
+gpio_init(void)
+{
+ int res;
+
+ /* do the formalities */
+
+ 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;
+ }
+
+ printk("ETRAX 100LX GPIO driver v2.1, (c) 2001 Axis Communications AB\n");
+
+ return res;
+}
+
+/* this makes sure that gpio_init is called during kernel boot */
+
+module_init(gpio_init);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)