Re: [RFC] Proposal: common kernel-wide GPIO interface

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Chris,

On Fri, Jul 28, 2006 at 09:44:40PM +0100, Chris Boot wrote:
> I propose to develop a common way of registering and accessing GPIO pins on 
> various devices.

I've attached the gpio framework we have developed a while ago; it is
not ready for upstream, only tested on pxa and has probably several
other drawbacks, but may be a start for your activities. One of the
problems we've recently seen is that for example on PowerPCs you don't
have such a clear "this is gpio pin x" nomenclature, so the question
would be how to do the mapping here.

Robert 
-- 
 Dipl.-Ing. Robert Schwebel | http://www.pengutronix.de
 Pengutronix - Linux Solutions for Science and Industry
   Handelsregister:  Amtsgericht Hildesheim, HRA 2686
     Hannoversche Str. 2, 31134 Hildesheim, Germany
   Phone: +49-5121-206917-0 |  Fax: +49-5121-206917-9

Subject: gpio: GPIO framework
From: Robert Schwebel <[email protected]>

This patch adds a generic GPIO framework. It is not ready for upstream
yet, only tested on ARM/PXA.

Signed-off-by: Robert Schwebel <[email protected]>

Index: arch/arm/mach-pxa/Makefile
===================================================================
--- arch/arm/mach-pxa/Makefile.orig
+++ arch/arm/mach-pxa/Makefile
@@ -35,3 +35,5 @@ obj-$(CONFIG_PXA_SSP) += ssp.o
 ifeq ($(CONFIG_PXA27x),y)
 obj-$(CONFIG_PM) += standby.o
 endif
+
+obj-$(CONFIG_GPIO)	+=	pxa27x_gpio.o
Index: arch/arm/mach-pxa/Kconfig
===================================================================
--- arch/arm/mach-pxa/Kconfig.orig
+++ arch/arm/mach-pxa/Kconfig
@@ -74,6 +74,64 @@ endchoice
 
 endif
 
+config GPIO
+	bool "GPIO pin support for PXA27x"
+	default y if ARM || PPC
+	default n
+	help
+	  Enabling this option adds support for PXA GPIO pins. Most
+	  System-on-Chip processors have this kind of pins mostly shared
+	  with some kind of internal hardware. Remaining pins may be used
+	  for any purposes (input/output/interrupt etc). To do so, enable
+	  this feature and provide some kernel parameters to define what
+	  pins are available. To define one or more GPIO pin give a:
+	  gpio.mapping=<pin#>:(in|(out:(hi|lo)))[.<pin#>:(in|(out:(hi|lo)))]
+	  Where <pin#> is any GPIO pin number your SoC supports, out or in
+	  is the data direction and hi or lo is one of the possible
+	  levels an output pin can have.
+	  For example. You have three GPIO: pin 8, 63 and 113. Pin 8 is a
+	  simple key input, pin 63 an output to control a motor. It needs
+	  a high level first to stop the motor while booting. Pin 113 is
+	  also an output pin, but needs low level while booting. To do so,
+	  give this kernel parameter:
+	   gpio.mapping=8:in.63:out:hi.113:out:lo
+	  With "cat /proc/gpio" you will get:
+	    GPIO   POLICY       DIRECTION    OWNER
+	      8:  user space      input      kernel
+	     63:  user space      output     kernel
+	    113:  user space      output     kernel
+	  To set pin 63 to low (to start the motor) do a:
+	   $ echo 0 > /sys/class/gpio/gpio63/level
+	  Or to stop the motor again:
+	   $ echo 1 > /sys/class/gpio/gpio63/level
+	  To get the level of the key (pin 8) do:
+	   $ cat /sys/class/gpio/gpio8/level
+	  The result will be 1 or 0.
+
+	  To add new GPIO pins at runtime (lets say pin 88 should be an input)
+	  you can do a:
+	   $ echo 88:in > /sys/class/gpio/map_gpio
+	  The same with a new GPIO pin 95, it should be an output and at high level:
+	   $ echo 95:out:hi > /sys/class/gpio/map_gpio
+
+	  After that "cat /proc/gpio" will show you:
+	    GPIO   POLICY       DIRECTION    OWNER
+	      8:  user space      input      kernel
+	     63:  user space      output     kernel
+	    113:  user space      output     kernel
+	     88:  user space      input      kernel
+	     95:  user space      output     kernel
+
+	  Note: You can add more than one new GPIO pin in one step. The period is the
+	  delimiter between each pin definition. To add the pins 88 and 95 in the
+	  example above in one step do a:
+	   $ echo 88:in.95:out:hi > /sys/class/gpio/map_gpio
+
+	  To remove any of these GPIOs use (in this example GPIO pin 95):
+	   $ echo 95 > /sys/class/gpio/unmap_gpio
+
+	  If unsure, say N.
+
 endmenu
 
 config MACH_POODLE
