lkml.org 
[lkml]   [2006]   [Jul]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
SubjectRe: [RFC] Proposal: common kernel-wide GPIO interface
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 <r.schwebel@pengutronix.de>

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

Signed-off-by: Robert Schwebel <r.schwebel@pengutronix.de>

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 <mkl@pengutronix.de>, 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 majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
\
 
 \ /
  Last update: 2006-07-30 15:11    [W:0.108 / U:0.056 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site