* [RFC] Proposal: common kernel-wide GPIO interface
@ 2006-07-28 20:44 Chris Boot
2006-07-29 19:41 ` Bill Davidsen
2006-07-30 13:08 ` Robert Schwebel
0 siblings, 2 replies; 17+ messages in thread
From: Chris Boot @ 2006-07-28 20:44 UTC (permalink / raw)
To: kernel list
Hello all,
More and more devices these days come with some sort of GPIO interface, and more
and more drivers within the kernel could make use of a common way of accessing
pins on such an interface, not to mention userspace apps. For example, we have
the I2C, LED, and SPI subsystems that each could drive a device that's actually
connected to some GPIO pins somewhere.
I propose to develop a common way of registering and accessing GPIO pins on
various devices. Now I'm no hardware expert, but I do like to dabble a bit and
would love to see such a system be developed. Most people tend to attach stuff
like LCD displays to their parallel ports, but GPIOs are much better suited to
such a purpose than a parallel port. Some (out of tree) drivers even emulate a
parallel interface in order that userspace software can be fooled to use the
GPIO pins as a parallel port. In my view, this is ugly.
As far as I can tell, GPIO interfaces all share the following attributes:
- One or more ports made up of one or more individual pins
- Value on input or output depending on configuration
- Various configuration bits might be available to influence the pin's behaviour
- Direction
- Push-pull / Open drain
- Pull-up enable
- Possibly others
We'll need some way of assigning pins to different functions I'm guessing as
well, especially when we come to write drivers for the interface. It might not
fit right into the GPIO driver, but as far as I can see the I2C, LED, and SPI
subsystem drivers would need such a method to create generic
GPIO<=>{I2C,LED,SPI} drivers.
As well as a kernel interface I propose a userspace interface of some kind. I'm
not entirely sure what might be the most efficient way of doing this, but the
current standard way seems to be to create a sysfs class interface, although an
old-fashioned device interface with IOCTLs and so on might be the best way
forward. Any suggestions?
As for drivers, to start with I suggest a parallel port driver as well as
drivers for the NSC SCx200 and PC8736x (since I own a board with both of those).
So, if anyone likes this idea and/or has some comments, please voice your
opinions! With a little guidance from the masters, I'm willing to put the effort
in to code such a system, but I'd really like to hear what people involved both
in the hardware side and software side of GPIOs and the kernel have to say about
such an interface.
Many thanks,
Chris
--
Chris Boot
bootc@bootc.net
http://www.bootc.net/
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-07-28 20:44 [RFC] Proposal: common kernel-wide GPIO interface Chris Boot
@ 2006-07-29 19:41 ` Bill Davidsen
2006-07-30 13:08 ` Robert Schwebel
1 sibling, 0 replies; 17+ messages in thread
From: Bill Davidsen @ 2006-07-29 19:41 UTC (permalink / raw)
To: Chris Boot, Kernel Mailing List
Chris Boot wrote:
> Hello all,
>
> More and more devices these days come with some sort of GPIO interface,
> and more and more drivers within the kernel could make use of a common
> way of accessing pins on such an interface, not to mention userspace
> apps. For example, we have the I2C, LED, and SPI subsystems that each
> could drive a device that's actually connected to some GPIO pins somewhere.
>
> I propose to develop a common way of registering and accessing GPIO pins
> on various devices. Now I'm no hardware expert, but I do like to dabble
> a bit and would love to see such a system be developed. Most people tend
> to attach stuff like LCD displays to their parallel ports, but GPIOs are
> much better suited to such a purpose than a parallel port. Some (out of
> tree) drivers even emulate a parallel interface in order that userspace
> software can be fooled to use the GPIO pins as a parallel port. In my
> view, this is ugly.
>
...
> So, if anyone likes this idea and/or has some comments, please voice
> your opinions! With a little guidance from the masters, I'm willing to
> put the effort in to code such a system, but I'd really like to hear
> what people involved both in the hardware side and software side of
> GPIOs and the kernel have to say about such an interface.
I think it's interesting enough so that it's worth figuring how this
would work with or replace the various interfaces you mentioned. I'm
sure another discussion would take place at that point, before writing code.
--
Bill Davidsen <davidsen@tmr.com>
Obscure bug of 2004: BASH BUFFER OVERFLOW - if bash is being run by a
normal user and is setuid root, with the "vi" line edit mode selected,
and the character set is "big5," an off-by-one errors occurs during
wildcard (glob) expansion.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-07-28 20:44 [RFC] Proposal: common kernel-wide GPIO interface Chris Boot
2006-07-29 19:41 ` Bill Davidsen
@ 2006-07-30 13:08 ` Robert Schwebel
2006-07-30 22:02 ` Ben Dooks
2006-08-01 21:25 ` Jim Cromie
1 sibling, 2 replies; 17+ messages in thread
From: Robert Schwebel @ 2006-07-30 13:08 UTC (permalink / raw)
To: Chris Boot; +Cc: kernel list
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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-07-30 13:08 ` Robert Schwebel
@ 2006-07-30 22:02 ` Ben Dooks
2006-07-31 16:10 ` Chris Boot
2006-08-01 15:53 ` Jim Cromie
2006-08-01 21:25 ` Jim Cromie
1 sibling, 2 replies; 17+ messages in thread
From: Ben Dooks @ 2006-07-30 22:02 UTC (permalink / raw)
To: Robert Schwebel; +Cc: Chris Boot, kernel list
On Sun, Jul 30, 2006 at 03:08:11PM +0200, Robert Schwebel wrote:
> 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.
Right, my $0.02 worth:
1) The system does not currently allow for other GPIO sources
than the CPU. There are a variety of GPIOs, that could come
from expansion chips, on board CPLDs, etc.
2) The GPIO configuration from my last thought experiment have the
following properties for each pin:
input:
- input
- inverted input
output:
- normal output
- inverted output
- tristatable output
- open collector (can only pull to zero)
- open emmitor (can only pull to high)
The allowance of inverted outputs, is very useful to allow
drivers to assume either '0' or '1' is an active signal, allowing
per-board fixups when the designer suddely decides the best way
of connecting device A to B is via a spare inverter...
The other way would be to allow the mapping of '0' and '1' states
to either of the states:
- output 1
- output 0
- tri-state
The classing of tri-state as a seperate from input, is in case the
hardware does not see input as a valid state, or that input and
output are somehow different.
pull resistor:
- tristate (no resistor)
- pull low
- pull high
The input and output are seperate, assuming that there is the
possiblity the system can read back the line even if the GPIO
is set as an output.
3) The sysfs interface should be configurable, as systems
with lots of GPIO would end up with large numbers of
files and directories in sysfs.
4) you probably want to ensure pull-up resistors are off if the
output is being driven.
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-07-30 22:02 ` Ben Dooks
@ 2006-07-31 16:10 ` Chris Boot
2006-07-31 20:17 ` Robert Schwebel
2006-08-01 15:53 ` Jim Cromie
1 sibling, 1 reply; 17+ messages in thread
From: Chris Boot @ 2006-07-31 16:10 UTC (permalink / raw)
To: Ben Dooks; +Cc: Robert Schwebel, kernel list
Ben Dooks wrote:
> On Sun, Jul 30, 2006 at 03:08:11PM +0200, Robert Schwebel wrote:
>> 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.
>
> Right, my $0.02 worth:
[snip]
Some very interesting comments and suggestions! Thanks very much for all the
info, that's exactly the sort of stuff I needed.
> 3) The sysfs interface should be configurable, as systems
> with lots of GPIO would end up with large numbers of
> files and directories in sysfs.
My current idea is to divide the interfaces by GPIO device and port. I've so far
not seen a GPIO device that couldn't be divided into ports of <= 32 bits. How
wide can a 'port' actually become?
Somehow I think that a separate device/file for each pin or possibly even port
might not be a wise idea. For example, twiddling individual pins on a GPIO when
you connect an LCD, I2C, or SPI interface seems extremely inefficient...
> 4) you probably want to ensure pull-up resistors are off if the
> output is being driven.
Yes, very good idea! So far I haven't managed to fry any chips by driving a
pulled up/down output, but it's so easy to make the mistake...
Chris
--
Chris Boot
bootc@bootc.net
http://www.bootc.net/
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-07-31 16:10 ` Chris Boot
@ 2006-07-31 20:17 ` Robert Schwebel
2006-07-31 21:23 ` Chris Boot
0 siblings, 1 reply; 17+ messages in thread
From: Robert Schwebel @ 2006-07-31 20:17 UTC (permalink / raw)
To: Chris Boot; +Cc: Ben Dooks, kernel list
On Mon, Jul 31, 2006 at 05:10:56PM +0100, Chris Boot wrote:
> My current idea is to divide the interfaces by GPIO device and port.
> I've so far not seen a GPIO device that couldn't be divided into ports
> of <= 32 bits. How wide can a 'port' actually become?
Not all GPIOs can be abstracted into "ports" in a sane way. For example,
the PXAs have something like 80 GPIOs, which might be randomly divided
into alternate functions and "real" GPIOs.
> Somehow I think that a separate device/file for each pin or possibly
> even port might not be a wise idea. For example, twiddling individual
> pins on a GPIO when you connect an LCD, I2C, or SPI interface seems
> extremely inefficient...
I think you have to take care about two things: a) a registration
infrastructure (which GPIO pin was requested, similar to mem and irq)
and b) an access API. For slow things like LEDs you might want to have
unified access functions, but for fast GPIOs (bitbanging i2c etc) you
want to directly manipulate the registers once you've requested them.
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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-07-31 20:17 ` Robert Schwebel
@ 2006-07-31 21:23 ` Chris Boot
2006-08-01 7:40 ` Juergen Beisert
0 siblings, 1 reply; 17+ messages in thread
From: Chris Boot @ 2006-07-31 21:23 UTC (permalink / raw)
To: Robert Schwebel; +Cc: Ben Dooks, kernel list
Robert Schwebel wrote:
> On Mon, Jul 31, 2006 at 05:10:56PM +0100, Chris Boot wrote:
>> My current idea is to divide the interfaces by GPIO device and port.
>> I've so far not seen a GPIO device that couldn't be divided into ports
>> of <= 32 bits. How wide can a 'port' actually become?
>
> Not all GPIOs can be abstracted into "ports" in a sane way. For example,
> the PXAs have something like 80 GPIOs, which might be randomly divided
> into alternate functions and "real" GPIOs.
Hmm, now that does complicate things then. :-/
>> Somehow I think that a separate device/file for each pin or possibly
>> even port might not be a wise idea. For example, twiddling individual
>> pins on a GPIO when you connect an LCD, I2C, or SPI interface seems
>> extremely inefficient...
>
> I think you have to take care about two things: a) a registration
> infrastructure (which GPIO pin was requested, similar to mem and irq)
> and b) an access API. For slow things like LEDs you might want to have
> unified access functions, but for fast GPIOs (bitbanging i2c etc) you
> want to directly manipulate the registers once you've requested them.
Yes I was thinking that a GPIO is a resource a little like an IRQ and thinking
of a registration and ownership system as well. I'm glad somebody came up with
that suggestion!
The access API is, as you say, more difficult. The access methods for slow GPIOs
is indeed very simple but I can't think of any way to provide (near-)direct
access for faster accesses in a portable way. Does anyone have any suggestions?
--
Chris Boot
bootc@bootc.net
http://www.bootc.net/
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-07-31 21:23 ` Chris Boot
@ 2006-08-01 7:40 ` Juergen Beisert
0 siblings, 0 replies; 17+ messages in thread
From: Juergen Beisert @ 2006-08-01 7:40 UTC (permalink / raw)
To: Chris Boot, Robert Schwebel; +Cc: Ben Dooks, kernel list
Hi Chris,
some ideas:
On Monday 31 July 2006 23:23, Chris Boot wrote:
> Yes I was thinking that a GPIO is a resource a little like an IRQ and
> thinking of a registration and ownership system as well. I'm glad somebody
> came up with that suggestion!
>
> The access API is, as you say, more difficult. The access methods for slow
> GPIOs is indeed very simple but I can't think of any way to provide
> (near-)direct access for faster accesses in a portable way. Does anyone
> have any suggestions?
Maybe three kind of API. One for fastest access: The driver request the GPIO
itself from the GPIO management and has to share some kind of locking
mechanism with the other API to avoid conflicts when accessing the registers
to manipulate this GPIO. This could be the way an I2C driver works to be as
fast as possible. Buts its achitecture specific.
The second API is slower and supports some functions to manipulate the GPIOs
at higer lever. This API would be architrecture independend but for use in
the kernel only.
The third API I like to have is for some slow GPIO. They should be accessible
from user space in an easy way. For example through some entries in sysfs the
user can access with simple "cat" and "echo" commands.
Alltogether share the GPIO management and the locking mechanism.
Juergen
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-07-30 22:02 ` Ben Dooks
2006-07-31 16:10 ` Chris Boot
@ 2006-08-01 15:53 ` Jim Cromie
1 sibling, 0 replies; 17+ messages in thread
From: Jim Cromie @ 2006-08-01 15:53 UTC (permalink / raw)
To: Ben Dooks; +Cc: Robert Schwebel, Chris Boot, kernel list
Ben Dooks wrote:
> On Sun, Jul 30, 2006 at 03:08:11PM +0200, Robert Schwebel wrote:
>
>> 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.
>>
>
> Right, my $0.02 worth:
>
$2.00 at least.
I have a patch which adds a sysfs interface much as youve described below.
an old version of patch is here:
http://marc.theaimsgroup.com/?l=linux-kernel&m=115324483926147&w=2
Its far from complete, but I think it belongs in this discussion !
> 1) The system does not currently allow for other GPIO sources
> than the CPU. There are a variety of GPIOs, that could come
> from expansion chips, on board CPLDs, etc.
>
>
Im not sure what you mean here -
the above patch manages to add a sysfs-gpio interface to 2 drivers:
scx200_gpio, and pc8736x_gpio.
ISTM that you've described a limitation of Robert Schwebel's patch,
since his examples use a single path in sysfs.
+ Or to stop the motor again:
+ $ echo 1 > /sys/class/gpio/gpio63/level
Heres my sysfs-gpio interface, which obviously covers both drivers:
soekris:/sys/devices/platform# ls *.0/bit_0.0_*
pc8736x_gpio.0/bit_0.0_current_output scx200_gpio.0/bit_0.0_current_output
pc8736x_gpio.0/bit_0.0_debounced scx200_gpio.0/bit_0.0_debounced
pc8736x_gpio.0/bit_0.0_locked scx200_gpio.0/bit_0.0_locked
pc8736x_gpio.0/bit_0.0_output_enabled scx200_gpio.0/bit_0.0_output_enabled
pc8736x_gpio.0/bit_0.0_pullup_enabled scx200_gpio.0/bit_0.0_pullup_enabled
pc8736x_gpio.0/bit_0.0_status scx200_gpio.0/bit_0.0_status
pc8736x_gpio.0/bit_0.0_totem scx200_gpio.0/bit_0.0_totem
pc8736x_gpio.0/bit_0.0_value scx200_gpio.0/bit_0.0_value
soekris:/sys/devices/platform#
Robert, sysfs seems to populate lots of symlinks underneath /sys,
do any of them give a device-centric organization that lets you address
separate devices ?
> 2) The GPIO configuration from my last thought experiment have the
> following properties for each pin:
>
> input:
> - input
> - inverted input
>
> output:
> - normal output
> - inverted output
> - tristatable output
> - open collector (can only pull to zero)
> - open emmitor (can only pull to high)
>
> The allowance of inverted outputs, is very useful to allow
> drivers to assume either '0' or '1' is an active signal, allowing
> per-board fixups when the designer suddely decides the best way
> of connecting device A to B is via a spare inverter...
>
> The other way would be to allow the mapping of '0' and '1' states
> to either of the states:
>
> - output 1
> - output 0
> - tri-state
>
> The classing of tri-state as a seperate from input, is in case the
> hardware does not see input as a valid state, or that input and
> output are somehow different.
>
> pull resistor:
> - tristate (no resistor)
> - pull low
> - pull high
>
> The input and output are seperate, assuming that there is the
> possiblity the system can read back the line even if the GPIO
> is set as an output.
>
>
heres how Im doing it..
struct gpio_attributes {
struct sensor_device_attribute_2 value;
struct sensor_device_attribute_2 curr;
struct sensor_device_attribute_2 output_enabled;
struct sensor_device_attribute_2 totem_pole;
struct sensor_device_attribute_2 pullup_enabled;
struct sensor_device_attribute_2 debounced;
struct sensor_device_attribute_2 locked;
struct sensor_device_attribute_2 status;
};
> 3) The sysfs interface should be configurable, as systems
> with lots of GPIO would end up with large numbers of
> files and directories in sysfs.
>
>
static int nobits = 0;
module_param(nobits, int, 0);
MODULE_PARM_DESC(nobits, "nobits=1 to suppress sysfs bits interface");
static int noports = 0;
module_param(noports, int, 0);
MODULE_PARM_DESC(noports, "noports=1 to supress sysfs ports interface");
> 4) you probably want to ensure pull-up resistors are off if the
> output is being driven.
>
>
Sounds smart. Is there any reason to make it overrideable ?
thanks
-jimc
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-07-30 13:08 ` Robert Schwebel
2006-07-30 22:02 ` Ben Dooks
@ 2006-08-01 21:25 ` Jim Cromie
2006-08-02 7:28 ` Robert Schwebel
2006-08-02 17:58 ` Lennart Sorensen
1 sibling, 2 replies; 17+ messages in thread
From: Jim Cromie @ 2006-08-01 21:25 UTC (permalink / raw)
To: Robert Schwebel; +Cc: Chris Boot, kernel list
Robert Schwebel wrote:
> 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
>
this is cool to see. Using a class-driver is very different from the
vtable-approach
that I used (struct nsc_gpio_ops) in pc8736x_gpio and scx200_gpio.
Are any of the limitation youve cited above related to the
/sys/class/gpio paths below ?
+ 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
+
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-08-01 21:25 ` Jim Cromie
@ 2006-08-02 7:28 ` Robert Schwebel
2006-08-02 17:58 ` Lennart Sorensen
1 sibling, 0 replies; 17+ messages in thread
From: Robert Schwebel @ 2006-08-02 7:28 UTC (permalink / raw)
To: Jim Cromie; +Cc: Chris Boot, kernel list
On Tue, Aug 01, 2006 at 03:25:32PM -0600, Jim Cromie wrote:
> >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
> >
> this is cool to see. Using a class-driver is very different from the
> vtable-approach
> that I used (struct nsc_gpio_ops) in pc8736x_gpio and scx200_gpio.
>
> Are any of the limitation youve cited above related to the
> /sys/class/gpio paths below ?
>
> + 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
> +
Please re-read my original mail.
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
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-08-01 21:25 ` Jim Cromie
2006-08-02 7:28 ` Robert Schwebel
@ 2006-08-02 17:58 ` Lennart Sorensen
2006-08-02 20:48 ` Jim Cromie
1 sibling, 1 reply; 17+ messages in thread
From: Lennart Sorensen @ 2006-08-02 17:58 UTC (permalink / raw)
To: Jim Cromie; +Cc: Robert Schwebel, Chris Boot, kernel list
On Tue, Aug 01, 2006 at 03:25:32PM -0600, Jim Cromie wrote:
> this is cool to see. Using a class-driver is very different from the
> vtable-approach
> that I used (struct nsc_gpio_ops) in pc8736x_gpio and scx200_gpio.
>
> Are any of the limitation youve cited above related to the
> /sys/class/gpio paths below ?
>
> + 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
> +
How do you deal with having multiple places that provide GPIOs then? I
may have 8 pins on a PCI UART chip, 22 on my super io chip, 16 on my
cpu, etc. How would this be mapped if you only have one map_gpio
method? It is much simpler to code for knowing pin 0 to 7 of device
uartgpio is where my UART pins are, and some other device has 22 pins
for the super io chip. If they all ended up in one place with
consequative numbers it would be a real pain.
Sometimes it is also nice to be able to control multiple pins as a block,
which only a few gpio interfaces seem to provide (they all seem to think
they should only be moved one pin at a time, which makes for a lot more
system calls to get things done).
Right now I am working on adding some stuff to the jsm driver to use an
Exar uart along with using the gpios, and so far I added gpio access
similar to how scx200_gpio does things, using minors 0 to 7 for the 8
pins on the first uart, 8 to 15 for the second, and so on. What to name
the /dev entries is a different issue. I can identify which device to
look for based on the /sys info for which pci slot the uart is connected
to. I am not sure how this would tie into a generic gpio design.
Does your gpio design deal with all the things gpios often do:
input/output/tristate
high/low
generate interrupt
edge/level trigger
high or low level/leading or trailing edge trigger
--
Len Sorensen
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-08-02 17:58 ` Lennart Sorensen
@ 2006-08-02 20:48 ` Jim Cromie
2006-08-03 13:55 ` Lennart Sorensen
2006-08-08 23:01 ` [RFC - patch] add a gpio-sysfs interface - was: " Jim Cromie
0 siblings, 2 replies; 17+ messages in thread
From: Jim Cromie @ 2006-08-02 20:48 UTC (permalink / raw)
To: Lennart Sorensen; +Cc: Robert Schwebel, Chris Boot, kernel list
Lennart Sorensen wrote:
> On Tue, Aug 01, 2006 at 03:25:32PM -0600, Jim Cromie wrote:
>
>> this is cool to see. Using a class-driver is very different from the
>> vtable-approach
>> that I used (struct nsc_gpio_ops) in pc8736x_gpio and scx200_gpio.
>>
>> Are any of the limitation youve cited above related to the
>> /sys/class/gpio paths below ?
>>
>> + 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
>> +
>>
>
> How do you deal with having multiple places that provide GPIOs then?
pc8736x_gpio and scx200_gpio appear here:
soekris:/sys/devices/platform# ls pc8736x_gpio.0/
Display all 292 possibilities? (y or n)
soekris:/sys/devices/platform# ls scx200_gpio.0/
Display all 532 possibilities? (y or n)
soekris:/sys/devices/platform# ls scx200_gpio.0/bit_0.0_*
scx200_gpio.0/bit_0.0_current_output scx200_gpio.0/bit_0.0_pullup_enabled
scx200_gpio.0/bit_0.0_debounced scx200_gpio.0/bit_0.0_status
scx200_gpio.0/bit_0.0_locked scx200_gpio.0/bit_0.0_totem
scx200_gpio.0/bit_0.0_output_enabled scx200_gpio.0/bit_0.0_value
Did you mean to ask that question of Robert ?
I'll rephrase my Q here.
/sys/class/gpio/gpio63/
this suggests that either
- only 1 GPIO device can register (bad)
- reservations might be taken in module-load order, and assigned
numerically (bad-subtle)
Using another path (like /sys/devices/platform/scx200_gpio.%d/ )
which names the driver (or some other structural info) seems much more
stable in the face of combinations of GPIO hardware.
FWIW, I didnt add the .0 to the directories, I think that was added for
me by the device-core,
(warmfuzzy) so Id expect it to handle .1,2,3 etc..
> I
> may have 8 pins on a PCI UART chip, 22 on my super io chip, 16 on my
> cpu, etc. How would this be mapped if you only have one map_gpio
> method? It is much simpler to code for knowing pin 0 to 7 of device
> uartgpio is where my UART pins are, and some other device has 22 pins
> for the super io chip. If they all ended up in one place with
> consequative numbers it would be a real pain.
>
> Sometimes it is also nice to be able to control multiple pins as a block,
> which only a few gpio interfaces seem to provide (they all seem to think
> they should only be moved one pin at a time, which makes for a lot more
> system calls to get things done).
>
Both GPIO chips Ive touched have port-wide read and write.
I consider it an essential minimum feature in the driver, for hardware
that supports it.
Other pin features (OE, etc) are only controllable per-pin.
If we synthesize port-wide from per-pin, then we get a bit/port agnostic
interface.
( driver users must still be cognizant of the limitations of synthetic
OutputEnable,
where tri-stating would take many bus cycles )
> Right now I am working on adding some stuff to the jsm driver to use an
> Exar uart along with using the gpios, and so far I added gpio access
> similar to how scx200_gpio does things, using minors 0 to 7 for the 8
> pins on the first uart, 8 to 15 for the second, and so on. What to name
> the /dev entries is a different issue. I can identify which device to
> look for based on the /sys info for which pci slot the uart is connected
> to. I am not sure how this would tie into a generic gpio design.
>
> Does your gpio design
I want to separate my answers -
- pc8736x_gpio , scx200_gpio went thru mm into mainline-rc - they
support the legacy gpio-bit
access via char-device-file. They expose port-wide read/write inside
the kernel, via struct nsc_gpio_ops,
but it seems a bad idea to expose them as device-files. ;-)
- This thread is about a new interface, I think we're all tacitly
agreeing on :
a sysfs based GPIO-attr representation
some of us want/demand a port-interface where hardware has portwide
read/write
a reservation scheme.
- Im working on a patch, which rendered the ls output I pasted above.
bits_ and ports_ agnostic
interfaces are nearly identical - its 0/1 vs 0xFF (hw dependent width)
no reservations yet :-/
> deal with all the things gpios often do:
>
char-dev interfaces in scx200_gpio 18-rc are compatible with legacy,
pc87360 is new (and same).
my sysfs-gpio patch actually has a half-baked compatibly hack on the
_status attr,
platform# more scx200_gpio.0/bit_0.0_status
io00: 0x0044 TS OD PUE EDGE LO DEBOUNCE io:1/1
> input/output/tristate
> high/low
>
not yet on these: patches/clues welcome.
> generate interrupt
> edge/level trigger
> high or low level/leading or trailing edge trigger
>
> --
> Len Sorensen
>
>
thanks for the input
Jim Cromie
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-08-02 20:48 ` Jim Cromie
@ 2006-08-03 13:55 ` Lennart Sorensen
2006-08-03 15:42 ` Robert Schwebel
2006-08-08 23:01 ` [RFC - patch] add a gpio-sysfs interface - was: " Jim Cromie
1 sibling, 1 reply; 17+ messages in thread
From: Lennart Sorensen @ 2006-08-03 13:55 UTC (permalink / raw)
To: Jim Cromie; +Cc: Robert Schwebel, Chris Boot, kernel list
On Wed, Aug 02, 2006 at 02:48:33PM -0600, Jim Cromie wrote:
> pc8736x_gpio and scx200_gpio appear here:
>
> soekris:/sys/devices/platform# ls pc8736x_gpio.0/
> Display all 292 possibilities? (y or n)
>
> soekris:/sys/devices/platform# ls scx200_gpio.0/
> Display all 532 possibilities? (y or n)
>
>
> soekris:/sys/devices/platform# ls scx200_gpio.0/bit_0.0_*
> scx200_gpio.0/bit_0.0_current_output scx200_gpio.0/bit_0.0_pullup_enabled
> scx200_gpio.0/bit_0.0_debounced scx200_gpio.0/bit_0.0_status
> scx200_gpio.0/bit_0.0_locked scx200_gpio.0/bit_0.0_totem
> scx200_gpio.0/bit_0.0_output_enabled scx200_gpio.0/bit_0.0_value
Hmm, that certainly is different than the old /dev/... interface. Could
be nice.
> Did you mean to ask that question of Robert ?
I might have. :)
> I'll rephrase my Q here.
>
> /sys/class/gpio/gpio63/
>
> this suggests that either
> - only 1 GPIO device can register (bad)
Unacceptably bad. I currently use anywhere from 2 to 4 different
devices with GPIOs.
> - reservations might be taken in module-load order, and assigned
> numerically (bad-subtle)
Too messy.
> Using another path (like /sys/devices/platform/scx200_gpio.%d/ )
> which names the driver (or some other structural info) seems much more
> stable in the face of combinations of GPIO hardware.
That I think is perfectly easy to work with, as long as I can somehow
find which device (usually on PCI bus in my case) goes to which
directory. I suspect that would be the case.
> FWIW, I didnt add the .0 to the directories, I think that was added for
> me by the device-core,
> (warmfuzzy) so Id expect it to handle .1,2,3 etc..
That sounds nice and convinient.
> Both GPIO chips Ive touched have port-wide read and write.
> I consider it an essential minimum feature in the driver, for hardware
> that supports it.
> Other pin features (OE, etc) are only controllable per-pin.
> If we synthesize port-wide from per-pin, then we get a bit/port agnostic
> interface.
> ( driver users must still be cognizant of the limitations of synthetic
> OutputEnable,
> where tri-stating would take many bus cycles )
I only consider the change of state on output pins, or reading state of
input pins to be a requirement for port-wide. Changing between
input/output, and enable and tristate and such, I have no problem with
doing bit by bit, since that is setup stuff.
> - pc8736x_gpio , scx200_gpio went thru mm into mainline-rc - they
> support the legacy gpio-bit
> access via char-device-file. They expose port-wide read/write inside
> the kernel, via struct nsc_gpio_ops,
> but it seems a bad idea to expose them as device-files. ;-)
Well I haven't seen a gpio driver that did port wide yet. I treat the
ppdev as 8 gpio pins, so at least for that I get port wide.
> - This thread is about a new interface, I think we're all tacitly
> agreeing on :
> a sysfs based GPIO-attr representation
> some of us want/demand a port-interface where hardware has portwide
> read/write
> a reservation scheme.
Sounds lovely to me.
> - Im working on a patch, which rendered the ls output I pasted above.
> bits_ and ports_ agnostic
> interfaces are nearly identical - its 0/1 vs 0xFF (hw dependent width)
> no reservations yet :-/
I look forward to seeing it.
> char-dev interfaces in scx200_gpio 18-rc are compatible with legacy,
> pc87360 is new (and same).
> my sysfs-gpio patch actually has a half-baked compatibly hack on the
> _status attr,
> platform# more scx200_gpio.0/bit_0.0_status
> io00: 0x0044 TS OD PUE EDGE LO DEBOUNCE io:1/1
Keeping the legacy for at least a while is probably a requirement since
people are already using those interfaces on that hardware.
> not yet on these: patches/clues welcome.
> >generate interrupt
> >edge/level trigger
> >high or low level/leading or trailing edge trigger
Well tying into interrupt handlers would be tricky I suspect. On the
other hand someone might have a use for the feature. I just mentioned
them because it is a feature of some of the devices I use, although I
don't use them for anything other than input/output myself.
--
Len Sorensen
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC] Proposal: common kernel-wide GPIO interface
2006-08-03 13:55 ` Lennart Sorensen
@ 2006-08-03 15:42 ` Robert Schwebel
0 siblings, 0 replies; 17+ messages in thread
From: Robert Schwebel @ 2006-08-03 15:42 UTC (permalink / raw)
To: Lennart Sorensen; +Cc: Jim Cromie, Robert Schwebel, Chris Boot, kernel list
On Thu, Aug 03, 2006 at 09:55:58AM -0400, Lennart Sorensen wrote:
> > I'll rephrase my Q here.
> >
> > /sys/class/gpio/gpio63/
> >
> > this suggests that either - only 1 GPIO device can register (bad)
>
> Unacceptably bad. I currently use anywhere from 2 to 4 different
> devices with GPIOs.
Sure, it would probably be nice to have GPIO devices in sysfs dirs of
their respective parent. The variant I've sent was just a quick-n-dirty
framework we hacked for a project.
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
^ permalink raw reply [flat|nested] 17+ messages in thread
* [RFC - patch] add a gpio-sysfs interface - was: Proposal: common kernel-wide GPIO interface
2006-08-02 20:48 ` Jim Cromie
2006-08-03 13:55 ` Lennart Sorensen
@ 2006-08-08 23:01 ` Jim Cromie
2006-08-09 17:12 ` Jim Cromie
1 sibling, 1 reply; 17+ messages in thread
From: Jim Cromie @ 2006-08-08 23:01 UTC (permalink / raw)
To: Chris Boot; +Cc: Lennart Sorensen, Robert Schwebel, kernel list
Jim Cromie wrote:
>
> nsc_gpio, pc8736x_gpio , scx200_gpio went thru mm into mainline-rc -
> they support the legacy gpio-bit
> access via char-device-file. They expose port-wide read/write inside
> the kernel,
correction - in-vtable portwide-accessors are in a to-be-applied patch
from Chris Boot to me.
Its in-my-queue for post 18, since we're now rc.
Im attaching a newer version of a patch sent earlier this thread.
It adds a sysfs-gpio interface, for both bits & ports.
- same feature-set for both.
- tried port-synthesis -
for pin-features that are implemented as per-bit only,
do set of queries/updates, and bundle them for presentation.
(having explored this, Im hedging ..)
the set of pin_* and port_* attributes are supported by 8 sysfs-callbacks,
split on following axes: bit/port, value/config, read/write (always,
diff fn-sigs).
*_value callbacks are simple/fast
both my gpio chips are port-based, bit-wide methods add masking
*_conf callbacks handle many attributes
_bit_conf - natural match - fetches bit, returns bit
_port_conf - synthesize port from bits
The 8 callbacks are mapped into place by 2 macros in nsc_gpio.h,
one each for bits/ports. BUG: the mapping is inelegant. see code for
more comments.
WRT port-synthesis from bit-wide properties
- different chips expose different properties as bit-vectors
pc87366 gives data-in, data-out-rd/wr only,
sc-1100 also shows interrupt-enable, gpio-event-status as bit-vectors
- port-synthesis lets us hide the difference, if done correctly.
BUG : Due to the macro design (built for static decl & initialization),
its cumbersome to do the mapping.
- alternative (and simpler) approach is to :
selectively create attr-files, thus exposing only the features desired
let user-space figure it out
this is all restated in patch comments.
Patch does nothing wrt reservations or class_dev design,
Chris, Im hoping you're focussing here, it would be really cool if my
callbacks
were to just drop right into gpio class impl ;-)
The following cut-pastes demonstrate some level of working-ness,
1 - pin reads via char-device-files
2 - same pins, read as sys-attr files, bit/port (values)
3 - demonstrates bitpos=1 exposing bit-in-port (values)
4 - config-features work - bit-read/write works, effect visible in
port-reads
5 - port-wide synthesis mostly works - isnt working yet
6 - currently nsc_gpio.c defines DEBUG 1, giving output cut-pasted below
pc8736x-gpio port reads:
running: cat /sys/devices/platform/pc8736x_gpio.0/port_0_value
/sys/devices/platform/pc8736x_gpio.0/port_1_value
/sys/devices/platform/pc8736x_gpio.0/port_2_value
/sys/devices/platform/pc8736x_gpio.0/port_3_value
0xff
0xd1
0x9e
0x1d
...
and bit-reads: (bitpos=1 exposes more info)
running: cat /sys/devices/platform/pc8736x_gpio.0/bit_1.0_value
/sys/devices/platform/pc8736x_gpio.0/bit_1.1_value
/sys/devices/platform/pc8736x_gpio.0/bit_1.2_value
/sys/devices/platform/pc8736x_gpio.0/bit_1.3_value
/sys/devices/platform/pc8736x_gpio.0/bit_1.4_value
/sys/devices/platform/pc8736x_gpio.0/bit_1.5_value
/sys/devices/platform/pc8736x_gpio.0/bit_1.6_value
/sys/devices/platform/pc8736x_gpio.0/bit_1.7_value
0x1
0x0
0x0
0x0
0x10
0x0
0x40
0x80
test port-synthesis - cmd-letter is borrowed from set understood by
char-dev-write-handler
running: cat /sys/devices/platform/pc8736x_gpio.0/port_0_output_enabled
[ 2039.060925] platform pc8736x_gpio.0: get-portconf bit:0.0 cfg:44
cmd'O' res:0
[ 2039.068312] platform pc8736x_gpio.0: get-portconf bit:0.1 cfg:44
cmd'O' res:0
[ 2039.075703] platform pc8736x_gpio.0: get-portconf bit:0.2 cfg:44
cmd'O' res:0
[ 2039.083053] platform pc8736x_gpio.0: get-portconf bit:0.3 cfg:44
cmd'O' res:0
[ 2039.090434] platform pc8736x_gpio.0: get-portconf bit:0.4 cfg:44
cmd'O' res:0
[ 2039.097789] platform pc8736x_gpio.0: get-portconf bit:0.5 cfg:44
cmd'O' res:0
[ 2039.105140] platform pc8736x_gpio.0: get-portconf bit:0.6 cfg:44
cmd'O' res:0
[ 2039.112487] platform pc8736x_gpio.0: get-portconf bit:0.7 cfg:44
cmd'O' res:0
[ 2039.119875] platform pc8736x_gpio.0: get-portconf() idx=0 cmd='O' ret:0
0x0
running: cat /sys/devices/platform/pc8736x_gpio.0/port_0_pullup_enabled
[ 2039.185437] platform pc8736x_gpio.0: get-portconf bit:0.0 cfg:44
cmd'P' res:0
[ 2039.192829] platform pc8736x_gpio.0: get-portconf bit:0.1 cfg:44
cmd'P' res:0
[ 2039.200181] platform pc8736x_gpio.0: get-portconf bit:0.2 cfg:44
cmd'P' res:0
[ 2039.207531] platform pc8736x_gpio.0: get-portconf bit:0.3 cfg:44
cmd'P' res:0
[ 2039.214887] platform pc8736x_gpio.0: get-portconf bit:0.4 cfg:44
cmd'P' res:0
[ 2039.222244] platform pc8736x_gpio.0: get-portconf bit:0.5 cfg:44
cmd'P' res:0
[ 2039.229633] platform pc8736x_gpio.0: get-portconf bit:0.6 cfg:44
cmd'P' res:0
[ 2039.236995] platform pc8736x_gpio.0: get-portconf bit:0.7 cfg:44
cmd'P' res:0
[ 2039.244300] platform pc8736x_gpio.0: get-portconf() idx=0 cmd='P' ret:0
0x0
soekris:/sys/devices/platform# echo 1 >
pc8736x_gpio.0/bit_0.1_pullup_enabled
[ 2760.304169] platform pc8736x_gpio.0: GPIO1 pullup enabled=1
[ 2760.310014] platform pc8736x_gpio.0: set-bitconf() idx:1 cmd:'P' buf:'1
[ 2760.310059] ' set:1
soekris:/sys/devices/platform#
soekris:/sys/devices/platform# cat pc8736x_gpio.0/port_0_pullup_enabled
[ 2780.140281] platform pc8736x_gpio.0: get-portconf bit:0.0 cfg:44
cmd'P' res:0
[ 2780.147761] platform pc8736x_gpio.0: get-portconf bit:0.1 cfg:46
cmd'P' res:2
[ 2780.155139] platform pc8736x_gpio.0: get-portconf bit:0.2 cfg:44
cmd'P' res:0
[ 2780.162585] platform pc8736x_gpio.0: get-portconf bit:0.3 cfg:44
cmd'P' res:0
[ 2780.169960] platform pc8736x_gpio.0: get-portconf bit:0.4 cfg:44
cmd'P' res:0
[ 2780.177379] platform pc8736x_gpio.0: get-portconf bit:0.5 cfg:44
cmd'P' res:0
[ 2780.184749] platform pc8736x_gpio.0: get-portconf bit:0.6 cfg:44
cmd'P' res:0
[ 2780.192158] platform pc8736x_gpio.0: get-portconf bit:0.7 cfg:44
cmd'P' res:0
[ 2780.199484] platform pc8736x_gpio.0: get-portconf() idx=0 cmd='P' ret:2
0x2
TIA for any comments, feedback.
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/Documentation/gpio-model.txt roll3-m2/Documentation/gpio-model.txt
--- ../linux-2.6.18-rc3-mm2-sk/Documentation/gpio-model.txt 1969-12-31 17:00:00.000000000 -0700
+++ roll3-m2/Documentation/gpio-model.txt 2006-08-08 16:47:12.000000000 -0600
@@ -0,0 +1,130 @@
+
+Sysfs GPIO Model
+
+GPIO Hardware Design Overview
+
+GPIO (General Purpose IO) Hardware provides digital IO on pins
+(physical connections to external circuitry). The pins can be read
+and written, and are configurable for adaptability to a variety of
+external circuit designs. The typical GPIO feature set looks like
+this:
+
+ input:
+ - input (read pin value)
+ - input (read pin driven-value)
+ - input debounce (hw noise conditioning)
+
+ output:
+ - data-out
+ - output-enable !tristate
+
+ config: (used less frequently)
+ - pullup-resistor
+ - output-enable
+ - open collector (can only pull to zero)
+ - open emmitor (can only pull to high)
+ - interrupt generation & control
+
+
+Hardware Ports Organization
+
+Many GPIO hardware units organize GPIO bits (aka pins) into ports (aka
+banks) with an implementation-dependent width. The pin values are
+written and read on a port-wide basis, ensuring that all pins in a
+port change *simultaneously*, ie not as result of separate bus cycles.
+This is essential for some applications. Port-wide access is
+typically just for data.
+
+Config features control the GPIO's electrical properties, and are
+rarely used (data-access dominates). These features are usually
+implemented per-bit, but with some variations.
+
+
+GPIO-Sysfs Features
+
+All GPIO device-attr-files have 3-part names:
+
+ <prefix>_<id>_<suffix>.
+
+ prefix 'bit_' or 'port_'
+ id 1.3 or 2 respectively
+ suffix feature-name (value, totem, etc)
+
+for example:
+
+soekris:/sys/devices/platform# ls pc8736x_gpio.0/bit_0.1_*
+pc8736x_gpio.0/bit_0.1_current pc8736x_gpio.0/bit_0.1_pullup_enabled
+pc8736x_gpio.0/bit_0.1_debounced pc8736x_gpio.0/bit_0.1_status
+pc8736x_gpio.0/bit_0.1_locked pc8736x_gpio.0/bit_0.1_totem
+pc8736x_gpio.0/bit_0.1_output_enabled pc8736x_gpio.0/bit_0.1_value
+
+
+Suffixes:
+
+The Suffix displays the feature-name. They are named for one of the
+states, rather than the property-name, since a state-name=X is
+self-explanatory.
+
+And, matching my hardware:
+ _output_enabled vs _tristate
+ _totem vs _pushpull
+
+The <suffix> should be user replaceable later, with a means to add
+logical inversion selectively.
+
+
+Bit vs Port features.
+
+Depending upon hardware, each GPIO feature is controllable via either
+a bit-wide or port-wide interface. The model allows driver to
+abstract this, and to synthesize port-wide features where needed.
+
+- driver can choose to expose features only as naturally supported.
+ User space can deal then recognize when port-wide control of
+ output-enable, interrupts, etc are supported by the hardware.
+
+- driver can synthesize port-features by banging the per-bit access.
+ This was done ad-hoc, got reasonable results.
+
+
+Port, Bit Reservation (a few thoughts)
+
+Assuming port-wide GPIOs (seeing a bias?), each hardware port is
+trivially reserved by masking in bits as they're taken.
+
+simplifing constraints:
+ - user-ports are always subset of single hw-port
+ - no hw-port spanning
+ - contiguous bit-blocks only (0x5,0x9 disallowed)
+
+
+Various Hardware Notes
+
+General rules
+
+- GPIOs generally power-up into safe states (tristate/input-only),
+ interrupts are off, etc.
+
+- GPIOs can have diminishing feature set across provided resources. A
+ designer rarely needs homogeneous features, often needs very few
+ pins which must generate interrupts.
+
+- when a pin doesnt support feature, it provides a 'ghost-bit', which
+ reads as disabled, and ignores writes/ enablements. This is not
+ universal however..
+
+PC87366
+
+- has 4 8-bit ports, with diminishing feature set.
+- pins in all 4 share common *basic-config* register defn
+- ports 2,3 lack interrupts
+-- *and* they lack register too.
+-- they should have given it a ghost register.
+
+SC-1100
+
+This GPIO-port also supports Interrupt-Enable & Status.
+
+Sadly, it doesnt expose port-wide output-enable, which means
+bus-logic (switching all N-lines to hiZ) is prohibitively slow.
+
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/drivers/char/nsc_gpio.c roll3-m2/drivers/char/nsc_gpio.c
--- ../linux-2.6.18-rc3-mm2-sk/drivers/char/nsc_gpio.c 2006-07-30 11:29:18.000000000 -0600
+++ roll3-m2/drivers/char/nsc_gpio.c 2006-08-08 16:54:01.000000000 -0600
@@ -7,36 +7,112 @@
Copyright (c) 2005 Jim Cromie <jim.cromie@gmail.com>
*/
+#define DRVNAME "nsc_gpio"
+
+#include <linux/device.h>
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/nsc_gpio.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
+#include <linux/nsc_gpio.h>
-#define NAME "nsc_gpio"
+static int noports = 0;
+module_param(noports, int, 0);
+MODULE_PARM_DESC(noports, "sysgpio 0:both 1:no-status 2:no-sysfs-flags");
+
+static int bitpos = 0;
+module_param(bitpos, int, 0);
+MODULE_PARM_DESC(bitpos, "1 to expose bit position in values");
-void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index)
+static char statbuf[256];
+static ssize_t nsc_gpio_status(struct nsc_gpio_ops *amp, unsigned index)
{
/* retrieve current config w/o changing it */
u32 config = amp->gpio_config(index, ~0, 0);
+
+ return sprintf (
+ statbuf, "i/o%02u:%d/%d 0x%04x %s %s %s %s %s %s %s",
+ index,
+ !!(amp->gpio_get(index)), /* boolean display */
+ amp->gpio_current(index), /* someday show [01z] */
+ config,
+ (config & 1) ? "OE" : "TS", /* output-enabled/tristate */
+ (config & 2) ? "PP" : "OD", /* push pull / open drain */
+ (config & 4) ? "PUE" : "PUD", /* pull up enabled/disabled */
+ (config & 8) ? "LOCKED" : "", /* locked / unlocked */
+ (config & 16) ? "LEVEL" : "EDGE",/* level/edge input */
+ (config & 32) ? "HI" : "LO", /* trigger on rise/fall edge */
+ (config & 64) ? "DEBOUNCE" : "" /* debounce */
+ );
+}
+
+void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index)
+{
+ nsc_gpio_status(amp,index);
+ dev_info(amp->dev, "%s\n", statbuf);
+}
+
+/* the pin-mode-change 'commands' of the legacy device-file-interface,
+ are reused in the sysfs-interface.
+*/
+static int command_write(struct nsc_gpio_ops *amp, char c, unsigned m)
+{
+ struct device *dev = amp->dev;
+ int err = 0;
- /* user requested via 'v' command, so its INFO */
- dev_info(amp->dev, "io%02u: 0x%04x %s %s %s %s %s %s %s\tio:%d/%d\n",
- index, config,
- (config & 1) ? "OE" : "TS", /* output-enabled/tristate */
- (config & 2) ? "PP" : "OD", /* push pull / open drain */
- (config & 4) ? "PUE" : "PUD", /* pull up enabled/disabled */
- (config & 8) ? "LOCKED" : "", /* locked / unlocked */
- (config & 16) ? "LEVEL" : "EDGE",/* level/edge input */
- (config & 32) ? "HI" : "LO", /* trigger on rise/fall edge */
- (config & 64) ? "DEBOUNCE" : "", /* debounce */
+ switch (c) {
- amp->gpio_get(index), amp->gpio_current(index));
+ /* cases are a mix of old command letters, and new PF_CMD_*
+ symbols, for Off & On actions respectively.
+ */
+ case '0':
+ amp->gpio_set(m, 0);
+ break;
+ case '1':
+ amp->gpio_set(m, 1);
+ break;
+ case PF_CMD_OUT_ENA:
+ dev_dbg(dev, "GPIO%d output enabled\n", m);
+ amp->gpio_config(m, ~1, 1);
+ break;
+ case 'o':
+ dev_dbg(dev, "GPIO%d output disabled\n", m);
+ amp->gpio_config(m, ~1, 0);
+ break;
+ case PF_CMD_TOTEM:
+ dev_dbg(dev, "GPIO%d output is push pull\n", m);
+ amp->gpio_config(m, ~2, 2);
+ break;
+ case 't':
+ dev_dbg(dev, "GPIO%d output is open drain\n", m);
+ amp->gpio_config(m, ~2, 0);
+ break;
+ case PF_CMD_PULLUP:
+ dev_dbg(dev, "GPIO%d pull up enabled\n", m);
+ amp->gpio_config(m, ~4, 4);
+ break;
+ case 'p':
+ dev_dbg(dev, "GPIO%d pull up disabled\n", m);
+ amp->gpio_config(m, ~4, 0);
+ break;
+ case 'v':
+ /* View Current pin settings */
+ amp->gpio_dump(amp, m);
+ break;
+ case '\n':
+ /* end of settings string, do nothing */
+ break;
+ default:
+ dev_err(dev, "io%2d bad setting: chr<0x%2x>\n",
+ m, (int)c);
+ err++;
+ }
+ return err;
}
ssize_t nsc_gpio_write(struct file *file, const char __user *data,
@@ -44,57 +120,14 @@ ssize_t nsc_gpio_write(struct file *file
{
unsigned m = iminor(file->f_dentry->d_inode);
struct nsc_gpio_ops *amp = file->private_data;
- struct device *dev = amp->dev;
- size_t i;
- int err = 0;
+ int i, err = 0;
for (i = 0; i < len; ++i) {
char c;
if (get_user(c, data + i))
return -EFAULT;
- switch (c) {
- case '0':
- amp->gpio_set(m, 0);
- break;
- case '1':
- amp->gpio_set(m, 1);
- break;
- case 'O':
- dev_dbg(dev, "GPIO%d output enabled\n", m);
- amp->gpio_config(m, ~1, 1);
- break;
- case 'o':
- dev_dbg(dev, "GPIO%d output disabled\n", m);
- amp->gpio_config(m, ~1, 0);
- break;
- case 'T':
- dev_dbg(dev, "GPIO%d output is push pull\n", m);
- amp->gpio_config(m, ~2, 2);
- break;
- case 't':
- dev_dbg(dev, "GPIO%d output is open drain\n", m);
- amp->gpio_config(m, ~2, 0);
- break;
- case 'P':
- dev_dbg(dev, "GPIO%d pull up enabled\n", m);
- amp->gpio_config(m, ~4, 4);
- break;
- case 'p':
- dev_dbg(dev, "GPIO%d pull up disabled\n", m);
- amp->gpio_config(m, ~4, 0);
- break;
- case 'v':
- /* View Current pin settings */
- amp->gpio_dump(amp, m);
- break;
- case '\n':
- /* end of settings string, do nothing */
- break;
- default:
- dev_err(dev, "io%2d bad setting: chr<0x%2x>\n",
- m, (int)c);
- err++;
- }
+
+ err += command_write(amp, c, m);
}
if (err)
return -EINVAL; /* full string handled, report error */
@@ -121,15 +154,414 @@ EXPORT_SYMBOL(nsc_gpio_write);
EXPORT_SYMBOL(nsc_gpio_read);
EXPORT_SYMBOL(nsc_gpio_dump);
+#define BITVAL(a) "%u\n", !!(a) /* force int to boolean */
+#define PORTVAL(a) "0x%x\n", (a) /* expose bitval pos in port */
+
+/* 8 sysfs-callbacks, partitioned on 3 axes:
+ get/set: as reqd by 2 different callback fn-signatures
+ value/: for port-wide
+ /feature: mostly use-once, slower, synthesized port-access
+ bit/port: for values, separate for simplicity, speed
+
+ This is arguably over-optimization, may clash with client-module
+ feature to bit/port mappings.
+*/
+ssize_t nsc_gpio_sysfs_get_bitval(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->bitnum;
+ int func = attr->fn_slct;
+ unsigned res = -1;
+
+ BUG_ON(!amp);
+
+ switch (func) {
+ case PF_CMD_VALUE:
+ res = amp->gpio_get(idx);
+ break;
+ case PF_CMD_CURR:
+ res = amp->gpio_current(idx);
+ break;
+ }
+ return bitpos ?
+ sprintf(buf, PORTVAL(res)) :
+ sprintf(buf, BITVAL(res));
+}
+ssize_t nsc_gpio_sysfs_get_portval(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->portnum;
+ int func = attr->fn_slct;
+ unsigned res = -1;
+
+ BUG_ON(!amp);
+ switch (func) {
+ case PF_CMD_VALUE:
+ res = amp->gpio_get_port(idx);
+ break;
+ case PF_CMD_CURR:
+ res = amp->gpio_current_port(idx);
+ break;
+ }
+ return sprintf(buf, PORTVAL(res));
+}
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_bitval);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_portval);
+
+ssize_t nsc_gpio_sysfs_set_bitval(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->bitnum;
+ long setting = simple_strtol(buf, NULL, 10);
+
+ /* CURR is VALUE for writes */
+ BUG_ON(!amp);
+ amp->gpio_set(idx, setting);
+ return count;
+}
+ssize_t nsc_gpio_sysfs_set_portval(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->portnum;
+ long setting = simple_strtol(buf, NULL, 10);
+
+ BUG_ON(!amp);
+ amp->gpio_setclr_port(idx, setting, ~setting);
+ return count;
+}
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_bitval);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_portval);
+
+ssize_t nsc_gpio_sysfs_get_bitconf(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->bitnum;
+ int func = attr->fn_slct;
+ u32 config, res = 0;
+
+ BUG_ON(!amp);
+ config = amp->gpio_config(idx, ~0, 0);
+
+ switch (func) {
+
+ case PF_CMD_CURR:
+ res = amp->gpio_current(idx);
+ return sprintf(buf, BITVAL(res));
+
+ case PF_CMD_STATUS:
+ nsc_gpio_status(amp, idx);
+ return sprintf(buf, "%s\n", statbuf);
+
+ case PF_CMD_OUT_ENA:
+ res = config & PF_MASK_OUT_ENA;
+ break;
+ case PF_CMD_TOTEM:
+ res = config & PF_MASK_TOTEM;
+ break;
+ case PF_CMD_PULLUP:
+ res = config & PF_MASK_PULLUP;
+ break;
+ case PF_CMD_LOCKED:
+ res = config & PF_MASK_LOCKED;
+ break;
+ case PF_CMD_DEBOUNCE:
+ res = config & PF_MASK_DEBOUNCE;
+ break;
+ case PF_CMD_INT_ENA:
+ res = config & PF_MASK_INT_ENA;
+ break;
+ case PF_CMD_INT_TRIG:
+ res = config & PF_MASK_INT_TRIG;
+ break;
+
+ default:
+ dev_err(dev, "unknown cmd '%c'\n", func);
+ }
+ dev_dbg(dev, "get-bitconf() idx:%d cmd:'%c' conf%02x res:%02x\n",
+ idx, func, config, res);
+
+ return sprintf(buf, BITVAL(res));
+}
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_bitconf);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_bitconf);
+
+
+#define replace(Mask,Val) ~Mask, Val ? Mask : 0
+
+ssize_t nsc_gpio_sysfs_set_bitconf(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->bitnum;
+ int func = attr->fn_slct;
+ long setting = simple_strtol(buf, NULL, 10);
+
+ BUG_ON(!amp);
+
+ switch (func) {
+
+ case PF_CMD_STATUS:
+ {
+ /* device-file write interface. Kludge, but small */
+ int i, err = 0;
+ dev_warn(dev, "try to set status to: %s\n", buf);
+ for (i = 0; buf[i]; i++)
+ err += command_write(amp, buf[i], idx);
+ if (err)
+ dev_warn(dev, "%d errs on cmd %s\n", err, buf);
+ break;
+ }
+ case PF_CMD_OUT_ENA:
+ dev_info(dev, "GPIO%d output enabled=%ld\n", idx, setting);
+ amp->gpio_config(idx, replace(1, setting));
+ // ~1, setting ? 1 : 0);
+ break;
+ case PF_CMD_PULLUP:
+ dev_info(dev, "GPIO%d pullup enabled=%ld\n", idx, setting);
+ amp->gpio_config(idx, ~2, setting ? 2 : 0);
+ break;
+ case PF_CMD_TOTEM:
+ dev_info(dev, "GPIO%d totem-pole enabled=%ld\n", idx, setting);
+ amp->gpio_config(idx, ~4, setting ? 4 : 0);
+ break;
+ case 'L':
+ dev_info(dev, "GPIO%d pin locked=%ld\n", idx, setting);
+ amp->gpio_config(idx, ~8, setting ? 8 : 0);
+ break;
+ case 'D':
+ dev_info(dev, "GPIO%d debounced=%ld\n", idx, setting);
+ amp->gpio_config(idx, ~PF_MASK_DEBOUNCE,
+ setting ? PF_MASK_DEBOUNCE : 0);
+ break;
+ default:
+ dev_err(dev, "sysfs unknown cmd '%c'\n", func);
+ }
+ dev_info(dev, "set-bitconf() idx:%d cmd:'%c' buf:'%s' set:%lx\n",
+ idx, func, buf, setting);
+
+ return strlen(buf);
+}
+
+#if 10
+ssize_t nsc_gpio_sysfs_get_portconf(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->portnum;
+ int func = attr->fn_slct;
+ u32 config, res = 0, ret = 0;
+ char *s = buf;
+ int bit, width = amp->port_size;
+
+ BUG_ON(!amp);
+ BUG_ON(!width);
+
+ for (bit = 0; bit < width; bit++) {
+
+ config = amp->gpio_config(bit + idx * width, ~0, 0);
+
+ switch ((char)func) {
+ case PF_CMD_CURR:
+ res = amp->gpio_current(bit);
+ sprintf(s, BITVAL(res));
+ case PF_CMD_STATUS:
+ nsc_gpio_status(amp, bit);
+ sprintf(s, "%s\n", statbuf);
+ case PF_CMD_OUT_ENA:
+ res = config & PF_MASK_OUT_ENA;
+ break;
+ case PF_CMD_PULLUP:
+ res = config & PF_MASK_PULLUP;
+ break;
+ case PF_CMD_TOTEM:
+ res = config & PF_MASK_TOTEM;
+ break;
+ case PF_CMD_LOCKED:
+ res = config & PF_MASK_LOCKED;
+ break;
+ case PF_CMD_DEBOUNCE:
+ res = config & PF_MASK_DEBOUNCE;
+ break;
+ case PF_CMD_INT_ENA:
+ res = config & PF_MASK_INT_ENA;
+ break;
+ case PF_CMD_INT_TRIG:
+ res = config & PF_MASK_INT_TRIG;
+ break;
+
+ default:
+ dev_err(dev, "unknown cmd '%c'\n", func);
+ res = 0;
+ }
+ res = res ? 1<<bit : 0;
+ ret |= res;
+
+ dev_dbg(dev, "get-portconf bit:%d.%d cfg:%x cmd'%c' res:%x\n",
+ idx, bit, config, func, res);
+ }
+ dev_dbg(dev, "get-portconf(%d) idx=%d cmd='%c' ret:%x\n",
+ width, idx, func, ret);
+
+ return sprintf(buf, PORTVAL(ret));
+}
+
+ssize_t nsc_gpio_sysfs_set_portconf(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->bitnum;
+ int func = attr->fn_slct;
+ int bit, width = amp->port_size;
+ long setting = simple_strtol(buf, NULL, 10);
+
+ BUG_ON(!amp);
+
+ for (bit = 0; bit < width; bit++) {
+
+ switch (func) {
+
+ case PF_CMD_STATUS:
+ {
+ /* device-file write interface */
+ int i, err = 0;
+ dev_warn(dev, "try to set status to: %s\n", buf);
+ for (i = 0; buf[i]; i++)
+ err += command_write(amp, buf[i], idx);
+ if (err)
+ dev_warn(dev, "%d errs on cmd %s\n", err, buf);
+ break;
+ }
+ case PF_CMD_OUT_ENA:
+ amp->gpio_config(idx, ~PF_MASK_OUT_ENA,
+ setting ? PF_MASK_OUT_ENA : 0);
+ break;
+ case PF_CMD_TOTEM:
+ amp->gpio_config(idx, ~PF_MASK_TOTEM,
+ setting ? PF_MASK_TOTEM : 0);
+ break;
+ case PF_CMD_PULLUP:
+ amp->gpio_config(idx, ~PF_MASK_PULLUP,
+ setting ? PF_MASK_PULLUP : 0);
+ break;
+ case PF_CMD_LOCKED:
+ amp->gpio_config(idx, ~PF_CMD_LOCKED,
+ setting ? PF_MASK_LOCKED : 0);
+ break;
+ case PF_CMD_DEBOUNCE:
+ amp->gpio_config(idx, ~PF_MASK_DEBOUNCE,
+ setting ? PF_MASK_DEBOUNCE : 0);
+ break;
+ default:
+ dev_err(dev, "sysfs unknown cmd '%c'\n", func);
+ }
+ }
+ dev_info(dev, "set-portconf() idx:%d cmd:'%c' buf:'%s' set:%lx\n",
+ idx, func, buf, setting);
+
+ return strlen(buf);
+}
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_portconf);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_portconf);
+#endif
+
+
+
+static u8 legacy = 1, feature_attrs = 1;
+static int dev_attr_create_errs;
+static void create_devattr_file(struct device* dev,
+ struct device_attribute *dev_attr)
+{
+ int err;
+
+ if (dev_attr->show || dev_attr->store) {
+
+ err = device_create_file(dev, dev_attr);
+ if (err)
+ dev_attr_create_errs++;
+ }
+ else
+ dev_dbg(dev, "skipping %s\n", dev_attr->attr.name);
+}
+
+/* next 2 functions (and previous kludge) could be replaced by pair of
+ calls to sysfs_create/remove_group, except that collecting
+ attributes into a group would be manual.
+*/
+
+void nsc_gpio_sysfs_bits_init(struct device* dev,
+ struct gpio_attributes pp[], int numdevs)
+{
+ int i;
+
+ for (i = 0; i < numdevs; i++) {
+
+ create_devattr_file(dev, &pp[i].value.dev_attr);
+ create_devattr_file(dev, &pp[i].curr.dev_attr);
+
+ /* provide legacy device-file emulation here */
+ if (legacy)
+ create_devattr_file(dev, &pp[i].status.dev_attr);
+
+ /* create separate attr-file per feature */
+ if (feature_attrs) {
+ create_devattr_file(dev, &pp[i].oe.dev_attr);
+ create_devattr_file(dev, &pp[i].pue.dev_attr);
+ create_devattr_file(dev, &pp[i].totem.dev_attr);
+ create_devattr_file(dev, &pp[i].locked.dev_attr);
+ create_devattr_file(dev, &pp[i].bounce.dev_attr);
+ create_devattr_file(dev, &pp[i].int_en.dev_attr);
+ create_devattr_file(dev, &pp[i].int_lvl.dev_attr);
+ }
+ }
+}
+
+void nsc_gpio_sysfs_bits_fini(struct device* dev,
+ struct gpio_attributes pp[], int numdevs)
+{
+ int i;
+ for (i = 0; i < numdevs; i++) {
+ device_remove_file(dev, &pp[i].value.dev_attr);
+ device_remove_file(dev, &pp[i].curr.dev_attr);
+ /* always remove, whether there or not */
+ device_remove_file(dev, &pp[i].status.dev_attr);
+ device_remove_file(dev, &pp[i].oe.dev_attr);
+ device_remove_file(dev, &pp[i].pue.dev_attr);
+ device_remove_file(dev, &pp[i].totem.dev_attr);
+ device_remove_file(dev, &pp[i].locked.dev_attr);
+ device_remove_file(dev, &pp[i].bounce.dev_attr);
+ }
+}
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_bits_init);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_bits_fini);
+
static int __init nsc_gpio_init(void)
{
- printk(KERN_DEBUG NAME " initializing\n");
+ printk(KERN_DEBUG DRVNAME " initializing\n");
return 0;
}
static void __exit nsc_gpio_cleanup(void)
{
- printk(KERN_DEBUG NAME " cleanup\n");
+ printk(KERN_DEBUG DRVNAME " cleanup\n");
}
module_init(nsc_gpio_init);
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/drivers/char/pc8736x_gpio.c roll3-m2/drivers/char/pc8736x_gpio.c
--- ../linux-2.6.18-rc3-mm2-sk/drivers/char/pc8736x_gpio.c 2006-08-06 10:18:40.000000000 -0600
+++ roll3-m2/drivers/char/pc8736x_gpio.c 2006-08-08 14:01:00.000000000 -0600
@@ -1,5 +1,4 @@
-/* linux/drivers/char/pc8736x_gpio.c
-
+/*
National Semiconductor PC8736x GPIO driver. Allows a user space
process to play with the GPIO pins.
@@ -9,6 +8,8 @@
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>,
*/
+#define DRVNAME "pc8736x_gpio"
+
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/errno.h>
@@ -22,11 +23,13 @@
#include <linux/platform_device.h>
#include <asm/uaccess.h>
-#define DEVNAME "pc8736x_gpio"
-
-MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
-MODULE_DESCRIPTION("NatSemi/Winbond PC-8736x GPIO Pin Driver");
-MODULE_LICENSE("GPL");
+static int nobits = 0;
+module_param(nobits, int, 0);
+MODULE_PARM_DESC(nobits, "nobits=1 to suppress sysfs bits interface");
+
+static int noports = 0;
+module_param(noports, int, 0);
+MODULE_PARM_DESC(noports, "noports=1 to supress sysfs ports interface");
static int major; /* default to dynamic major */
module_param(major, int, 0);
@@ -144,15 +147,29 @@ static u32 pc8736x_gpio_configure(unsign
SIO_GPIO_PIN_CONFIG);
}
-static int pc8736x_gpio_get(unsigned minor)
+/* vtable-API functions.
+ this is a gpio-port device, so bitvals are pulled from ports (in princple)
+*/
+static u32 pc8736x_gpio_get_port(unsigned port)
+{
+ u8 val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
+
+ dev_dbg(&pdev->dev, "_gpio_get(%d from %x) == val %d\n",
+ port, pc8736x_gpio_base + port_offset[port] + PORT_IN,
+ val);
+
+ return (u32)val;
+}
+
+static u32 pc8736x_gpio_get(unsigned minor)
{
- int port, bit, val;
+ int port, bit;
+ u32 val;
port = minor >> 3;
bit = minor & 7;
val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
- val >>= bit;
- val &= 1;
+ val &= 1 << bit;
dev_dbg(&pdev->dev, "_gpio_get(%d from %x bit %d) == val %d\n",
minor, pc8736x_gpio_base + port_offset[port] + PORT_IN, bit,
@@ -188,13 +205,21 @@ static void pc8736x_gpio_set(unsigned mi
pc8736x_gpio_shadow[port] = val;
}
-static int pc8736x_gpio_current(unsigned minor)
+static u32 pc8736x_gpio_current_port(unsigned minor)
{
int port, bit;
minor &= 0x1f;
port = minor >> 3;
bit = minor & 7;
- return ((pc8736x_gpio_shadow[port] >> bit) & 0x01);
+ return (u32)((pc8736x_gpio_shadow[port] >> bit) & 0x01);
+}
+static u32 pc8736x_gpio_current(unsigned minor)
+{
+ int port, bit;
+ minor &= 0x1f;
+ port = minor >> 3;
+ bit = minor & 7;
+ return (u32)((pc8736x_gpio_shadow[port] >> bit) & 0x01);
}
static void pc8736x_gpio_change(unsigned index)
@@ -202,6 +227,26 @@ static void pc8736x_gpio_change(unsigned
pc8736x_gpio_set(index, !pc8736x_gpio_current(index));
}
+static void pc8736x_gpio_setclr_port(unsigned port, u32 bits, u32 mask)
+{
+ u8 val;
+ u8 curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);
+
+ val = (curval & ~mask) | (bits & mask);
+
+ dev_dbg(&pdev->dev, "gpio_set(port:%d addr:%x cur:%x new:%d)\n",
+ port, pc8736x_gpio_base + port_offset[port] + PORT_OUT,
+ curval, val);
+
+ outb_p(val, pc8736x_gpio_base + port_offset[port] + PORT_OUT);
+
+ curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);
+ val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
+
+ dev_dbg(&pdev->dev, "wrote %x, read: %x\n", curval, val);
+ pc8736x_gpio_shadow[port] = val;
+}
+
static struct nsc_gpio_ops pc8736x_gpio_ops = {
.owner = THIS_MODULE,
.gpio_config = pc8736x_gpio_configure,
@@ -209,9 +254,14 @@ static struct nsc_gpio_ops pc8736x_gpio_
.gpio_get = pc8736x_gpio_get,
.gpio_set = pc8736x_gpio_set,
.gpio_change = pc8736x_gpio_change,
- .gpio_current = pc8736x_gpio_current
+ .gpio_current = pc8736x_gpio_current,
+ .port_size = 8,
+ .gpio_get_port = pc8736x_gpio_get_port,
+ .gpio_setclr_port = pc8736x_gpio_setclr_port,
+ .gpio_current_port = pc8736x_gpio_current_port,
};
+/* char-dev API */
static int pc8736x_gpio_open(struct inode *inode, struct file *file)
{
unsigned m = iminor(inode);
@@ -231,6 +281,61 @@ static const struct file_operations pc87
.read = nsc_gpio_read,
};
+/* sysfs-gpio API */
+
+static struct gpio_attributes port0[] = {
+ GPIO_ATTRS(0,0), GPIO_ATTRS(0,1), GPIO_ATTRS(0,2),
+ GPIO_ATTRS(0,3), GPIO_ATTRS(0,4), GPIO_ATTRS(0,5),
+ GPIO_ATTRS(0,6), GPIO_ATTRS(0,7)
+};
+
+static struct gpio_attributes port1[] = {
+ GPIO_ATTRS(1,0), GPIO_ATTRS(1,1), GPIO_ATTRS(1,2),
+ GPIO_ATTRS(1,3), GPIO_ATTRS(1,4), GPIO_ATTRS(1,5),
+ GPIO_ATTRS(1,6), GPIO_ATTRS(1,7)
+};
+static struct gpio_attributes port2[] = {
+ GPIO_ATTRS(2,0), GPIO_ATTRS(2,1), GPIO_ATTRS(2,2),
+ GPIO_ATTRS(2,3), GPIO_ATTRS(2,4), GPIO_ATTRS(2,5),
+ GPIO_ATTRS(2,6), GPIO_ATTRS(2,7)
+};
+static struct gpio_attributes port3[] = {
+ GPIO_ATTRS(3,0), GPIO_ATTRS(3,1), GPIO_ATTRS(3,2),
+ GPIO_ATTRS(3,3), GPIO_ATTRS(3,4), GPIO_ATTRS(3,5),
+ GPIO_ATTRS(3,6), GPIO_ATTRS(3,7)
+};
+
+static struct gpio_attributes ports[] = {
+ GPIO_PORT_ATTRS(0), GPIO_PORT_ATTRS(1),
+ GPIO_PORT_ATTRS(2), GPIO_PORT_ATTRS(3),
+};
+
+static void __init pc8736x_sysfs_init(struct device* dev)
+{
+ if (!nobits) {
+ dev_info(dev, "creating gpio-sysfs pin interfaces\n");
+ nsc_gpio_sysfs_bits_init(&pdev->dev, port0, 8);
+ nsc_gpio_sysfs_bits_init(&pdev->dev, port1, 8);
+ nsc_gpio_sysfs_bits_init(&pdev->dev, port2, 8);
+ nsc_gpio_sysfs_bits_init(&pdev->dev, port3, 8);
+ }
+ if (!noports) {
+ dev_info(dev, "creating gpio-sysfs port interfaces\n");
+ nsc_gpio_sysfs_bits_init(&pdev->dev, ports, 4);
+ }
+}
+static void pc8736x_sysfs_fini(struct device* dev)
+{
+ dev_info(dev, "pc8736x_sysfs_fini");
+ nsc_gpio_sysfs_bits_fini(&pdev->dev, port0, 8);
+ nsc_gpio_sysfs_bits_fini(&pdev->dev, port1, 8);
+ nsc_gpio_sysfs_bits_fini(&pdev->dev, port2, 8);
+ nsc_gpio_sysfs_bits_fini(&pdev->dev, port3, 8);
+
+ nsc_gpio_sysfs_bits_fini(&pdev->dev, ports, 4);
+}
+
+/* device init */
static void __init pc8736x_init_shadow(void)
{
int port;
@@ -250,7 +355,7 @@ static int __init pc8736x_gpio_init(void
int rc;
dev_t devid;
- pdev = platform_device_alloc(DEVNAME, 0);
+ pdev = platform_device_alloc(DRVNAME, 0);
if (!pdev)
return -ENOMEM;
@@ -288,7 +393,7 @@ static int __init pc8736x_gpio_init(void
pc8736x_gpio_base = (superio_inb(SIO_BASE_HADDR) << 8
| superio_inb(SIO_BASE_LADDR));
- if (!request_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE, DEVNAME)) {
+ if (!request_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE, DRVNAME)) {
rc = -ENODEV;
dev_err(&pdev->dev, "GPIO ioport %x busy\n",
pc8736x_gpio_base);
@@ -298,9 +403,9 @@ static int __init pc8736x_gpio_init(void
if (major) {
devid = MKDEV(major, 0);
- rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DEVNAME);
+ rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DRVNAME);
} else {
- rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DEVNAME);
+ rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DRVNAME);
major = MAJOR(devid);
}
@@ -319,6 +424,10 @@ static int __init pc8736x_gpio_init(void
cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops);
cdev_add(&pc8736x_gpio_cdev, devid, PC8736X_GPIO_CT);
+ /* provide info where sysfs callbacks can get them */
+ pc8736x_gpio_ops.dev->driver_data = &pc8736x_gpio_ops;
+ pc8736x_sysfs_init(&pdev->dev);
+
return 0;
undo_request_region:
@@ -335,6 +444,8 @@ static void __exit pc8736x_gpio_cleanup(
{
dev_dbg(&pdev->dev, "cleanup\n");
+ pc8736x_sysfs_fini(&pdev->dev);
+
cdev_del(&pc8736x_gpio_cdev);
unregister_chrdev_region(MKDEV(major,0), PC8736X_GPIO_CT);
release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);
@@ -345,3 +456,7 @@ static void __exit pc8736x_gpio_cleanup(
module_init(pc8736x_gpio_init);
module_exit(pc8736x_gpio_cleanup);
+
+MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
+MODULE_DESCRIPTION("NatSemi/Winbond PC-8736x GPIO Pin Driver");
+MODULE_LICENSE("GPL");
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/drivers/char/scx200_gpio.c roll3-m2/drivers/char/scx200_gpio.c
--- ../linux-2.6.18-rc3-mm2-sk/drivers/char/scx200_gpio.c 2006-08-06 10:18:42.000000000 -0600
+++ roll3-m2/drivers/char/scx200_gpio.c 2006-08-08 13:20:42.000000000 -0600
@@ -1,9 +1,11 @@
-/* linux/drivers/char/scx200_gpio.c
-
+/*
National Semiconductor SCx200 GPIO driver. Allows a user space
process to play with the GPIO pins.
- Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */
+ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+*/
+
+#define DRVNAME "scx200_gpio"
#include <linux/device.h>
#include <linux/fs.h>
@@ -21,10 +23,6 @@
#include <linux/scx200_gpio.h>
#include <linux/nsc_gpio.h>
-#define DRVNAME "scx200_gpio"
-
-static struct platform_device *pdev;
-
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi/AMD SCx200 GPIO Pin Driver");
MODULE_LICENSE("GPL");
@@ -33,6 +31,14 @@ static int major = 0; /* default to dyn
module_param(major, int, 0);
MODULE_PARM_DESC(major, "Major device number");
+static int nobits = 0;
+module_param(nobits, int, 0);
+MODULE_PARM_DESC(nobits, "nobits=1 to suppress sysfs bits interface");
+
+static int noports = 0;
+module_param(noports, int, 0);
+MODULE_PARM_DESC(noports, "noports=1 to supress sysfs ports interface");
+
#define MAX_PINS 32 /* 64 later, when known ok */
struct nsc_gpio_ops scx200_gpio_ops = {
@@ -42,10 +48,73 @@ struct nsc_gpio_ops scx200_gpio_ops = {
.gpio_get = scx200_gpio_get,
.gpio_set = scx200_gpio_set,
.gpio_change = scx200_gpio_change,
- .gpio_current = scx200_gpio_current
+ .gpio_current = scx200_gpio_current,
+ .port_size = 32,
+ .gpio_get_port = scx200_gpio_get_port,
+ .gpio_setclr_port = scx200_gpio_setclr_port,
+
+ /* add these back to exploit pxa-2xx, which has separate
+ set/clear addresses, avoiding read-modify-write cycles on
+ the pin. (maybe)
+
+ void (*gpio_set_lo) (unsigned iminor, int state);
+ void (*gpio_set_hi) (unsigned iminor, int state);
+ */
};
EXPORT_SYMBOL_GPL(scx200_gpio_ops);
+static struct gpio_attributes port0[] = {
+ GPIO_ATTRS(0,0), GPIO_ATTRS(0,1), GPIO_ATTRS(0,2),
+ GPIO_ATTRS(0,3), GPIO_ATTRS(0,4), GPIO_ATTRS(0,5),
+ GPIO_ATTRS(0,6), GPIO_ATTRS(0,7), GPIO_ATTRS(0,8),
+ GPIO_ATTRS(0,9), GPIO_ATTRS(0,10), GPIO_ATTRS(0,11),
+ GPIO_ATTRS(0,12), GPIO_ATTRS(0,13), GPIO_ATTRS(0,14),
+ GPIO_ATTRS(0,15), GPIO_ATTRS(0,16), GPIO_ATTRS(0,17),
+ GPIO_ATTRS(0,18), GPIO_ATTRS(0,19), GPIO_ATTRS(0,20),
+ GPIO_ATTRS(0,21), GPIO_ATTRS(0,22), GPIO_ATTRS(0,23),
+ GPIO_ATTRS(0,24), GPIO_ATTRS(0,25), GPIO_ATTRS(0,26),
+ GPIO_ATTRS(0,27), GPIO_ATTRS(0,28), GPIO_ATTRS(0,29),
+ GPIO_ATTRS(0,30), GPIO_ATTRS(0,31),
+};
+
+static struct gpio_attributes port1[] = {
+ GPIO_ATTRS(1,0), GPIO_ATTRS(1,1), GPIO_ATTRS(1,2),
+ GPIO_ATTRS(1,3), GPIO_ATTRS(1,4), GPIO_ATTRS(1,5),
+ GPIO_ATTRS(1,6), GPIO_ATTRS(1,7), GPIO_ATTRS(1,8),
+ GPIO_ATTRS(1,9), GPIO_ATTRS(1,10), GPIO_ATTRS(1,11),
+ GPIO_ATTRS(1,12), GPIO_ATTRS(1,13), GPIO_ATTRS(1,14),
+ GPIO_ATTRS(1,15), GPIO_ATTRS(1,16), GPIO_ATTRS(1,17),
+ GPIO_ATTRS(1,18), GPIO_ATTRS(1,19), GPIO_ATTRS(1,20),
+ GPIO_ATTRS(1,21), GPIO_ATTRS(1,22), GPIO_ATTRS(1,23),
+ GPIO_ATTRS(1,24), GPIO_ATTRS(1,25), GPIO_ATTRS(1,26),
+ GPIO_ATTRS(1,27), GPIO_ATTRS(1,28), GPIO_ATTRS(1,29),
+ GPIO_ATTRS(1,30), GPIO_ATTRS(1,31),
+};
+static struct gpio_attributes ports[] = {
+ GPIO_PORT_ATTRS(0), GPIO_PORT_ATTRS(1),
+};
+
+static void __init scx200_sysfs_init(struct device* dev)
+{
+ if (!nobits) {
+ nsc_gpio_sysfs_bits_init(dev, port0, ARRAY_SIZE(port0));
+ nsc_gpio_sysfs_bits_init(dev, port1, ARRAY_SIZE(port1));
+ }
+ if (!noports)
+ nsc_gpio_sysfs_bits_init(dev, ports, ARRAY_SIZE(ports));
+}
+static void scx200_sysfs_fini(struct device* dev)
+{
+ dev_info(dev, "scx200_sysfs_fini");
+ if (!nobits) {
+ nsc_gpio_sysfs_bits_fini(dev, port0, ARRAY_SIZE(port0));
+ nsc_gpio_sysfs_bits_fini(dev, port1, ARRAY_SIZE(port1));
+ }
+ if (!noports)
+ nsc_gpio_sysfs_bits_fini(dev, ports, ARRAY_SIZE(ports));
+}
+
+/* file API */
static int scx200_gpio_open(struct inode *inode, struct file *file)
{
unsigned m = iminor(inode);
@@ -69,7 +138,8 @@ static const struct file_operations scx2
.release = scx200_gpio_release,
};
-static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
+static struct platform_device *pdev; /* for dev_info(&pdev->dev, */
+static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
static int __init scx200_gpio_init(void)
{
@@ -92,6 +162,7 @@ static int __init scx200_gpio_init(void)
/* nsc_gpio uses dev_dbg(), so needs this */
scx200_gpio_ops.dev = &pdev->dev;
+ scx200_gpio_ops.dev->driver_data = &scx200_gpio_ops;
if (major) {
devid = MKDEV(major, 0);
@@ -108,6 +179,8 @@ static int __init scx200_gpio_init(void)
cdev_init(&scx200_gpio_cdev, &scx200_gpio_fileops);
cdev_add(&scx200_gpio_cdev, devid, MAX_PINS);
+ scx200_sysfs_init(&pdev->dev);
+
return 0; /* succeed */
undo_platform_device_add:
@@ -120,6 +193,7 @@ undo_malloc:
static void __exit scx200_gpio_cleanup(void)
{
+ scx200_sysfs_fini(&pdev->dev);
cdev_del(&scx200_gpio_cdev);
/* cdev_put(&scx200_gpio_cdev); */
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/include/linux/nsc_gpio.h roll3-m2/include/linux/nsc_gpio.h
--- ../linux-2.6.18-rc3-mm2-sk/include/linux/nsc_gpio.h 2006-07-30 11:31:26.000000000 -0600
+++ roll3-m2/include/linux/nsc_gpio.h 2006-08-08 16:55:35.000000000 -0600
@@ -1,19 +1,16 @@
/**
- nsc_gpio.c
-
National Semiconductor GPIO common access methods.
- struct nsc_gpio_ops abstracts the low-level access
- operations for the GPIO units on 2 NSC chip families; the GEODE
- integrated CPU, and the PC-8736[03456] integrated PC-peripheral
- chips.
+ struct nsc_gpio_ops abstracts the low-level access operations for
+ the GPIO units on 2 NSC chip families; the GEODE integrated CPU
+ SC-1100, and the PC-8736[03456] integrated PC-peripheral chips.
The GPIO units on these chips have the same pin architecture, but
the access methods differ. Thus, scx200_gpio and pc8736x_gpio
implement their own versions of these routines; and use the common
file-operations routines implemented in nsc_gpio module.
- Copyright (c) 2005 Jim Cromie <jim.cromie@gmail.com>
+ Copyright (c) 2005,2006 Jim Cromie <jim.cromie@gmail.com>
NB: this work was tested on the Geode SC-1100 and PC-87366 chips.
NSC sold the GEODE line to AMD, and the PC-8736x line to Winbond.
@@ -21,15 +18,39 @@
struct nsc_gpio_ops {
struct module* owner;
- u32 (*gpio_config) (unsigned iminor, u32 mask, u32 bits);
+ struct device* dev; /* for dev_dbg() support, set in init */
+ u8 port_size; /* 8 or 32 so far. 32 max */
+
+ /* config ctrl & human desc */
+ u32 (*gpio_config) (unsigned iminor, u32 bits, u32 clr);
void (*gpio_dump) (struct nsc_gpio_ops *amp, unsigned iminor);
- int (*gpio_get) (unsigned iminor);
+
+ /* bit-wide value interface */
+ u32 (*gpio_get) (unsigned iminor);
void (*gpio_set) (unsigned iminor, int state);
void (*gpio_change) (unsigned iminor);
- int (*gpio_current) (unsigned iminor);
- struct device* dev; /* for dev_dbg() support, set in init */
+ u32 (*gpio_current) (unsigned iminor);
+
+ /* want to restore set-hi()/set-lo() for PXA, which has
+ separate set and clear registers/insns, allowing PXA to
+ avoid read-modify-write cycles (IIUC). Im missing
+ something though, since RMW cycles dont pertain to single
+ bits, but rather to ports (where an RMW is needed to
+ preserve un-changed bits) void (*gpio_set_hi) (unsigned
+ iminor); void (*gpio_set_lo) (unsigned iminor);
+ */
+
+ /* port-wide accessors (thanks Chris). For hardware which can
+ exploit it, gpio_setclr_port() separates set and clear
+ params to avoid Read-Modify-Write cycles.
+ */
+ u32 (*gpio_get_port) (unsigned portnum);
+ void (*gpio_set_port) (unsigned portnum, u32 bits);
+ void (*gpio_setclr_port) (unsigned portnum, u32 bits, u32 clr);
+ u32 (*gpio_current_port) (unsigned iminor);
};
+/* fileops user-API */
extern ssize_t nsc_gpio_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos);
@@ -38,3 +59,226 @@ extern ssize_t nsc_gpio_read(struct file
extern void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index);
+
+/* GPIO-sysfs model */
+#include <linux/device.h>
+#include <linux/sysfs.h>
+
+/* basic gpio-sysfs object, with 3-D addressing. This allows the use
+ of combined-callbacks which decode the 3-D parameters, and perform
+ the selected operation. (We only use 2 axes, but keeping all 3 has
+ almost no cost, and gives more info and flexibility
+*/
+struct gpio_dev_attr {
+ struct device_attribute dev_attr;
+ u8 bitnum; /* bit index */
+ u8 portnum; /* port index */
+ u8 fn_slct; /* feature selector */
+ u8 bit_port_type; /* GPIO_BIT, GPIO_PORT */
+};
+#define GPIO_BIT_TYPE 0
+#define GPIO_PORT_TYPE 1
+
+#define to_gpio_dev_attr(d) container_of(d, struct gpio_dev_attr, dev_attr)
+
+/* constants for 3 aspects of Pin Features used to initialize the
+ dev_attrs needed for the gpio-object's attr-files
+
+ PF_SFX - pin-feature dev-attr filename suffix. Might be nice to
+ make these driver-configurable (L8r)
+
+ PF_CMD - commands: constants are borrowed from fileops write
+ handler. The sysfs callbacks see them in the fn_slct field, so the
+ *_config handlers switch(fn_slct) to fetch the right bits.
+
+ PF_MASK - pin-feature mask, ie config bits understood by the 2
+ client drivers of this module (scx200_gpio, pc8736x_gpio). This is
+ too coupled to the features exposed by nsc_gpio_ops->gpio_config(),
+ but its a simple re-do of the fileops write-handler.
+
+ The cmd-set has expanded to cover all pin-features, not just
+ 'config' features, notably 'V' for values and 'C' for
+ current-values.
+*/
+#define PF_SFX_OUT_ENA _output_enabled
+#define PF_SFX_TOTEM _totem
+#define PF_SFX_PULLUP _pullup_enabled
+#define PF_SFX_LOCKED _locked
+#define PF_SFX_DEBOUNCE _debounced
+#define PF_SFX_INT_ENA _int_enabled /* too exposing !?! */
+#define PF_SFX_INT_TRIG _int_level_trig
+
+#define PF_CMD_OUT_ENA 'O' /* 'o' tristate */
+#define PF_CMD_TOTEM 'T' /* 't' open-drain */
+#define PF_CMD_PULLUP 'P' /* 'p' disables pullup */
+#define PF_CMD_LOCKED 'L' /* no unlock */
+/* these are newer than 18-rcX */
+#define PF_CMD_DEBOUNCE 'D' /* 'd' disables */
+#define PF_CMD_INT_ENA 'I' /* too exposing !?! */
+#define PF_CMD_INT_TRIG 'E' /* 'e' level-triggered */
+
+#define PF_MASK_OUT_ENA 1 /* !tristate */
+#define PF_MASK_TOTEM 2 /* !open-drain */
+#define PF_MASK_PULLUP 4 /* !open-drain */
+#define PF_MASK_LOCKED 8 /* no unlock possible */
+#define PF_MASK_INT_ENA 16 /* some pins cant do this */
+#define PF_MASK_INT_TRIG 32 /* level !edge */
+#define PF_MASK_DEBOUNCE 64
+
+/* constants for expanded feature-set */
+#define PF_SFX_VALUE _value /* input on pin */
+#define PF_SFX_CURR _current /* last written value */
+#define PF_SFX_STATUS _status /* _config ?? */
+
+#define PF_CMD_VALUE 'V' /* values-in often port-wide */
+#define PF_CMD_CURR 'C' /* current values-out */
+#define PF_CMD_STATUS 'S' /* possible chardev emulation */
+
+#define PF_MASK_VALUE 0 /* not for gpio_config() */
+#define PF_MASK_CURR 0 /* use <0 if differences needed */
+#define PF_MASK_STATUS 0
+
+
+struct gpio_attributes {
+ struct gpio_dev_attr value; /* hw often is port-wide here */
+ struct gpio_dev_attr curr; /* driven value, may be != value */
+
+ /* pin-features, which are mostly use-once, so hw often
+ provides it per-pin only. */
+ struct gpio_dev_attr oe; /* output-enable, !tristate */
+ struct gpio_dev_attr totem; /* !open-drain */
+ struct gpio_dev_attr pue; /* pullup-enabled */
+ struct gpio_dev_attr bounce; /* debounce circuit active */
+ struct gpio_dev_attr locked; /* once locked, no unlock */
+ struct gpio_dev_attr status; /* device-file compat-hack */
+
+ struct gpio_dev_attr int_en; /* interrupt enable */
+ struct gpio_dev_attr int_lvl; /* int on level !edge */
+};
+
+/* GPIO_ATTRS and GPIO_PORT_ATTRS macros let driver declare the
+ interfaces for the underlying hardware:
+
+ static struct gpio_attributes port_0[] = {
+ GPIO_ATTRS(0,0), GPIO_ATTRS(0,1), ... GPIO_ATTRS(0,31) };
+
+ static struct gpio_attributes port_set[] = {
+ GPIO_PORT_ATTRS(0,32), GPIO_PORT_ATTRS(1,32) };
+*/
+
+#define GPIO_PIN(Port, Bit, Suffix, Mode, Show, Store, Cmd) \
+ { .dev_attr = __ATTR( bit_ ## Port.Bit ## Suffix, \
+ Mode, Show, Store), \
+ .bitnum = Bit, \
+ .portnum = Port, \
+ .fn_slct = Cmd, \
+ .bit_port_type = GPIO_BIT_TYPE }
+
+#define SYSFS_CB_RD(Access) nsc_gpio_sysfs_get_ ## Access
+#define SYSFS_CB_WR(Access) nsc_gpio_sysfs_set_ ## Access
+#define SYSFS_CB_NULL (void*)0
+
+#define GPIO_ATTR(Portnum, Bitnum, Feat, Suffix, Access) \
+ GPIO_PIN(Portnum, Bitnum, Suffix, \
+ S_IWUSR | S_IRUGO, \
+ SYSFS_CB_RD(Access), SYSFS_CB_WR(Access), Feat)
+
+/* WARNING: this macro hardwires one of 2 sysfs-callbacks (port-wide
+ vs bit-wide access) to each attr. This is broken, since each GPIO
+ driver (ie author) must determine whether each feature is exposed
+ per-pin or port-wide. ATM, they can only copy and modify this
+ macro. But then, theyre already wed to the nsc_gpio_ops ;-)
+*/
+#define GPIO_ATTRS(Port, Idx) { \
+ .value = GPIO_ATTR(Port, Idx, PF_CMD_VALUE, PF_SFX_VALUE, bitval), \
+ .curr = GPIO_ATTR(Port, Idx, PF_CMD_CURR, PF_SFX_CURR, bitval), \
+ .oe = GPIO_ATTR(Port, Idx, PF_CMD_OUT_ENA, PF_SFX_OUT_ENA, bitconf), \
+ .pue = GPIO_ATTR(Port, Idx, PF_CMD_PULLUP, PF_SFX_PULLUP, bitconf), \
+ .totem = GPIO_ATTR(Port, Idx, PF_CMD_TOTEM, PF_SFX_TOTEM, bitconf), \
+ .locked = GPIO_ATTR(Port, Idx, PF_CMD_LOCKED, PF_SFX_LOCKED, bitconf), \
+ .bounce = GPIO_ATTR(Port, Idx, PF_CMD_DEBOUNCE, PF_SFX_DEBOUNCE,bitconf), \
+ .status = GPIO_ATTR(Port, Idx, PF_CMD_STATUS, PF_SFX_STATUS, bitconf), \
+ .int_en = GPIO_ATTR(Port, Idx, PF_CMD_INT_ENA, PF_SFX_INT_ENA, bitconf), \
+ .int_lvl= GPIO_ATTR(Port, Idx, PF_CMD_INT_TRIG, PF_SFX_INT_TRIG,bitconf) \
+}
+
+/* port-wide sysfs access */
+
+#define GPIO_PORT(Port, Sfx, Mode, Show, Store, Cmd) \
+ { .dev_attr = __ATTR( port_ ## Port ## Sfx, \
+ Mode, Show, Store), \
+ .portnum = Port, \
+ .bitnum = -1, \
+ .fn_slct = Cmd, \
+ .bit_port_type = GPIO_PORT_TYPE }
+
+#define GPIO_PORT_ATTR(Portnum, Cmd, Suffix, Access) \
+ GPIO_PORT( Portnum, Suffix, \
+ S_IWUSR | S_IRUGO, \
+ SYSFS_CB_RD(Access), SYSFS_CB_WR(Access), Cmd)
+
+/* WARNING: same caveats as above apply, only moreso */
+
+#define GPIO_PORT_ATTRS(Port) { \
+ .value = GPIO_PORT_ATTR(Port, PF_CMD_VALUE, PF_SFX_VALUE, portval), \
+ .curr = GPIO_PORT_ATTR(Port, PF_CMD_CURR, PF_SFX_CURR, portval), \
+ .oe = GPIO_PORT_ATTR(Port, PF_CMD_OUT_ENA, PF_SFX_OUT_ENA, portconf), \
+ .pue = GPIO_PORT_ATTR(Port, PF_CMD_PULLUP, PF_SFX_PULLUP, portconf), \
+ .totem = GPIO_PORT_ATTR(Port, PF_CMD_TOTEM, PF_SFX_TOTEM, portconf), \
+ .locked = GPIO_PORT_ATTR(Port, PF_CMD_LOCKED, PF_SFX_LOCKED, portconf), \
+ .bounce = GPIO_PORT_ATTR(Port, PF_CMD_DEBOUNCE, PF_SFX_DEBOUNCE,portconf), \
+ .status = GPIO_PORT_ATTR(Port, PF_CMD_STATUS, PF_SFX_STATUS, portconf), \
+ .int_en = GPIO_PORT_ATTR(Port, PF_CMD_INT_ENA, PF_SFX_INT_ENA, portconf), \
+ .int_lvl= GPIO_PORT_ATTR(Port, PF_CMD_INT_TRIG, PF_SFX_INT_TRIG,portconf) \
+}
+
+/* attr-file callbacks, named per glob-name:
+ nsc_gpio_sysfs_{get,set}_{bit,port}{val,conf}
+
+ -bitval,portval separate for speed, simplicity
+ -bitconf is combo-callback, handling slower, per-pin properties
+ -portconf - synthesize port-wide from bit-wide
+ -get,set as required by fn-prototypes
+*/
+extern ssize_t nsc_gpio_sysfs_get_bitval(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+extern ssize_t nsc_gpio_sysfs_set_bitval(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count);
+
+extern ssize_t nsc_gpio_sysfs_get_portval(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+extern ssize_t nsc_gpio_sysfs_set_portval(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count);
+
+
+extern ssize_t nsc_gpio_sysfs_get_bitconf(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+extern ssize_t nsc_gpio_sysfs_set_bitconf(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count);
+
+extern ssize_t nsc_gpio_sysfs_get_portconf(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+extern ssize_t nsc_gpio_sysfs_set_portconf(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count);
+
+
+/* device-create-file for all attrs, all bits in port.
+ called by higher level client _inits unless nobits=1.
+ Also called for ports-init !!
+*/
+extern void nsc_gpio_sysfs_bits_init(struct device* dev,
+ struct gpio_attributes pp[],
+ int numdevs);
+
+extern void nsc_gpio_sysfs_bits_fini(struct device* dev,
+ struct gpio_attributes pp[],
+ int numdevs);
+
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/include/linux/scx200_gpio.h roll3-m2/include/linux/scx200_gpio.h
--- ../linux-2.6.18-rc3-mm2-sk/include/linux/scx200_gpio.h 2006-08-06 10:19:38.000000000 -0600
+++ roll3-m2/include/linux/scx200_gpio.h 2006-08-08 12:51:09.000000000 -0600
@@ -18,7 +18,7 @@ extern struct nsc_gpio_ops scx200_gpio_o
/* returns the value of the GPIO pin */
-static inline int scx200_gpio_get(unsigned index) {
+static inline u32 scx200_gpio_get(unsigned index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR + 0x04;
__SCx200_GPIO_INDEX;
@@ -30,7 +30,7 @@ static inline int scx200_gpio_get(unsign
driven if the GPIO is configured as an output, it might not be the
state of the GPIO right now if the GPIO is configured as an input) */
-static inline int scx200_gpio_current(unsigned index) {
+static inline u32 scx200_gpio_current(unsigned index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_INDEX;
@@ -83,6 +83,30 @@ static inline void scx200_gpio_change(un
__SCx200_GPIO_OUT;
}
+/* return the value of a whole port (bank) */
+static inline u32 scx200_gpio_get_port(unsigned port)
+{
+ unsigned bank = port & 0x1f;
+ __SCx200_GPIO_IOADDR + 0x04;
+
+ return inl(ioaddr);
+}
+
+/* return the value of a whole port (bank) */
+static inline void scx200_gpio_setclr_port(unsigned port, u32 bits, u32 mask)
+{
+ unsigned bank = port & 0x1f;
+ __SCx200_GPIO_IOADDR;
+ __SCx200_GPIO_SHADOW;
+ *shadow = (*shadow & ~mask) | (bits & mask);
+ __SCx200_GPIO_OUT;
+}
+
+static inline void scx200_gpio_set_port(unsigned port, u32 bits)
+{
+ scx200_gpio_setclr_port(port, bits, ~bits);
+}
+
#undef __SCx200_GPIO_BANK
#undef __SCx200_GPIO_IOADDR
#undef __SCx200_GPIO_SHADOW
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [RFC - patch] add a gpio-sysfs interface - was: Proposal: common kernel-wide GPIO interface
2006-08-08 23:01 ` [RFC - patch] add a gpio-sysfs interface - was: " Jim Cromie
@ 2006-08-09 17:12 ` Jim Cromie
0 siblings, 0 replies; 17+ messages in thread
From: Jim Cromie @ 2006-08-09 17:12 UTC (permalink / raw)
To: Chris Boot; +Cc: kernel list
[-- Attachment #1: Type: text/plain, Size: 209 bytes --]
hey Chris,
I saw what looked like a failure to <preformat> b4 pasting,
here it is as attachment, and with exersize script.
hope youve got the time to try it out, and perhaps chat about where its
heading..
[-- Attachment #2: diff.roll3-m2-good-1 --]
[-- Type: text/plain, Size: 48500 bytes --]
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/Documentation/gpio-model.txt roll3-m2/Documentation/gpio-model.txt
--- ../linux-2.6.18-rc3-mm2-sk/Documentation/gpio-model.txt 1969-12-31 17:00:00.000000000 -0700
+++ roll3-m2/Documentation/gpio-model.txt 2006-08-08 16:47:12.000000000 -0600
@@ -0,0 +1,130 @@
+
+Sysfs GPIO Model
+
+GPIO Hardware Design Overview
+
+GPIO (General Purpose IO) Hardware provides digital IO on pins
+(physical connections to external circuitry). The pins can be read
+and written, and are configurable for adaptability to a variety of
+external circuit designs. The typical GPIO feature set looks like
+this:
+
+ input:
+ - input (read pin value)
+ - input (read pin driven-value)
+ - input debounce (hw noise conditioning)
+
+ output:
+ - data-out
+ - output-enable !tristate
+
+ config: (used less frequently)
+ - pullup-resistor
+ - output-enable
+ - open collector (can only pull to zero)
+ - open emmitor (can only pull to high)
+ - interrupt generation & control
+
+
+Hardware Ports Organization
+
+Many GPIO hardware units organize GPIO bits (aka pins) into ports (aka
+banks) with an implementation-dependent width. The pin values are
+written and read on a port-wide basis, ensuring that all pins in a
+port change *simultaneously*, ie not as result of separate bus cycles.
+This is essential for some applications. Port-wide access is
+typically just for data.
+
+Config features control the GPIO's electrical properties, and are
+rarely used (data-access dominates). These features are usually
+implemented per-bit, but with some variations.
+
+
+GPIO-Sysfs Features
+
+All GPIO device-attr-files have 3-part names:
+
+ <prefix>_<id>_<suffix>.
+
+ prefix 'bit_' or 'port_'
+ id 1.3 or 2 respectively
+ suffix feature-name (value, totem, etc)
+
+for example:
+
+soekris:/sys/devices/platform# ls pc8736x_gpio.0/bit_0.1_*
+pc8736x_gpio.0/bit_0.1_current pc8736x_gpio.0/bit_0.1_pullup_enabled
+pc8736x_gpio.0/bit_0.1_debounced pc8736x_gpio.0/bit_0.1_status
+pc8736x_gpio.0/bit_0.1_locked pc8736x_gpio.0/bit_0.1_totem
+pc8736x_gpio.0/bit_0.1_output_enabled pc8736x_gpio.0/bit_0.1_value
+
+
+Suffixes:
+
+The Suffix displays the feature-name. They are named for one of the
+states, rather than the property-name, since a state-name=X is
+self-explanatory.
+
+And, matching my hardware:
+ _output_enabled vs _tristate
+ _totem vs _pushpull
+
+The <suffix> should be user replaceable later, with a means to add
+logical inversion selectively.
+
+
+Bit vs Port features.
+
+Depending upon hardware, each GPIO feature is controllable via either
+a bit-wide or port-wide interface. The model allows driver to
+abstract this, and to synthesize port-wide features where needed.
+
+- driver can choose to expose features only as naturally supported.
+ User space can deal then recognize when port-wide control of
+ output-enable, interrupts, etc are supported by the hardware.
+
+- driver can synthesize port-features by banging the per-bit access.
+ This was done ad-hoc, got reasonable results.
+
+
+Port, Bit Reservation (a few thoughts)
+
+Assuming port-wide GPIOs (seeing a bias?), each hardware port is
+trivially reserved by masking in bits as they're taken.
+
+simplifing constraints:
+ - user-ports are always subset of single hw-port
+ - no hw-port spanning
+ - contiguous bit-blocks only (0x5,0x9 disallowed)
+
+
+Various Hardware Notes
+
+General rules
+
+- GPIOs generally power-up into safe states (tristate/input-only),
+ interrupts are off, etc.
+
+- GPIOs can have diminishing feature set across provided resources. A
+ designer rarely needs homogeneous features, often needs very few
+ pins which must generate interrupts.
+
+- when a pin doesnt support feature, it provides a 'ghost-bit', which
+ reads as disabled, and ignores writes/ enablements. This is not
+ universal however..
+
+PC87366
+
+- has 4 8-bit ports, with diminishing feature set.
+- pins in all 4 share common *basic-config* register defn
+- ports 2,3 lack interrupts
+-- *and* they lack register too.
+-- they should have given it a ghost register.
+
+SC-1100
+
+This GPIO-port also supports Interrupt-Enable & Status.
+
+Sadly, it doesnt expose port-wide output-enable, which means
+bus-logic (switching all N-lines to hiZ) is prohibitively slow.
+
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/drivers/char/nsc_gpio.c roll3-m2/drivers/char/nsc_gpio.c
--- ../linux-2.6.18-rc3-mm2-sk/drivers/char/nsc_gpio.c 2006-07-30 11:29:18.000000000 -0600
+++ roll3-m2/drivers/char/nsc_gpio.c 2006-08-08 16:54:01.000000000 -0600
@@ -7,36 +7,112 @@
Copyright (c) 2005 Jim Cromie <jim.cromie@gmail.com>
*/
+#define DRVNAME "nsc_gpio"
+
+#include <linux/device.h>
#include <linux/config.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/nsc_gpio.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
+#include <linux/nsc_gpio.h>
-#define NAME "nsc_gpio"
+static int noports = 0;
+module_param(noports, int, 0);
+MODULE_PARM_DESC(noports, "sysgpio 0:both 1:no-status 2:no-sysfs-flags");
+
+static int bitpos = 0;
+module_param(bitpos, int, 0);
+MODULE_PARM_DESC(bitpos, "1 to expose bit position in values");
-void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index)
+static char statbuf[256];
+static ssize_t nsc_gpio_status(struct nsc_gpio_ops *amp, unsigned index)
{
/* retrieve current config w/o changing it */
u32 config = amp->gpio_config(index, ~0, 0);
+
+ return sprintf (
+ statbuf, "i/o%02u:%d/%d 0x%04x %s %s %s %s %s %s %s",
+ index,
+ !!(amp->gpio_get(index)), /* boolean display */
+ amp->gpio_current(index), /* someday show [01z] */
+ config,
+ (config & 1) ? "OE" : "TS", /* output-enabled/tristate */
+ (config & 2) ? "PP" : "OD", /* push pull / open drain */
+ (config & 4) ? "PUE" : "PUD", /* pull up enabled/disabled */
+ (config & 8) ? "LOCKED" : "", /* locked / unlocked */
+ (config & 16) ? "LEVEL" : "EDGE",/* level/edge input */
+ (config & 32) ? "HI" : "LO", /* trigger on rise/fall edge */
+ (config & 64) ? "DEBOUNCE" : "" /* debounce */
+ );
+}
+
+void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index)
+{
+ nsc_gpio_status(amp,index);
+ dev_info(amp->dev, "%s\n", statbuf);
+}
+
+/* the pin-mode-change 'commands' of the legacy device-file-interface,
+ are reused in the sysfs-interface.
+*/
+static int command_write(struct nsc_gpio_ops *amp, char c, unsigned m)
+{
+ struct device *dev = amp->dev;
+ int err = 0;
- /* user requested via 'v' command, so its INFO */
- dev_info(amp->dev, "io%02u: 0x%04x %s %s %s %s %s %s %s\tio:%d/%d\n",
- index, config,
- (config & 1) ? "OE" : "TS", /* output-enabled/tristate */
- (config & 2) ? "PP" : "OD", /* push pull / open drain */
- (config & 4) ? "PUE" : "PUD", /* pull up enabled/disabled */
- (config & 8) ? "LOCKED" : "", /* locked / unlocked */
- (config & 16) ? "LEVEL" : "EDGE",/* level/edge input */
- (config & 32) ? "HI" : "LO", /* trigger on rise/fall edge */
- (config & 64) ? "DEBOUNCE" : "", /* debounce */
+ switch (c) {
- amp->gpio_get(index), amp->gpio_current(index));
+ /* cases are a mix of old command letters, and new PF_CMD_*
+ symbols, for Off & On actions respectively.
+ */
+ case '0':
+ amp->gpio_set(m, 0);
+ break;
+ case '1':
+ amp->gpio_set(m, 1);
+ break;
+ case PF_CMD_OUT_ENA:
+ dev_dbg(dev, "GPIO%d output enabled\n", m);
+ amp->gpio_config(m, ~1, 1);
+ break;
+ case 'o':
+ dev_dbg(dev, "GPIO%d output disabled\n", m);
+ amp->gpio_config(m, ~1, 0);
+ break;
+ case PF_CMD_TOTEM:
+ dev_dbg(dev, "GPIO%d output is push pull\n", m);
+ amp->gpio_config(m, ~2, 2);
+ break;
+ case 't':
+ dev_dbg(dev, "GPIO%d output is open drain\n", m);
+ amp->gpio_config(m, ~2, 0);
+ break;
+ case PF_CMD_PULLUP:
+ dev_dbg(dev, "GPIO%d pull up enabled\n", m);
+ amp->gpio_config(m, ~4, 4);
+ break;
+ case 'p':
+ dev_dbg(dev, "GPIO%d pull up disabled\n", m);
+ amp->gpio_config(m, ~4, 0);
+ break;
+ case 'v':
+ /* View Current pin settings */
+ amp->gpio_dump(amp, m);
+ break;
+ case '\n':
+ /* end of settings string, do nothing */
+ break;
+ default:
+ dev_err(dev, "io%2d bad setting: chr<0x%2x>\n",
+ m, (int)c);
+ err++;
+ }
+ return err;
}
ssize_t nsc_gpio_write(struct file *file, const char __user *data,
@@ -44,57 +120,14 @@ ssize_t nsc_gpio_write(struct file *file
{
unsigned m = iminor(file->f_dentry->d_inode);
struct nsc_gpio_ops *amp = file->private_data;
- struct device *dev = amp->dev;
- size_t i;
- int err = 0;
+ int i, err = 0;
for (i = 0; i < len; ++i) {
char c;
if (get_user(c, data + i))
return -EFAULT;
- switch (c) {
- case '0':
- amp->gpio_set(m, 0);
- break;
- case '1':
- amp->gpio_set(m, 1);
- break;
- case 'O':
- dev_dbg(dev, "GPIO%d output enabled\n", m);
- amp->gpio_config(m, ~1, 1);
- break;
- case 'o':
- dev_dbg(dev, "GPIO%d output disabled\n", m);
- amp->gpio_config(m, ~1, 0);
- break;
- case 'T':
- dev_dbg(dev, "GPIO%d output is push pull\n", m);
- amp->gpio_config(m, ~2, 2);
- break;
- case 't':
- dev_dbg(dev, "GPIO%d output is open drain\n", m);
- amp->gpio_config(m, ~2, 0);
- break;
- case 'P':
- dev_dbg(dev, "GPIO%d pull up enabled\n", m);
- amp->gpio_config(m, ~4, 4);
- break;
- case 'p':
- dev_dbg(dev, "GPIO%d pull up disabled\n", m);
- amp->gpio_config(m, ~4, 0);
- break;
- case 'v':
- /* View Current pin settings */
- amp->gpio_dump(amp, m);
- break;
- case '\n':
- /* end of settings string, do nothing */
- break;
- default:
- dev_err(dev, "io%2d bad setting: chr<0x%2x>\n",
- m, (int)c);
- err++;
- }
+
+ err += command_write(amp, c, m);
}
if (err)
return -EINVAL; /* full string handled, report error */
@@ -121,15 +154,414 @@ EXPORT_SYMBOL(nsc_gpio_write);
EXPORT_SYMBOL(nsc_gpio_read);
EXPORT_SYMBOL(nsc_gpio_dump);
+#define BITVAL(a) "%u\n", !!(a) /* force int to boolean */
+#define PORTVAL(a) "0x%x\n", (a) /* expose bitval pos in port */
+
+/* 8 sysfs-callbacks, partitioned on 3 axes:
+ get/set: as reqd by 2 different callback fn-signatures
+ value/: for port-wide
+ /feature: mostly use-once, slower, synthesized port-access
+ bit/port: for values, separate for simplicity, speed
+
+ This is arguably over-optimization, may clash with client-module
+ feature to bit/port mappings.
+*/
+ssize_t nsc_gpio_sysfs_get_bitval(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->bitnum;
+ int func = attr->fn_slct;
+ unsigned res = -1;
+
+ BUG_ON(!amp);
+
+ switch (func) {
+ case PF_CMD_VALUE:
+ res = amp->gpio_get(idx);
+ break;
+ case PF_CMD_CURR:
+ res = amp->gpio_current(idx);
+ break;
+ }
+ return bitpos ?
+ sprintf(buf, PORTVAL(res)) :
+ sprintf(buf, BITVAL(res));
+}
+ssize_t nsc_gpio_sysfs_get_portval(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->portnum;
+ int func = attr->fn_slct;
+ unsigned res = -1;
+
+ BUG_ON(!amp);
+ switch (func) {
+ case PF_CMD_VALUE:
+ res = amp->gpio_get_port(idx);
+ break;
+ case PF_CMD_CURR:
+ res = amp->gpio_current_port(idx);
+ break;
+ }
+ return sprintf(buf, PORTVAL(res));
+}
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_bitval);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_portval);
+
+ssize_t nsc_gpio_sysfs_set_bitval(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->bitnum;
+ long setting = simple_strtol(buf, NULL, 10);
+
+ /* CURR is VALUE for writes */
+ BUG_ON(!amp);
+ amp->gpio_set(idx, setting);
+ return count;
+}
+ssize_t nsc_gpio_sysfs_set_portval(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->portnum;
+ long setting = simple_strtol(buf, NULL, 10);
+
+ BUG_ON(!amp);
+ amp->gpio_setclr_port(idx, setting, ~setting);
+ return count;
+}
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_bitval);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_portval);
+
+ssize_t nsc_gpio_sysfs_get_bitconf(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->bitnum;
+ int func = attr->fn_slct;
+ u32 config, res = 0;
+
+ BUG_ON(!amp);
+ config = amp->gpio_config(idx, ~0, 0);
+
+ switch (func) {
+
+ case PF_CMD_CURR:
+ res = amp->gpio_current(idx);
+ return sprintf(buf, BITVAL(res));
+
+ case PF_CMD_STATUS:
+ nsc_gpio_status(amp, idx);
+ return sprintf(buf, "%s\n", statbuf);
+
+ case PF_CMD_OUT_ENA:
+ res = config & PF_MASK_OUT_ENA;
+ break;
+ case PF_CMD_TOTEM:
+ res = config & PF_MASK_TOTEM;
+ break;
+ case PF_CMD_PULLUP:
+ res = config & PF_MASK_PULLUP;
+ break;
+ case PF_CMD_LOCKED:
+ res = config & PF_MASK_LOCKED;
+ break;
+ case PF_CMD_DEBOUNCE:
+ res = config & PF_MASK_DEBOUNCE;
+ break;
+ case PF_CMD_INT_ENA:
+ res = config & PF_MASK_INT_ENA;
+ break;
+ case PF_CMD_INT_TRIG:
+ res = config & PF_MASK_INT_TRIG;
+ break;
+
+ default:
+ dev_err(dev, "unknown cmd '%c'\n", func);
+ }
+ dev_dbg(dev, "get-bitconf() idx:%d cmd:'%c' conf%02x res:%02x\n",
+ idx, func, config, res);
+
+ return sprintf(buf, BITVAL(res));
+}
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_bitconf);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_bitconf);
+
+
+#define replace(Mask,Val) ~Mask, Val ? Mask : 0
+
+ssize_t nsc_gpio_sysfs_set_bitconf(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->bitnum;
+ int func = attr->fn_slct;
+ long setting = simple_strtol(buf, NULL, 10);
+
+ BUG_ON(!amp);
+
+ switch (func) {
+
+ case PF_CMD_STATUS:
+ {
+ /* device-file write interface. Kludge, but small */
+ int i, err = 0;
+ dev_warn(dev, "try to set status to: %s\n", buf);
+ for (i = 0; buf[i]; i++)
+ err += command_write(amp, buf[i], idx);
+ if (err)
+ dev_warn(dev, "%d errs on cmd %s\n", err, buf);
+ break;
+ }
+ case PF_CMD_OUT_ENA:
+ dev_info(dev, "GPIO%d output enabled=%ld\n", idx, setting);
+ amp->gpio_config(idx, replace(1, setting));
+ // ~1, setting ? 1 : 0);
+ break;
+ case PF_CMD_PULLUP:
+ dev_info(dev, "GPIO%d pullup enabled=%ld\n", idx, setting);
+ amp->gpio_config(idx, ~2, setting ? 2 : 0);
+ break;
+ case PF_CMD_TOTEM:
+ dev_info(dev, "GPIO%d totem-pole enabled=%ld\n", idx, setting);
+ amp->gpio_config(idx, ~4, setting ? 4 : 0);
+ break;
+ case 'L':
+ dev_info(dev, "GPIO%d pin locked=%ld\n", idx, setting);
+ amp->gpio_config(idx, ~8, setting ? 8 : 0);
+ break;
+ case 'D':
+ dev_info(dev, "GPIO%d debounced=%ld\n", idx, setting);
+ amp->gpio_config(idx, ~PF_MASK_DEBOUNCE,
+ setting ? PF_MASK_DEBOUNCE : 0);
+ break;
+ default:
+ dev_err(dev, "sysfs unknown cmd '%c'\n", func);
+ }
+ dev_info(dev, "set-bitconf() idx:%d cmd:'%c' buf:'%s' set:%lx\n",
+ idx, func, buf, setting);
+
+ return strlen(buf);
+}
+
+#if 10
+ssize_t nsc_gpio_sysfs_get_portconf(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->portnum;
+ int func = attr->fn_slct;
+ u32 config, res = 0, ret = 0;
+ char *s = buf;
+ int bit, width = amp->port_size;
+
+ BUG_ON(!amp);
+ BUG_ON(!width);
+
+ for (bit = 0; bit < width; bit++) {
+
+ config = amp->gpio_config(bit + idx * width, ~0, 0);
+
+ switch ((char)func) {
+ case PF_CMD_CURR:
+ res = amp->gpio_current(bit);
+ sprintf(s, BITVAL(res));
+ case PF_CMD_STATUS:
+ nsc_gpio_status(amp, bit);
+ sprintf(s, "%s\n", statbuf);
+ case PF_CMD_OUT_ENA:
+ res = config & PF_MASK_OUT_ENA;
+ break;
+ case PF_CMD_PULLUP:
+ res = config & PF_MASK_PULLUP;
+ break;
+ case PF_CMD_TOTEM:
+ res = config & PF_MASK_TOTEM;
+ break;
+ case PF_CMD_LOCKED:
+ res = config & PF_MASK_LOCKED;
+ break;
+ case PF_CMD_DEBOUNCE:
+ res = config & PF_MASK_DEBOUNCE;
+ break;
+ case PF_CMD_INT_ENA:
+ res = config & PF_MASK_INT_ENA;
+ break;
+ case PF_CMD_INT_TRIG:
+ res = config & PF_MASK_INT_TRIG;
+ break;
+
+ default:
+ dev_err(dev, "unknown cmd '%c'\n", func);
+ res = 0;
+ }
+ res = res ? 1<<bit : 0;
+ ret |= res;
+
+ dev_dbg(dev, "get-portconf bit:%d.%d cfg:%x cmd'%c' res:%x\n",
+ idx, bit, config, func, res);
+ }
+ dev_dbg(dev, "get-portconf(%d) idx=%d cmd='%c' ret:%x\n",
+ width, idx, func, ret);
+
+ return sprintf(buf, PORTVAL(ret));
+}
+
+ssize_t nsc_gpio_sysfs_set_portconf(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct nsc_gpio_ops *amp = dev->driver_data;
+ struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
+ int idx = attr->bitnum;
+ int func = attr->fn_slct;
+ int bit, width = amp->port_size;
+ long setting = simple_strtol(buf, NULL, 10);
+
+ BUG_ON(!amp);
+
+ for (bit = 0; bit < width; bit++) {
+
+ switch (func) {
+
+ case PF_CMD_STATUS:
+ {
+ /* device-file write interface */
+ int i, err = 0;
+ dev_warn(dev, "try to set status to: %s\n", buf);
+ for (i = 0; buf[i]; i++)
+ err += command_write(amp, buf[i], idx);
+ if (err)
+ dev_warn(dev, "%d errs on cmd %s\n", err, buf);
+ break;
+ }
+ case PF_CMD_OUT_ENA:
+ amp->gpio_config(idx, ~PF_MASK_OUT_ENA,
+ setting ? PF_MASK_OUT_ENA : 0);
+ break;
+ case PF_CMD_TOTEM:
+ amp->gpio_config(idx, ~PF_MASK_TOTEM,
+ setting ? PF_MASK_TOTEM : 0);
+ break;
+ case PF_CMD_PULLUP:
+ amp->gpio_config(idx, ~PF_MASK_PULLUP,
+ setting ? PF_MASK_PULLUP : 0);
+ break;
+ case PF_CMD_LOCKED:
+ amp->gpio_config(idx, ~PF_CMD_LOCKED,
+ setting ? PF_MASK_LOCKED : 0);
+ break;
+ case PF_CMD_DEBOUNCE:
+ amp->gpio_config(idx, ~PF_MASK_DEBOUNCE,
+ setting ? PF_MASK_DEBOUNCE : 0);
+ break;
+ default:
+ dev_err(dev, "sysfs unknown cmd '%c'\n", func);
+ }
+ }
+ dev_info(dev, "set-portconf() idx:%d cmd:'%c' buf:'%s' set:%lx\n",
+ idx, func, buf, setting);
+
+ return strlen(buf);
+}
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_portconf);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_portconf);
+#endif
+
+
+
+static u8 legacy = 1, feature_attrs = 1;
+static int dev_attr_create_errs;
+static void create_devattr_file(struct device* dev,
+ struct device_attribute *dev_attr)
+{
+ int err;
+
+ if (dev_attr->show || dev_attr->store) {
+
+ err = device_create_file(dev, dev_attr);
+ if (err)
+ dev_attr_create_errs++;
+ }
+ else
+ dev_dbg(dev, "skipping %s\n", dev_attr->attr.name);
+}
+
+/* next 2 functions (and previous kludge) could be replaced by pair of
+ calls to sysfs_create/remove_group, except that collecting
+ attributes into a group would be manual.
+*/
+
+void nsc_gpio_sysfs_bits_init(struct device* dev,
+ struct gpio_attributes pp[], int numdevs)
+{
+ int i;
+
+ for (i = 0; i < numdevs; i++) {
+
+ create_devattr_file(dev, &pp[i].value.dev_attr);
+ create_devattr_file(dev, &pp[i].curr.dev_attr);
+
+ /* provide legacy device-file emulation here */
+ if (legacy)
+ create_devattr_file(dev, &pp[i].status.dev_attr);
+
+ /* create separate attr-file per feature */
+ if (feature_attrs) {
+ create_devattr_file(dev, &pp[i].oe.dev_attr);
+ create_devattr_file(dev, &pp[i].pue.dev_attr);
+ create_devattr_file(dev, &pp[i].totem.dev_attr);
+ create_devattr_file(dev, &pp[i].locked.dev_attr);
+ create_devattr_file(dev, &pp[i].bounce.dev_attr);
+ create_devattr_file(dev, &pp[i].int_en.dev_attr);
+ create_devattr_file(dev, &pp[i].int_lvl.dev_attr);
+ }
+ }
+}
+
+void nsc_gpio_sysfs_bits_fini(struct device* dev,
+ struct gpio_attributes pp[], int numdevs)
+{
+ int i;
+ for (i = 0; i < numdevs; i++) {
+ device_remove_file(dev, &pp[i].value.dev_attr);
+ device_remove_file(dev, &pp[i].curr.dev_attr);
+ /* always remove, whether there or not */
+ device_remove_file(dev, &pp[i].status.dev_attr);
+ device_remove_file(dev, &pp[i].oe.dev_attr);
+ device_remove_file(dev, &pp[i].pue.dev_attr);
+ device_remove_file(dev, &pp[i].totem.dev_attr);
+ device_remove_file(dev, &pp[i].locked.dev_attr);
+ device_remove_file(dev, &pp[i].bounce.dev_attr);
+ }
+}
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_bits_init);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_bits_fini);
+
static int __init nsc_gpio_init(void)
{
- printk(KERN_DEBUG NAME " initializing\n");
+ printk(KERN_DEBUG DRVNAME " initializing\n");
return 0;
}
static void __exit nsc_gpio_cleanup(void)
{
- printk(KERN_DEBUG NAME " cleanup\n");
+ printk(KERN_DEBUG DRVNAME " cleanup\n");
}
module_init(nsc_gpio_init);
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/drivers/char/pc8736x_gpio.c roll3-m2/drivers/char/pc8736x_gpio.c
--- ../linux-2.6.18-rc3-mm2-sk/drivers/char/pc8736x_gpio.c 2006-08-06 10:18:40.000000000 -0600
+++ roll3-m2/drivers/char/pc8736x_gpio.c 2006-08-08 14:01:00.000000000 -0600
@@ -1,5 +1,4 @@
-/* linux/drivers/char/pc8736x_gpio.c
-
+/*
National Semiconductor PC8736x GPIO driver. Allows a user space
process to play with the GPIO pins.
@@ -9,6 +8,8 @@
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>,
*/
+#define DRVNAME "pc8736x_gpio"
+
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/errno.h>
@@ -22,11 +23,13 @@
#include <linux/platform_device.h>
#include <asm/uaccess.h>
-#define DEVNAME "pc8736x_gpio"
-
-MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
-MODULE_DESCRIPTION("NatSemi/Winbond PC-8736x GPIO Pin Driver");
-MODULE_LICENSE("GPL");
+static int nobits = 0;
+module_param(nobits, int, 0);
+MODULE_PARM_DESC(nobits, "nobits=1 to suppress sysfs bits interface");
+
+static int noports = 0;
+module_param(noports, int, 0);
+MODULE_PARM_DESC(noports, "noports=1 to supress sysfs ports interface");
static int major; /* default to dynamic major */
module_param(major, int, 0);
@@ -144,15 +147,29 @@ static u32 pc8736x_gpio_configure(unsign
SIO_GPIO_PIN_CONFIG);
}
-static int pc8736x_gpio_get(unsigned minor)
+/* vtable-API functions.
+ this is a gpio-port device, so bitvals are pulled from ports (in princple)
+*/
+static u32 pc8736x_gpio_get_port(unsigned port)
+{
+ u8 val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
+
+ dev_dbg(&pdev->dev, "_gpio_get(%d from %x) == val %d\n",
+ port, pc8736x_gpio_base + port_offset[port] + PORT_IN,
+ val);
+
+ return (u32)val;
+}
+
+static u32 pc8736x_gpio_get(unsigned minor)
{
- int port, bit, val;
+ int port, bit;
+ u32 val;
port = minor >> 3;
bit = minor & 7;
val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
- val >>= bit;
- val &= 1;
+ val &= 1 << bit;
dev_dbg(&pdev->dev, "_gpio_get(%d from %x bit %d) == val %d\n",
minor, pc8736x_gpio_base + port_offset[port] + PORT_IN, bit,
@@ -188,13 +205,21 @@ static void pc8736x_gpio_set(unsigned mi
pc8736x_gpio_shadow[port] = val;
}
-static int pc8736x_gpio_current(unsigned minor)
+static u32 pc8736x_gpio_current_port(unsigned minor)
{
int port, bit;
minor &= 0x1f;
port = minor >> 3;
bit = minor & 7;
- return ((pc8736x_gpio_shadow[port] >> bit) & 0x01);
+ return (u32)((pc8736x_gpio_shadow[port] >> bit) & 0x01);
+}
+static u32 pc8736x_gpio_current(unsigned minor)
+{
+ int port, bit;
+ minor &= 0x1f;
+ port = minor >> 3;
+ bit = minor & 7;
+ return (u32)((pc8736x_gpio_shadow[port] >> bit) & 0x01);
}
static void pc8736x_gpio_change(unsigned index)
@@ -202,6 +227,26 @@ static void pc8736x_gpio_change(unsigned
pc8736x_gpio_set(index, !pc8736x_gpio_current(index));
}
+static void pc8736x_gpio_setclr_port(unsigned port, u32 bits, u32 mask)
+{
+ u8 val;
+ u8 curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);
+
+ val = (curval & ~mask) | (bits & mask);
+
+ dev_dbg(&pdev->dev, "gpio_set(port:%d addr:%x cur:%x new:%d)\n",
+ port, pc8736x_gpio_base + port_offset[port] + PORT_OUT,
+ curval, val);
+
+ outb_p(val, pc8736x_gpio_base + port_offset[port] + PORT_OUT);
+
+ curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);
+ val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
+
+ dev_dbg(&pdev->dev, "wrote %x, read: %x\n", curval, val);
+ pc8736x_gpio_shadow[port] = val;
+}
+
static struct nsc_gpio_ops pc8736x_gpio_ops = {
.owner = THIS_MODULE,
.gpio_config = pc8736x_gpio_configure,
@@ -209,9 +254,14 @@ static struct nsc_gpio_ops pc8736x_gpio_
.gpio_get = pc8736x_gpio_get,
.gpio_set = pc8736x_gpio_set,
.gpio_change = pc8736x_gpio_change,
- .gpio_current = pc8736x_gpio_current
+ .gpio_current = pc8736x_gpio_current,
+ .port_size = 8,
+ .gpio_get_port = pc8736x_gpio_get_port,
+ .gpio_setclr_port = pc8736x_gpio_setclr_port,
+ .gpio_current_port = pc8736x_gpio_current_port,
};
+/* char-dev API */
static int pc8736x_gpio_open(struct inode *inode, struct file *file)
{
unsigned m = iminor(inode);
@@ -231,6 +281,61 @@ static const struct file_operations pc87
.read = nsc_gpio_read,
};
+/* sysfs-gpio API */
+
+static struct gpio_attributes port0[] = {
+ GPIO_ATTRS(0,0), GPIO_ATTRS(0,1), GPIO_ATTRS(0,2),
+ GPIO_ATTRS(0,3), GPIO_ATTRS(0,4), GPIO_ATTRS(0,5),
+ GPIO_ATTRS(0,6), GPIO_ATTRS(0,7)
+};
+
+static struct gpio_attributes port1[] = {
+ GPIO_ATTRS(1,0), GPIO_ATTRS(1,1), GPIO_ATTRS(1,2),
+ GPIO_ATTRS(1,3), GPIO_ATTRS(1,4), GPIO_ATTRS(1,5),
+ GPIO_ATTRS(1,6), GPIO_ATTRS(1,7)
+};
+static struct gpio_attributes port2[] = {
+ GPIO_ATTRS(2,0), GPIO_ATTRS(2,1), GPIO_ATTRS(2,2),
+ GPIO_ATTRS(2,3), GPIO_ATTRS(2,4), GPIO_ATTRS(2,5),
+ GPIO_ATTRS(2,6), GPIO_ATTRS(2,7)
+};
+static struct gpio_attributes port3[] = {
+ GPIO_ATTRS(3,0), GPIO_ATTRS(3,1), GPIO_ATTRS(3,2),
+ GPIO_ATTRS(3,3), GPIO_ATTRS(3,4), GPIO_ATTRS(3,5),
+ GPIO_ATTRS(3,6), GPIO_ATTRS(3,7)
+};
+
+static struct gpio_attributes ports[] = {
+ GPIO_PORT_ATTRS(0), GPIO_PORT_ATTRS(1),
+ GPIO_PORT_ATTRS(2), GPIO_PORT_ATTRS(3),
+};
+
+static void __init pc8736x_sysfs_init(struct device* dev)
+{
+ if (!nobits) {
+ dev_info(dev, "creating gpio-sysfs pin interfaces\n");
+ nsc_gpio_sysfs_bits_init(&pdev->dev, port0, 8);
+ nsc_gpio_sysfs_bits_init(&pdev->dev, port1, 8);
+ nsc_gpio_sysfs_bits_init(&pdev->dev, port2, 8);
+ nsc_gpio_sysfs_bits_init(&pdev->dev, port3, 8);
+ }
+ if (!noports) {
+ dev_info(dev, "creating gpio-sysfs port interfaces\n");
+ nsc_gpio_sysfs_bits_init(&pdev->dev, ports, 4);
+ }
+}
+static void pc8736x_sysfs_fini(struct device* dev)
+{
+ dev_info(dev, "pc8736x_sysfs_fini");
+ nsc_gpio_sysfs_bits_fini(&pdev->dev, port0, 8);
+ nsc_gpio_sysfs_bits_fini(&pdev->dev, port1, 8);
+ nsc_gpio_sysfs_bits_fini(&pdev->dev, port2, 8);
+ nsc_gpio_sysfs_bits_fini(&pdev->dev, port3, 8);
+
+ nsc_gpio_sysfs_bits_fini(&pdev->dev, ports, 4);
+}
+
+/* device init */
static void __init pc8736x_init_shadow(void)
{
int port;
@@ -250,7 +355,7 @@ static int __init pc8736x_gpio_init(void
int rc;
dev_t devid;
- pdev = platform_device_alloc(DEVNAME, 0);
+ pdev = platform_device_alloc(DRVNAME, 0);
if (!pdev)
return -ENOMEM;
@@ -288,7 +393,7 @@ static int __init pc8736x_gpio_init(void
pc8736x_gpio_base = (superio_inb(SIO_BASE_HADDR) << 8
| superio_inb(SIO_BASE_LADDR));
- if (!request_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE, DEVNAME)) {
+ if (!request_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE, DRVNAME)) {
rc = -ENODEV;
dev_err(&pdev->dev, "GPIO ioport %x busy\n",
pc8736x_gpio_base);
@@ -298,9 +403,9 @@ static int __init pc8736x_gpio_init(void
if (major) {
devid = MKDEV(major, 0);
- rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DEVNAME);
+ rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DRVNAME);
} else {
- rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DEVNAME);
+ rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DRVNAME);
major = MAJOR(devid);
}
@@ -319,6 +424,10 @@ static int __init pc8736x_gpio_init(void
cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops);
cdev_add(&pc8736x_gpio_cdev, devid, PC8736X_GPIO_CT);
+ /* provide info where sysfs callbacks can get them */
+ pc8736x_gpio_ops.dev->driver_data = &pc8736x_gpio_ops;
+ pc8736x_sysfs_init(&pdev->dev);
+
return 0;
undo_request_region:
@@ -335,6 +444,8 @@ static void __exit pc8736x_gpio_cleanup(
{
dev_dbg(&pdev->dev, "cleanup\n");
+ pc8736x_sysfs_fini(&pdev->dev);
+
cdev_del(&pc8736x_gpio_cdev);
unregister_chrdev_region(MKDEV(major,0), PC8736X_GPIO_CT);
release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);
@@ -345,3 +456,7 @@ static void __exit pc8736x_gpio_cleanup(
module_init(pc8736x_gpio_init);
module_exit(pc8736x_gpio_cleanup);
+
+MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
+MODULE_DESCRIPTION("NatSemi/Winbond PC-8736x GPIO Pin Driver");
+MODULE_LICENSE("GPL");
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/drivers/char/scx200_gpio.c roll3-m2/drivers/char/scx200_gpio.c
--- ../linux-2.6.18-rc3-mm2-sk/drivers/char/scx200_gpio.c 2006-08-06 10:18:42.000000000 -0600
+++ roll3-m2/drivers/char/scx200_gpio.c 2006-08-08 13:20:42.000000000 -0600
@@ -1,9 +1,11 @@
-/* linux/drivers/char/scx200_gpio.c
-
+/*
National Semiconductor SCx200 GPIO driver. Allows a user space
process to play with the GPIO pins.
- Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */
+ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+*/
+
+#define DRVNAME "scx200_gpio"
#include <linux/device.h>
#include <linux/fs.h>
@@ -21,10 +23,6 @@
#include <linux/scx200_gpio.h>
#include <linux/nsc_gpio.h>
-#define DRVNAME "scx200_gpio"
-
-static struct platform_device *pdev;
-
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi/AMD SCx200 GPIO Pin Driver");
MODULE_LICENSE("GPL");
@@ -33,6 +31,14 @@ static int major = 0; /* default to dyn
module_param(major, int, 0);
MODULE_PARM_DESC(major, "Major device number");
+static int nobits = 0;
+module_param(nobits, int, 0);
+MODULE_PARM_DESC(nobits, "nobits=1 to suppress sysfs bits interface");
+
+static int noports = 0;
+module_param(noports, int, 0);
+MODULE_PARM_DESC(noports, "noports=1 to supress sysfs ports interface");
+
#define MAX_PINS 32 /* 64 later, when known ok */
struct nsc_gpio_ops scx200_gpio_ops = {
@@ -42,10 +48,73 @@ struct nsc_gpio_ops scx200_gpio_ops = {
.gpio_get = scx200_gpio_get,
.gpio_set = scx200_gpio_set,
.gpio_change = scx200_gpio_change,
- .gpio_current = scx200_gpio_current
+ .gpio_current = scx200_gpio_current,
+ .port_size = 32,
+ .gpio_get_port = scx200_gpio_get_port,
+ .gpio_setclr_port = scx200_gpio_setclr_port,
+
+ /* add these back to exploit pxa-2xx, which has separate
+ set/clear addresses, avoiding read-modify-write cycles on
+ the pin. (maybe)
+
+ void (*gpio_set_lo) (unsigned iminor, int state);
+ void (*gpio_set_hi) (unsigned iminor, int state);
+ */
};
EXPORT_SYMBOL_GPL(scx200_gpio_ops);
+static struct gpio_attributes port0[] = {
+ GPIO_ATTRS(0,0), GPIO_ATTRS(0,1), GPIO_ATTRS(0,2),
+ GPIO_ATTRS(0,3), GPIO_ATTRS(0,4), GPIO_ATTRS(0,5),
+ GPIO_ATTRS(0,6), GPIO_ATTRS(0,7), GPIO_ATTRS(0,8),
+ GPIO_ATTRS(0,9), GPIO_ATTRS(0,10), GPIO_ATTRS(0,11),
+ GPIO_ATTRS(0,12), GPIO_ATTRS(0,13), GPIO_ATTRS(0,14),
+ GPIO_ATTRS(0,15), GPIO_ATTRS(0,16), GPIO_ATTRS(0,17),
+ GPIO_ATTRS(0,18), GPIO_ATTRS(0,19), GPIO_ATTRS(0,20),
+ GPIO_ATTRS(0,21), GPIO_ATTRS(0,22), GPIO_ATTRS(0,23),
+ GPIO_ATTRS(0,24), GPIO_ATTRS(0,25), GPIO_ATTRS(0,26),
+ GPIO_ATTRS(0,27), GPIO_ATTRS(0,28), GPIO_ATTRS(0,29),
+ GPIO_ATTRS(0,30), GPIO_ATTRS(0,31),
+};
+
+static struct gpio_attributes port1[] = {
+ GPIO_ATTRS(1,0), GPIO_ATTRS(1,1), GPIO_ATTRS(1,2),
+ GPIO_ATTRS(1,3), GPIO_ATTRS(1,4), GPIO_ATTRS(1,5),
+ GPIO_ATTRS(1,6), GPIO_ATTRS(1,7), GPIO_ATTRS(1,8),
+ GPIO_ATTRS(1,9), GPIO_ATTRS(1,10), GPIO_ATTRS(1,11),
+ GPIO_ATTRS(1,12), GPIO_ATTRS(1,13), GPIO_ATTRS(1,14),
+ GPIO_ATTRS(1,15), GPIO_ATTRS(1,16), GPIO_ATTRS(1,17),
+ GPIO_ATTRS(1,18), GPIO_ATTRS(1,19), GPIO_ATTRS(1,20),
+ GPIO_ATTRS(1,21), GPIO_ATTRS(1,22), GPIO_ATTRS(1,23),
+ GPIO_ATTRS(1,24), GPIO_ATTRS(1,25), GPIO_ATTRS(1,26),
+ GPIO_ATTRS(1,27), GPIO_ATTRS(1,28), GPIO_ATTRS(1,29),
+ GPIO_ATTRS(1,30), GPIO_ATTRS(1,31),
+};
+static struct gpio_attributes ports[] = {
+ GPIO_PORT_ATTRS(0), GPIO_PORT_ATTRS(1),
+};
+
+static void __init scx200_sysfs_init(struct device* dev)
+{
+ if (!nobits) {
+ nsc_gpio_sysfs_bits_init(dev, port0, ARRAY_SIZE(port0));
+ nsc_gpio_sysfs_bits_init(dev, port1, ARRAY_SIZE(port1));
+ }
+ if (!noports)
+ nsc_gpio_sysfs_bits_init(dev, ports, ARRAY_SIZE(ports));
+}
+static void scx200_sysfs_fini(struct device* dev)
+{
+ dev_info(dev, "scx200_sysfs_fini");
+ if (!nobits) {
+ nsc_gpio_sysfs_bits_fini(dev, port0, ARRAY_SIZE(port0));
+ nsc_gpio_sysfs_bits_fini(dev, port1, ARRAY_SIZE(port1));
+ }
+ if (!noports)
+ nsc_gpio_sysfs_bits_fini(dev, ports, ARRAY_SIZE(ports));
+}
+
+/* file API */
static int scx200_gpio_open(struct inode *inode, struct file *file)
{
unsigned m = iminor(inode);
@@ -69,7 +138,8 @@ static const struct file_operations scx2
.release = scx200_gpio_release,
};
-static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
+static struct platform_device *pdev; /* for dev_info(&pdev->dev, */
+static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
static int __init scx200_gpio_init(void)
{
@@ -92,6 +162,7 @@ static int __init scx200_gpio_init(void)
/* nsc_gpio uses dev_dbg(), so needs this */
scx200_gpio_ops.dev = &pdev->dev;
+ scx200_gpio_ops.dev->driver_data = &scx200_gpio_ops;
if (major) {
devid = MKDEV(major, 0);
@@ -108,6 +179,8 @@ static int __init scx200_gpio_init(void)
cdev_init(&scx200_gpio_cdev, &scx200_gpio_fileops);
cdev_add(&scx200_gpio_cdev, devid, MAX_PINS);
+ scx200_sysfs_init(&pdev->dev);
+
return 0; /* succeed */
undo_platform_device_add:
@@ -120,6 +193,7 @@ undo_malloc:
static void __exit scx200_gpio_cleanup(void)
{
+ scx200_sysfs_fini(&pdev->dev);
cdev_del(&scx200_gpio_cdev);
/* cdev_put(&scx200_gpio_cdev); */
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/include/linux/nsc_gpio.h roll3-m2/include/linux/nsc_gpio.h
--- ../linux-2.6.18-rc3-mm2-sk/include/linux/nsc_gpio.h 2006-07-30 11:31:26.000000000 -0600
+++ roll3-m2/include/linux/nsc_gpio.h 2006-08-08 16:55:35.000000000 -0600
@@ -1,19 +1,16 @@
/**
- nsc_gpio.c
-
National Semiconductor GPIO common access methods.
- struct nsc_gpio_ops abstracts the low-level access
- operations for the GPIO units on 2 NSC chip families; the GEODE
- integrated CPU, and the PC-8736[03456] integrated PC-peripheral
- chips.
+ struct nsc_gpio_ops abstracts the low-level access operations for
+ the GPIO units on 2 NSC chip families; the GEODE integrated CPU
+ SC-1100, and the PC-8736[03456] integrated PC-peripheral chips.
The GPIO units on these chips have the same pin architecture, but
the access methods differ. Thus, scx200_gpio and pc8736x_gpio
implement their own versions of these routines; and use the common
file-operations routines implemented in nsc_gpio module.
- Copyright (c) 2005 Jim Cromie <jim.cromie@gmail.com>
+ Copyright (c) 2005,2006 Jim Cromie <jim.cromie@gmail.com>
NB: this work was tested on the Geode SC-1100 and PC-87366 chips.
NSC sold the GEODE line to AMD, and the PC-8736x line to Winbond.
@@ -21,15 +18,39 @@
struct nsc_gpio_ops {
struct module* owner;
- u32 (*gpio_config) (unsigned iminor, u32 mask, u32 bits);
+ struct device* dev; /* for dev_dbg() support, set in init */
+ u8 port_size; /* 8 or 32 so far. 32 max */
+
+ /* config ctrl & human desc */
+ u32 (*gpio_config) (unsigned iminor, u32 bits, u32 clr);
void (*gpio_dump) (struct nsc_gpio_ops *amp, unsigned iminor);
- int (*gpio_get) (unsigned iminor);
+
+ /* bit-wide value interface */
+ u32 (*gpio_get) (unsigned iminor);
void (*gpio_set) (unsigned iminor, int state);
void (*gpio_change) (unsigned iminor);
- int (*gpio_current) (unsigned iminor);
- struct device* dev; /* for dev_dbg() support, set in init */
+ u32 (*gpio_current) (unsigned iminor);
+
+ /* want to restore set-hi()/set-lo() for PXA, which has
+ separate set and clear registers/insns, allowing PXA to
+ avoid read-modify-write cycles (IIUC). Im missing
+ something though, since RMW cycles dont pertain to single
+ bits, but rather to ports (where an RMW is needed to
+ preserve un-changed bits) void (*gpio_set_hi) (unsigned
+ iminor); void (*gpio_set_lo) (unsigned iminor);
+ */
+
+ /* port-wide accessors (thanks Chris). For hardware which can
+ exploit it, gpio_setclr_port() separates set and clear
+ params to avoid Read-Modify-Write cycles.
+ */
+ u32 (*gpio_get_port) (unsigned portnum);
+ void (*gpio_set_port) (unsigned portnum, u32 bits);
+ void (*gpio_setclr_port) (unsigned portnum, u32 bits, u32 clr);
+ u32 (*gpio_current_port) (unsigned iminor);
};
+/* fileops user-API */
extern ssize_t nsc_gpio_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos);
@@ -38,3 +59,226 @@ extern ssize_t nsc_gpio_read(struct file
extern void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index);
+
+/* GPIO-sysfs model */
+#include <linux/device.h>
+#include <linux/sysfs.h>
+
+/* basic gpio-sysfs object, with 3-D addressing. This allows the use
+ of combined-callbacks which decode the 3-D parameters, and perform
+ the selected operation. (We only use 2 axes, but keeping all 3 has
+ almost no cost, and gives more info and flexibility
+*/
+struct gpio_dev_attr {
+ struct device_attribute dev_attr;
+ u8 bitnum; /* bit index */
+ u8 portnum; /* port index */
+ u8 fn_slct; /* feature selector */
+ u8 bit_port_type; /* GPIO_BIT, GPIO_PORT */
+};
+#define GPIO_BIT_TYPE 0
+#define GPIO_PORT_TYPE 1
+
+#define to_gpio_dev_attr(d) container_of(d, struct gpio_dev_attr, dev_attr)
+
+/* constants for 3 aspects of Pin Features used to initialize the
+ dev_attrs needed for the gpio-object's attr-files
+
+ PF_SFX - pin-feature dev-attr filename suffix. Might be nice to
+ make these driver-configurable (L8r)
+
+ PF_CMD - commands: constants are borrowed from fileops write
+ handler. The sysfs callbacks see them in the fn_slct field, so the
+ *_config handlers switch(fn_slct) to fetch the right bits.
+
+ PF_MASK - pin-feature mask, ie config bits understood by the 2
+ client drivers of this module (scx200_gpio, pc8736x_gpio). This is
+ too coupled to the features exposed by nsc_gpio_ops->gpio_config(),
+ but its a simple re-do of the fileops write-handler.
+
+ The cmd-set has expanded to cover all pin-features, not just
+ 'config' features, notably 'V' for values and 'C' for
+ current-values.
+*/
+#define PF_SFX_OUT_ENA _output_enabled
+#define PF_SFX_TOTEM _totem
+#define PF_SFX_PULLUP _pullup_enabled
+#define PF_SFX_LOCKED _locked
+#define PF_SFX_DEBOUNCE _debounced
+#define PF_SFX_INT_ENA _int_enabled /* too exposing !?! */
+#define PF_SFX_INT_TRIG _int_level_trig
+
+#define PF_CMD_OUT_ENA 'O' /* 'o' tristate */
+#define PF_CMD_TOTEM 'T' /* 't' open-drain */
+#define PF_CMD_PULLUP 'P' /* 'p' disables pullup */
+#define PF_CMD_LOCKED 'L' /* no unlock */
+/* these are newer than 18-rcX */
+#define PF_CMD_DEBOUNCE 'D' /* 'd' disables */
+#define PF_CMD_INT_ENA 'I' /* too exposing !?! */
+#define PF_CMD_INT_TRIG 'E' /* 'e' level-triggered */
+
+#define PF_MASK_OUT_ENA 1 /* !tristate */
+#define PF_MASK_TOTEM 2 /* !open-drain */
+#define PF_MASK_PULLUP 4 /* !open-drain */
+#define PF_MASK_LOCKED 8 /* no unlock possible */
+#define PF_MASK_INT_ENA 16 /* some pins cant do this */
+#define PF_MASK_INT_TRIG 32 /* level !edge */
+#define PF_MASK_DEBOUNCE 64
+
+/* constants for expanded feature-set */
+#define PF_SFX_VALUE _value /* input on pin */
+#define PF_SFX_CURR _current /* last written value */
+#define PF_SFX_STATUS _status /* _config ?? */
+
+#define PF_CMD_VALUE 'V' /* values-in often port-wide */
+#define PF_CMD_CURR 'C' /* current values-out */
+#define PF_CMD_STATUS 'S' /* possible chardev emulation */
+
+#define PF_MASK_VALUE 0 /* not for gpio_config() */
+#define PF_MASK_CURR 0 /* use <0 if differences needed */
+#define PF_MASK_STATUS 0
+
+
+struct gpio_attributes {
+ struct gpio_dev_attr value; /* hw often is port-wide here */
+ struct gpio_dev_attr curr; /* driven value, may be != value */
+
+ /* pin-features, which are mostly use-once, so hw often
+ provides it per-pin only. */
+ struct gpio_dev_attr oe; /* output-enable, !tristate */
+ struct gpio_dev_attr totem; /* !open-drain */
+ struct gpio_dev_attr pue; /* pullup-enabled */
+ struct gpio_dev_attr bounce; /* debounce circuit active */
+ struct gpio_dev_attr locked; /* once locked, no unlock */
+ struct gpio_dev_attr status; /* device-file compat-hack */
+
+ struct gpio_dev_attr int_en; /* interrupt enable */
+ struct gpio_dev_attr int_lvl; /* int on level !edge */
+};
+
+/* GPIO_ATTRS and GPIO_PORT_ATTRS macros let driver declare the
+ interfaces for the underlying hardware:
+
+ static struct gpio_attributes port_0[] = {
+ GPIO_ATTRS(0,0), GPIO_ATTRS(0,1), ... GPIO_ATTRS(0,31) };
+
+ static struct gpio_attributes port_set[] = {
+ GPIO_PORT_ATTRS(0,32), GPIO_PORT_ATTRS(1,32) };
+*/
+
+#define GPIO_PIN(Port, Bit, Suffix, Mode, Show, Store, Cmd) \
+ { .dev_attr = __ATTR( bit_ ## Port.Bit ## Suffix, \
+ Mode, Show, Store), \
+ .bitnum = Bit, \
+ .portnum = Port, \
+ .fn_slct = Cmd, \
+ .bit_port_type = GPIO_BIT_TYPE }
+
+#define SYSFS_CB_RD(Access) nsc_gpio_sysfs_get_ ## Access
+#define SYSFS_CB_WR(Access) nsc_gpio_sysfs_set_ ## Access
+#define SYSFS_CB_NULL (void*)0
+
+#define GPIO_ATTR(Portnum, Bitnum, Feat, Suffix, Access) \
+ GPIO_PIN(Portnum, Bitnum, Suffix, \
+ S_IWUSR | S_IRUGO, \
+ SYSFS_CB_RD(Access), SYSFS_CB_WR(Access), Feat)
+
+/* WARNING: this macro hardwires one of 2 sysfs-callbacks (port-wide
+ vs bit-wide access) to each attr. This is broken, since each GPIO
+ driver (ie author) must determine whether each feature is exposed
+ per-pin or port-wide. ATM, they can only copy and modify this
+ macro. But then, theyre already wed to the nsc_gpio_ops ;-)
+*/
+#define GPIO_ATTRS(Port, Idx) { \
+ .value = GPIO_ATTR(Port, Idx, PF_CMD_VALUE, PF_SFX_VALUE, bitval), \
+ .curr = GPIO_ATTR(Port, Idx, PF_CMD_CURR, PF_SFX_CURR, bitval), \
+ .oe = GPIO_ATTR(Port, Idx, PF_CMD_OUT_ENA, PF_SFX_OUT_ENA, bitconf), \
+ .pue = GPIO_ATTR(Port, Idx, PF_CMD_PULLUP, PF_SFX_PULLUP, bitconf), \
+ .totem = GPIO_ATTR(Port, Idx, PF_CMD_TOTEM, PF_SFX_TOTEM, bitconf), \
+ .locked = GPIO_ATTR(Port, Idx, PF_CMD_LOCKED, PF_SFX_LOCKED, bitconf), \
+ .bounce = GPIO_ATTR(Port, Idx, PF_CMD_DEBOUNCE, PF_SFX_DEBOUNCE,bitconf), \
+ .status = GPIO_ATTR(Port, Idx, PF_CMD_STATUS, PF_SFX_STATUS, bitconf), \
+ .int_en = GPIO_ATTR(Port, Idx, PF_CMD_INT_ENA, PF_SFX_INT_ENA, bitconf), \
+ .int_lvl= GPIO_ATTR(Port, Idx, PF_CMD_INT_TRIG, PF_SFX_INT_TRIG,bitconf) \
+}
+
+/* port-wide sysfs access */
+
+#define GPIO_PORT(Port, Sfx, Mode, Show, Store, Cmd) \
+ { .dev_attr = __ATTR( port_ ## Port ## Sfx, \
+ Mode, Show, Store), \
+ .portnum = Port, \
+ .bitnum = -1, \
+ .fn_slct = Cmd, \
+ .bit_port_type = GPIO_PORT_TYPE }
+
+#define GPIO_PORT_ATTR(Portnum, Cmd, Suffix, Access) \
+ GPIO_PORT( Portnum, Suffix, \
+ S_IWUSR | S_IRUGO, \
+ SYSFS_CB_RD(Access), SYSFS_CB_WR(Access), Cmd)
+
+/* WARNING: same caveats as above apply, only moreso */
+
+#define GPIO_PORT_ATTRS(Port) { \
+ .value = GPIO_PORT_ATTR(Port, PF_CMD_VALUE, PF_SFX_VALUE, portval), \
+ .curr = GPIO_PORT_ATTR(Port, PF_CMD_CURR, PF_SFX_CURR, portval), \
+ .oe = GPIO_PORT_ATTR(Port, PF_CMD_OUT_ENA, PF_SFX_OUT_ENA, portconf), \
+ .pue = GPIO_PORT_ATTR(Port, PF_CMD_PULLUP, PF_SFX_PULLUP, portconf), \
+ .totem = GPIO_PORT_ATTR(Port, PF_CMD_TOTEM, PF_SFX_TOTEM, portconf), \
+ .locked = GPIO_PORT_ATTR(Port, PF_CMD_LOCKED, PF_SFX_LOCKED, portconf), \
+ .bounce = GPIO_PORT_ATTR(Port, PF_CMD_DEBOUNCE, PF_SFX_DEBOUNCE,portconf), \
+ .status = GPIO_PORT_ATTR(Port, PF_CMD_STATUS, PF_SFX_STATUS, portconf), \
+ .int_en = GPIO_PORT_ATTR(Port, PF_CMD_INT_ENA, PF_SFX_INT_ENA, portconf), \
+ .int_lvl= GPIO_PORT_ATTR(Port, PF_CMD_INT_TRIG, PF_SFX_INT_TRIG,portconf) \
+}
+
+/* attr-file callbacks, named per glob-name:
+ nsc_gpio_sysfs_{get,set}_{bit,port}{val,conf}
+
+ -bitval,portval separate for speed, simplicity
+ -bitconf is combo-callback, handling slower, per-pin properties
+ -portconf - synthesize port-wide from bit-wide
+ -get,set as required by fn-prototypes
+*/
+extern ssize_t nsc_gpio_sysfs_get_bitval(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+extern ssize_t nsc_gpio_sysfs_set_bitval(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count);
+
+extern ssize_t nsc_gpio_sysfs_get_portval(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+extern ssize_t nsc_gpio_sysfs_set_portval(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count);
+
+
+extern ssize_t nsc_gpio_sysfs_get_bitconf(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+extern ssize_t nsc_gpio_sysfs_set_bitconf(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count);
+
+extern ssize_t nsc_gpio_sysfs_get_portconf(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf);
+extern ssize_t nsc_gpio_sysfs_set_portconf(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count);
+
+
+/* device-create-file for all attrs, all bits in port.
+ called by higher level client _inits unless nobits=1.
+ Also called for ports-init !!
+*/
+extern void nsc_gpio_sysfs_bits_init(struct device* dev,
+ struct gpio_attributes pp[],
+ int numdevs);
+
+extern void nsc_gpio_sysfs_bits_fini(struct device* dev,
+ struct gpio_attributes pp[],
+ int numdevs);
+
diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/include/linux/scx200_gpio.h roll3-m2/include/linux/scx200_gpio.h
--- ../linux-2.6.18-rc3-mm2-sk/include/linux/scx200_gpio.h 2006-08-06 10:19:38.000000000 -0600
+++ roll3-m2/include/linux/scx200_gpio.h 2006-08-08 12:51:09.000000000 -0600
@@ -18,7 +18,7 @@ extern struct nsc_gpio_ops scx200_gpio_o
/* returns the value of the GPIO pin */
-static inline int scx200_gpio_get(unsigned index) {
+static inline u32 scx200_gpio_get(unsigned index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_IOADDR + 0x04;
__SCx200_GPIO_INDEX;
@@ -30,7 +30,7 @@ static inline int scx200_gpio_get(unsign
driven if the GPIO is configured as an output, it might not be the
state of the GPIO right now if the GPIO is configured as an input) */
-static inline int scx200_gpio_current(unsigned index) {
+static inline u32 scx200_gpio_current(unsigned index) {
__SCx200_GPIO_BANK;
__SCx200_GPIO_INDEX;
@@ -83,6 +83,30 @@ static inline void scx200_gpio_change(un
__SCx200_GPIO_OUT;
}
+/* return the value of a whole port (bank) */
+static inline u32 scx200_gpio_get_port(unsigned port)
+{
+ unsigned bank = port & 0x1f;
+ __SCx200_GPIO_IOADDR + 0x04;
+
+ return inl(ioaddr);
+}
+
+/* return the value of a whole port (bank) */
+static inline void scx200_gpio_setclr_port(unsigned port, u32 bits, u32 mask)
+{
+ unsigned bank = port & 0x1f;
+ __SCx200_GPIO_IOADDR;
+ __SCx200_GPIO_SHADOW;
+ *shadow = (*shadow & ~mask) | (bits & mask);
+ __SCx200_GPIO_OUT;
+}
+
+static inline void scx200_gpio_set_port(unsigned port, u32 bits)
+{
+ scx200_gpio_setclr_port(port, bits, ~bits);
+}
+
#undef __SCx200_GPIO_BANK
#undef __SCx200_GPIO_IOADDR
#undef __SCx200_GPIO_SHADOW
[-- Attachment #3: exer-gpio.sh --]
[-- Type: application/x-shellscript, Size: 2353 bytes --]
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2006-08-09 17:11 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-07-28 20:44 [RFC] Proposal: common kernel-wide GPIO interface Chris Boot
2006-07-29 19:41 ` Bill Davidsen
2006-07-30 13:08 ` Robert Schwebel
2006-07-30 22:02 ` Ben Dooks
2006-07-31 16:10 ` Chris Boot
2006-07-31 20:17 ` Robert Schwebel
2006-07-31 21:23 ` Chris Boot
2006-08-01 7:40 ` Juergen Beisert
2006-08-01 15:53 ` Jim Cromie
2006-08-01 21:25 ` Jim Cromie
2006-08-02 7:28 ` Robert Schwebel
2006-08-02 17:58 ` Lennart Sorensen
2006-08-02 20:48 ` Jim Cromie
2006-08-03 13:55 ` Lennart Sorensen
2006-08-03 15:42 ` Robert Schwebel
2006-08-08 23:01 ` [RFC - patch] add a gpio-sysfs interface - was: " Jim Cromie
2006-08-09 17:12 ` Jim Cromie
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).