Index: arch/arm/mach-pxa/pxa27x_gpio.c
===================================================================
--- /dev/null
+++ arch/arm/mach-pxa/pxa27x_gpio.c
@@ -0,0 +1,605 @@
+/*
+ * linux/kernel/gpio.c
+ *
+ * (C) 2004 Robert Schwebel, Pengutronix
+ *
+ * modified by Benedikt Spranger, Pengutronix
+ * modified by Marc Kleine-Budde <[email protected]>, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/proc_fs.h>
+#include <linux/gpio.h>
+#include <linux/parser.h>
+
+#define DRIVER_NAME "gpio"
+
+static char __initdata mapping[255] = "";
+
+MODULE_AUTHOR("John Lenz, Robert Schwebel");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Generic GPIO Infrastructure");
+module_param_string(mapping, mapping, sizeof(mapping), 0);
+MODULE_PARM_DESC(mapping,
+"period delimited options string to map GPIO pins to userland:\n"
+"\n"
+"	<pin>:[out|in][hi|lo]\n"
+"\n"
+"	example: gpio.mapping=5:out:hi.8:in\n"
+);
+
+static ssize_t gpio_show_level(struct class_device *dev, char *buf);
+static ssize_t gpio_store_level(struct class_device *dev, const char *buf, size_t size);
+static ssize_t gpio_show_policy(struct class_device *dev, char *buf);
+
+struct gpio_properties {
+	unsigned int       pin_nr;
+	unsigned char      policy;	/* GPIO_xxx */
+	char               pin_level;	/* -1=tristate, 0, 1 */
+	char               owner[20];
+	struct gpio_device *gpio_dev;
+};
+
+struct gpio_device {
+	spinlock_t lock; 		/* protects the props field */
+	struct gpio_properties props;
+	struct class_device class_dev;
+	struct list_head list;
+};
+#define to_gpio_device(d) container_of(d, struct gpio_device, class_dev)
+
+static LIST_HEAD(gpio_list);
+static rwlock_t gpio_list_lock = RW_LOCK_UNLOCKED;
+
+/* gpio_device is static, so we don't have to free it here */
+static void gpio_class_release(struct class_device *dev)
+{
+	return;
+}
+
+static struct class gpio_class = {
+	.name		= "gpio",
+	.release	= gpio_class_release,
+};
+
+
+/*
+ * Attribute: /sys/class/gpio/gpioX/level
+ */
+static struct class_device_attribute attr_gpio_level = {
+	.attr = { .name = "level", .mode = 0644, .owner = THIS_MODULE },
+	.show = gpio_show_level,
+	.store = gpio_store_level,
+};
+
+/**
+ * gpio_show_level - shows the current level of an input GPIO pin
+ *                   or the current setting of an output pin
+ * @dev:
+ * @buf:
+ *
+ * Called when a read from /sys/class/gpio/gpioX/level occures
+ * FIXME: size of @buf?
+ */
+static ssize_t gpio_show_level(struct class_device *dev, char *buf)
+{
+	struct gpio_device *gpio_dev = to_gpio_device(dev);
+	ssize_t ret_size = 0;
+
+	spin_lock(&gpio_dev->lock);
+	if (!(gpio_dev->props.policy & GPIO_OUTPUT))
+		gpio_dev->props.pin_level = gpio_get_pin(gpio_dev->props.pin_nr);
+
+	ret_size += sprintf(buf, "%i\n", gpio_dev->props.pin_level);
+	spin_unlock(&gpio_dev->lock);
+
+	return ret_size;
+}
+
+/**
+ * gpio_store_level - sets a new level for an output GPIO pin
+ * @dev:
+ * @buf:
+ * @size:
+ *
+ * Called when a write to /sys/class/gpio/gpioX/level occures
+ */
+static ssize_t gpio_store_level(struct class_device *dev, const char *buf, size_t size)
+{
+	struct gpio_device *gpio_dev = to_gpio_device(dev);
+	long value;
+
+	spin_lock(&gpio_dev->lock);
+	if (!(gpio_dev->props.policy & GPIO_OUTPUT)) {
+		spin_unlock(&gpio_dev->lock);
+		return -EINVAL;
+	}
+
+	value = simple_strtol(buf, NULL, 10);
+
+	if (value)
+		value = 1;
+	gpio_dev->props.pin_level = value;
+
+	/* set real hardware */
+	switch (value) {
+		case 0:  gpio_clear_pin(gpio_dev->props.pin_nr);
+			 gpio_dir_output(gpio_dev->props.pin_nr);
+			 break;
+		case 1:  gpio_set_pin(gpio_dev->props.pin_nr);
+			 gpio_dir_output(gpio_dev->props.pin_nr);
+			 break;
+		default: break;
+	}
+	spin_unlock(&gpio_dev->lock);
+	return size;
+}
+
+/*
+ * Attribute: /sys/class/gpio/gpioX/policy
+ */
+static struct class_device_attribute attr_gpio_policy = {
+	.attr = { .name = "policy", .mode = 0444, .owner = THIS_MODULE },
+	.show = gpio_show_policy,
+	.store = NULL,
+};
+
+/**
+ * gpio_show_policy - shows the current policy
+ * @dev:
+ * @buf: to write the answer in
+ *
+ * Called when a read from /sys/class/gpio/gpioX/policy occures
+ * FIXME: size of @buf?
+ */
+static ssize_t gpio_show_policy(struct class_device *dev, char *buf)
+{
+	struct gpio_device *gpio_dev = to_gpio_device(dev);
+	ssize_t ret_size = 0;
+
+	spin_lock(&gpio_dev->lock);
+	if (gpio_dev->props.policy & GPIO_USER)
+		ret_size += sprintf(buf,"userspace\n");
+	else
+		ret_size += sprintf(buf,"kernel\n");
+	spin_unlock(&gpio_dev->lock);
+
+	return ret_size;
+}
+
+/**
+ * gpio_read_proc - gets called when you read from /proc/gpio ;-)
+ * @page:
+ * @start:
+ * @off:
+ * @count:
+ * @eof:
+ * @data:
+ */
+static int gpio_read_proc(char *page, char **start, off_t off,
+			  int count, int *eof, void *data)
+{
+	char *p = page;
+	int size = 0;
+	struct gpio_device *gpio_dev;
+	struct list_head *lact, *ltmp;
+
+	if (off != 0)
+		goto end;
+
+	p += sprintf(p, "GPIO   POLICY       DIRECTION    OWNER\n");
+	read_lock(&gpio_list_lock);
+	list_for_each_safe(lact, ltmp, &gpio_list) {
+		gpio_dev = list_entry(lact, struct gpio_device, list);
+		spin_lock(&gpio_dev->lock);
+		p += sprintf(p, "%3i:   ", gpio_dev->props.pin_nr);
+
+		if (gpio_dev->props.policy & GPIO_USER)
+			p += sprintf(p, "user space   ");
+		else
+			p += sprintf(p, "kernel       ");
+
+		if (gpio_dev->props.policy & GPIO_OUTPUT)
+			p += sprintf(p, "output       ");
+		else
+			p += sprintf(p, "input        ");
+
+		p += sprintf(p, "%s\n", gpio_dev->props.owner);
+		spin_unlock(&gpio_dev->lock);
+	}
+	read_unlock(&gpio_list_lock);
+end:
+	size = (p - page);
+	if (size <= off + count)
+		*eof = 1;
+	*start = page + off;
+	size -= off;
+	if (size > count)
+		size = count;
+	if (size < 0)
+		size = 0;
+
+	return size;
+}
+
+/**
+ * request_gpio - register a new object of gpio_device class.
+ *
+ * @pin_nr:     GPIO pin which is registered
+ * @owner:      name of the driver that owns this pin
+ * @policy:     set policy for this pin, which is one of these:
+ * 		- GPIO_USER or GPIO_KERNEL
+ * 		- GPIO_INPUT or GPIO_OUTPUT
+ * 		For user space registered pins a sysfs entry is added.
+ * @init_level: initially configured pin level
+ */
+int request_gpio(unsigned int pin_nr, const char *owner,
+		 unsigned char policy, unsigned char init_level)
+{
+	int rc;
+	struct gpio_device *gpio_dev;
+	struct list_head *lact, *ltmp;
+
+	write_lock(&gpio_list_lock);
+	list_for_each_safe(lact, ltmp, &gpio_list) {
+		gpio_dev = list_entry(lact, struct gpio_device, list);
+		if (pin_nr == gpio_dev->props.pin_nr) {
+			printk(KERN_ERR "gpio pin %i is already used by %s\n",
+				pin_nr, gpio_dev->props.owner);
+			write_unlock(&gpio_list_lock);
+			return -EBUSY;
+		}
+	}
+
+	gpio_dev = kmalloc(sizeof(struct gpio_device), GFP_KERNEL);
+
+	if (unlikely(!gpio_dev)) {
+		printk(KERN_ERR "%s: couldn't allocate memory\n", DRIVER_NAME);
+		write_unlock(&gpio_list_lock);
+		return -ENOMEM;
+	}
+
+	gpio_dev->props.pin_nr = pin_nr;
+	INIT_LIST_HEAD(&gpio_dev->list);
+	list_add_tail(&gpio_dev->list, &gpio_list);
+	write_unlock(&gpio_list_lock);
+
+	spin_lock_init(&gpio_dev->lock);
+	gpio_dev->props.policy = policy;
+	gpio_dev->props.pin_level = init_level;
+	gpio_dev->props.gpio_dev = gpio_dev;
+
+	strncpy(gpio_dev->props.owner, owner, 20);
+
+	memset(&gpio_dev->class_dev, 0, sizeof(gpio_dev->class_dev));
+	gpio_dev->class_dev.class = &gpio_class;
+	snprintf(gpio_dev->class_dev.class_id, BUS_ID_SIZE, "gpio%i", pin_nr);
+
+	rc = class_device_register(&gpio_dev->class_dev);
+	if (unlikely(rc)) {
+		printk(KERN_ERR "%s: class registering failed\n", DRIVER_NAME);
+		kfree(gpio_dev);
+		return rc;
+	}
+
+	/* register the attributes */
+	if (policy & GPIO_USER)
+		class_device_create_file(&gpio_dev->class_dev,&attr_gpio_level);
+
+	class_device_create_file(&gpio_dev->class_dev, &attr_gpio_policy);
+
+	/* set real hardware */
+	spin_lock(&gpio_dev->lock);
+	pxa_gpio_mode(pin_nr);
+	if (policy & GPIO_OUTPUT) {
+		switch (init_level) {
+			case 0: gpio_clear_pin(pin_nr); break;
+			case 1: gpio_set_pin(pin_nr); break;
+			default: break;
+		}
+		gpio_dir_output(pin_nr);
+	}
+	spin_unlock(&gpio_dev->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(request_gpio);
+
+
+/**
+ * free_gpio - unregisters an object of gpio_properties class.
+ *
+ * @pin_nr: pin number to free.
+ *
+ * Unregisters a previously registered via request_gpio object.
+ */
+void free_gpio(unsigned int pin_nr)
+{
+	struct gpio_device *gpio_dev;
+	struct list_head *lact, *ltmp;
+
+	write_lock(&gpio_list_lock);
+	list_for_each_safe(lact, ltmp, &gpio_list) {
+		gpio_dev = list_entry(lact, struct gpio_device, list);
+		if (pin_nr == gpio_dev->props.pin_nr) {
+			printk(KERN_INFO "unregistering gpio pin %i\n", pin_nr);
+			/* unregister attributes */
+			if (gpio_dev->props.policy & GPIO_USER)
+				class_device_remove_file(&gpio_dev->class_dev,&attr_gpio_level);
+			list_del(&gpio_dev->list);
+			write_unlock(&gpio_list_lock);
+			class_device_unregister(&gpio_dev->class_dev);
+			kfree(gpio_dev);
+			return;
+		}
+	}
+	write_unlock(&gpio_list_lock);
+	return;
+}
+EXPORT_SYMBOL(free_gpio);
+
+
+static struct sysdev_class gpio_sysclass = {
+	set_kset_name("gpio"),
+};
+
+static struct sys_device gpio_sys_device = {
+	.id		= 0,
+	.cls		= &gpio_sysclass,
+};
+
+
+enum {
+	Opt_in,
+	Opt_out_high,
+	Opt_out_low,
+	Opt_err,
+};
+
+
+static match_table_t tokens =
+{
+	{Opt_in, "%u:in"},
+	{Opt_out_high,"%u:out:hi"},
+	{Opt_out_low,"%u:out:lo"},
+	{Opt_err, NULL}
+};
+
+
+/**
+ * gpio_setup - parses string and registers GPIOs
+ * @s: string to parse
+ *
+ * Parses a string with the form
+ * <number>:(in|(out:(hi|lo)))[.<number>:(in|(out:(hi|lo)))]
+ * So it works with:
+ * - "86:in"
+ * - "86:in.119:in"
+ * - "86:in.119:in.121:out:hi"
+ *
+ * Returns 0 on success, -EINVAL if it cant parse the string or
+ * -EIO if the pin can't be registered
+ */
+static int gpio_setup(char *s)
+{
+	char *pin_nr_str;
+	int pin_nr;
+	unsigned char policy;
+	unsigned char init_level;
+	substring_t args[MAX_OPT_ARGS];
+	int token;
+	char *next_string;
+
+	next_string=s;
+
+	while (1) {
+		/*
+		 * find one part in this string
+		 */
+		while((*next_string != '\0') && (*next_string != '.'))
+			next_string++;
+
+		if (*next_string == '.') {	/* end or one more? */
+			*next_string = '\0';
+			next_string++;	/* ... one more */
+		}
+		policy = GPIO_USER;	/* reset */
+		init_level = 0;
+		/*
+		 * test this part
+		 */
+		token = match_token(s, tokens, args);
+		switch(token) {
+		/*
+		* global fallthrough
+		*/
+		case Opt_out_high:
+			init_level = 1;
+		case Opt_out_low:
+			policy |= GPIO_OUTPUT;
+		case Opt_in:
+			pin_nr_str = args[0].from;
+			pin_nr = simple_strtoull(pin_nr_str, &pin_nr_str, 0);
+			break;
+		default:
+			printk(KERN_ERR "%s: Unrecognized option \"%s\"\n", DRIVER_NAME, s);
+			return -EINVAL;
+		}
+		/*
+		 * register this part
+		 */
+		if (request_gpio(pin_nr, "kernel", policy, init_level) != 0) {
+			printk(KERN_ERR "%s: could not register GPIO pins!\n",DRIVER_NAME);
+			return -EIO;
+		}
+
+		if (*next_string == '\0')	/* end or one more? */
+			break;
+		else
+			s=next_string;	/* ... one more */
+	}
+
+	return(0);
+}
+
+
+/**
+ * gpio_map_store - map a GPIO pin to userspace
+ * @class:
+ * @buf: recieved string
+ * @count: size of string
+ *
+ * called by sysfs framework on behalf of the user, to map gpio pin to userspace
+ * This could be done for pin 3 (an output with high level) with:
+ * $ echo 3:out:hi > /sys/class/gpio/map_gpio
+ *
+ */
+static ssize_t gpio_map_store(struct class *class, const char *buf, size_t count)
+{
+	int ret;
+	char *tmp;
+
+	/* fist '\n' at the end of the buffer? */
+	if (((char *)memchr(buf, '\n', count) - buf + 1) != count)
+		return -EINVAL;
+
+	tmp = (char *)kmalloc(count, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	/* override the '\n' with end-of-string '\0' */
+	strncpy(tmp, buf, count);
+	*(tmp + count - 1) = '\0';
+
+	ret = gpio_setup(tmp);
+
+	kfree(tmp);
+
+	return ret ? ret : count;
+}
+
+
+/**
+ * gpio_unmap_store - unmap a GPIO pin
+ * @class:
+ * @buf: recieved string
+ * @count: size of string
+ *
+ * called by sysfs framework on behalf of the user, to unmap gpio pin from userspace
+ */
+static ssize_t gpio_unmap_store(struct class *class, const char *buf, size_t count)
+{
+	struct gpio_device *gpio_dev;
+	struct list_head *lact, *ltmp;
+	char *end;
+	int pin_nr;
+
+	pin_nr = (int)simple_strtol(buf, &end, 10);
+
+	/* stuff after pin-number */
+	if ((end - buf + 1) != count)
+		return -EINVAL;
+
+	write_lock(&gpio_list_lock);
+	list_for_each_safe(lact, ltmp, &gpio_list) {
+		gpio_dev = list_entry(lact, struct gpio_device, list);
+		if (pin_nr == gpio_dev->props.pin_nr) {
+			printk(KERN_INFO "unregistering gpio pin %i\n", pin_nr);
+			/* unregister attributes */
+			if (! (gpio_dev->props.policy & GPIO_USER))
+				return -EACCES;
+			class_device_remove_file(&gpio_dev->class_dev,&attr_gpio_level);
+			list_del(&gpio_dev->list);
+			write_unlock(&gpio_list_lock);
+			class_device_unregister(&gpio_dev->class_dev);
+			kfree(gpio_dev);
+			return count;
+		}
+	}
+	write_unlock(&gpio_list_lock);
+
+	printk(KERN_ERR "could not unregister gpio pin %i\n", pin_nr);
+	return -ENXIO;
+}
+
+static CLASS_ATTR(map_gpio,     0200,   NULL,   gpio_map_store);
+static CLASS_ATTR(unmap_gpio,   0200,   NULL,   gpio_unmap_store);
+
+
+static int __init gpio_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "Initialising gpio device class.\n");
+
+	ret = class_register(&gpio_class);
+	if (ret) {
+		printk(KERN_ERR "%s: couldn't register class, exiting\n", DRIVER_NAME);
+		goto out_class;
+	}
+
+	ret = sysdev_class_register(&gpio_sysclass);
+	if (ret) {
+		printk(KERN_ERR "%s: couldn't register sysdev class, exiting\n", DRIVER_NAME);
+		goto out_sysdev_class;
+	}
+
+	ret = sysdev_register(&gpio_sys_device);
+	if (ret) {
+		printk(KERN_ERR "%s: couldn't register sysdev, exiting\n", DRIVER_NAME);
+		goto out_sysdev_register;
+	}
+
+	ret = class_create_file(&gpio_class, &class_attr_map_gpio);
+	if (ret) {
+		printk(KERN_ERR "%s: couldn't register class attribute, exiting\n", DRIVER_NAME);
+		goto out_class_create_file_map_gpio;
+	}
+
+	ret = class_create_file(&gpio_class, &class_attr_unmap_gpio);
+	if (ret) {
+		printk(KERN_ERR "%s: couldn't register class attribute, exiting\n", DRIVER_NAME);
+		goto out_class_create_file_unmap_gpio;
+	}
+
+	if (!create_proc_read_entry ("gpio", 0, NULL, gpio_read_proc, NULL)) {
+		printk(KERN_ERR "%s: couldn't register proc entry, exiting\n", DRIVER_NAME);
+		ret = -1;
+		goto out_proc;
+	}
+	/*
+	 * If the user has given some kernel params,
+	 * parse them and setup the GPIOs
+	 */
+	ret=gpio_setup(mapping);
+
+	return ret;
+
+out_proc:
+	class_remove_file(&gpio_class, &class_attr_unmap_gpio);
+out_class_create_file_unmap_gpio:
+	class_remove_file(&gpio_class, &class_attr_map_gpio);
+out_class_create_file_map_gpio:
+	sysdev_unregister(&gpio_sys_device);
+out_sysdev_register:
+	sysdev_class_unregister(&gpio_sysclass);
+out_sysdev_class:
+	class_unregister(&gpio_class);
+out_class:
+	return ret;
+}
+
+module_init(gpio_init);
Index: include/linux/gpio.h
===================================================================
--- /dev/null
+++ include/linux/gpio.h
@@ -0,0 +1,25 @@
+/*
+ * include/linux/gpio.h
+ *
+ * Copyright (C) 2004 Robert Schwebel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __GPIO_H
+#define __GPIO_H
+
+#include "asm/arch/gpio.h"
+
+/* Values for policy */
+#define GPIO_USER       (1<<0)
+#define GPIO_OUTPUT     (1<<1)
+
+int request_gpio(unsigned int pin_nr, const char *owner,
+		 unsigned char policy, unsigned char init_level);
+
+void free_gpio(unsigned int pin_nr);
+
+#endif
Index: include/asm-arm/arch-pxa/gpio.h
===================================================================
--- /dev/null
+++ include/asm-arm/arch-pxa/gpio.h
@@ -0,0 +1,47 @@
+/*
+ * linux/include/asm-arm/arch-pxa/gpio.h
+ *
+ * Copyright (C) 2004 Robert Schwebel, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ARM_PXA_GPIO_H
+#define __ARM_PXA_GPIO_H
+
+#include <linux/kernel.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+static inline void gpio_set_pin(int gpio_nr)
+{
+	GPSR(gpio_nr) |= GPIO_bit(gpio_nr);
+	return;
+}
+
+static inline void gpio_clear_pin(int gpio_nr)
+{
+	GPCR(gpio_nr) |= GPIO_bit(gpio_nr);
+	return;
+}
+
+static inline void gpio_dir_input(int gpio_nr)
+{
+	GPDR(gpio_nr) &= ~GPIO_bit(gpio_nr);
+	return;
+}
+
+static inline void gpio_dir_output(int gpio_nr)
+{
+	GPDR(gpio_nr) |= GPIO_bit(gpio_nr);
+	return;
+}
+
+static inline int gpio_get_pin(int gpio_nr)
+{
+	return GPLR(gpio_nr) & GPIO_bit(gpio_nr) ? 1:0;
+}
+
+#endif
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux