From: Robert Schwebel <robert@schwebel.de>
To: Robert Schwebel <robert@schwebel.de>
Cc: John Lenz <lenz@cs.wisc.edu>, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 2.6.8.1 0/2] leds: new class for led devices
Date: Fri, 3 Sep 2004 15:31:52 +0200 [thread overview]
Message-ID: <20040903133152.GH1369@pengutronix.de> (raw)
In-Reply-To: <20040903131715.GG1369@pengutronix.de>
[-- Attachment #1: Type: text/plain, Size: 554 bytes --]
On Fri, Sep 03, 2004 at 03:17:15PM +0200, Robert Schwebel wrote:
> I'll pull the gpio patch out of my working tree and post it here for
> discussion.
Attached. Parts of it have been taken from your LEDs patch anyway, so it
should be not too difficult to reunify them.
Robert
--
Dipl.-Ing. Robert Schwebel | http://www.pengutronix.de
Pengutronix - Linux Solutions for Science and Industry
Handelsregister: Amtsgericht Hildesheim, HRA 2686
Hornemannstraße 12, 31137 Hildesheim, Germany
Phone: +49-5121-28619-0 | Fax: +49-5121-28619-4
[-- Attachment #2: gpio.diff --]
[-- Type: text/plain, Size: 15969 bytes --]
Index: kernel/gpio.c
===================================================================
--- a/kernel/gpio.c (revision 0)
+++ b/kernel/gpio.c (revision 205)
@@ -0,0 +1,459 @@
+/*
+ * linux/kernel/gpio.c
+ *
+ * (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.
+ */
+
+#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>
+
+#define DRIVER_NAME "gpio"
+
+static char mapping[255] = "";
+
+MODULE_AUTHOR("John Lenz, Robert Schwebel");
+MODULE_LICENSE("GPL");
+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: 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,
+};
+
+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;
+
+ if (gpio_dev->props.policy & GPIO_INPUT)
+ gpio_dev->props.pin_level = gpio_get_pin(gpio_dev->props.pin_nr);
+
+ spin_lock(&gpio_dev->lock);
+ ret_size += sprintf(buf, "%i\n", gpio_dev->props.pin_level);
+ spin_unlock(&gpio_dev->lock);
+
+ return ret_size;
+}
+
+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;
+
+ if (gpio_dev->props.policy & GPIO_INPUT)
+ return -EINVAL;
+
+ value = simple_strtol(buf, NULL, 10);
+ if ((value != 0) && (value != 1))
+ return -EINVAL;
+ 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;
+ }
+ 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,
+};
+
+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");
+ if (gpio_dev->props.policy & GPIO_KERNEL)
+ ret_size += sprintf(buf,"kernel\n");
+ spin_unlock(&gpio_dev->lock);
+
+ return ret_size;
+}
+
+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");
+ list_for_each_safe(lact, ltmp, &gpio_list) {
+ gpio_dev = list_entry(lact, struct gpio_device, list);
+ p += sprintf(p, "%3i: ", gpio_dev->props.pin_nr);
+ if (gpio_dev->props.policy & GPIO_KERNEL)
+ p += sprintf(p, "kernel ");
+ if (gpio_dev->props.policy & GPIO_USER)
+ p += sprintf(p, "user space ");
+ if (gpio_dev->props.policy & GPIO_INPUT)
+ p += sprintf(p, "input ");
+ if (gpio_dev->props.policy & GPIO_OUTPUT)
+ p += sprintf(p, "output ");
+ p += sprintf(p, "%s\n", gpio_dev->props.owner);
+ }
+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;
+
+ 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);
+ return -EBUSY;
+ }
+ }
+
+ /* value checks - FIXME: is there a logical xor in C? */
+ if ( ( (policy & GPIO_USER) && (policy & GPIO_KERNEL)) ||
+ (!(policy & GPIO_USER) && !(policy & GPIO_KERNEL))) {
+ printk(KERN_ERR "%s: policy has to be one of GPIO_KERNEL, GPIO_USER\n", DRIVER_NAME);
+ return -EINVAL;
+ }
+ if ( ( (policy & GPIO_INPUT) && (policy & GPIO_OUTPUT)) ||
+ (!(policy & GPIO_INPUT) && !(policy & GPIO_OUTPUT))) {
+ printk(KERN_ERR "%s: policy has to be one of GPIO_INPUT, GPIO_OUTPUT\n", DRIVER_NAME);
+ return -EINVAL;
+ }
+
+ gpio_dev = kmalloc(sizeof(struct gpio_device), GFP_KERNEL);
+
+ if (unlikely(!gpio_dev)) {
+ printk(KERN_ERR "%s: couldn't allocate memory\n", DRIVER_NAME);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&gpio_dev->lock);
+ gpio_dev->props.pin_nr = pin_nr;
+ 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;
+ }
+
+ INIT_LIST_HEAD(&gpio_dev->list);
+
+ /* 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 */
+ 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);
+ }
+
+ write_lock(&gpio_list_lock);
+ list_add_tail(&gpio_dev->list, &gpio_list);
+ write_unlock(&gpio_list_lock);
+
+ printk(KERN_INFO "registered gpio%i\n", pin_nr);
+
+ return 0;
+}
+EXPORT_SYMBOL(request_gpio);
+
+/**
+ * free_gpio - unregisters a 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;
+
+ 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);
+
+ class_device_remove_file(&gpio_dev->class_dev, &attr_gpio_level);
+
+ class_device_unregister(&gpio_dev->class_dev);
+ write_lock(&gpio_list_lock);
+ list_del(&gpio_dev->list);
+ write_unlock(&gpio_list_lock);
+ kfree(gpio_dev);
+ return;
+ }
+ }
+}
+EXPORT_SYMBOL(free_gpio);
+
+void free_all_gpios(void)
+{
+ struct gpio_device *gpio_dev;
+ struct list_head *lact, *ltmp;
+
+ list_for_each_safe(lact, ltmp, &gpio_list) {
+ gpio_dev = list_entry(lact, struct gpio_device, list);
+
+ printk(KERN_INFO "unregistering gpio pin %i\n", gpio_dev->props.pin_nr);
+
+ /* unregister attributes */
+ if (gpio_dev->props.policy & GPIO_USER)
+ class_device_remove_file(&gpio_dev->class_dev,
+ &attr_gpio_level);
+
+ class_device_remove_file(&gpio_dev->class_dev, &attr_gpio_level);
+
+ class_device_unregister(&gpio_dev->class_dev);
+ write_lock(&gpio_list_lock);
+ list_del(&gpio_dev->list);
+ write_unlock(&gpio_list_lock);
+ kfree(gpio_dev);
+ return;
+ }
+}
+EXPORT_SYMBOL(free_all_gpios);
+
+static struct sysdev_class gpio_sysclass = {
+ set_kset_name("gpio"),
+};
+
+static struct sys_device gpio_sys_device = {
+ .id = 0,
+ .cls = &gpio_sysclass,
+};
+
+static int gpio_setup(char *s)
+{
+ char *p, *q = NULL;
+ int pin_nr;
+ unsigned char policy;
+ unsigned char init_level = 0;
+
+ while ((p = strsep(&s, ".,")) != NULL) {
+
+ /* end of command reached? -> next command */
+ if (*p == '\0')
+ continue;
+
+ /* process one command */
+ pin_nr = -1;
+ policy = 0;
+ q = strsep(&p, ":");
+ if (*q == '\0') {
+ printk("%s: invalid token (scanning pin_nr): %s\n", DRIVER_NAME, p);
+ continue;
+ }
+ pin_nr = simple_strtoul(q, NULL, 0); /* FIXME: this doesn't detect if no number is given! */
+ q = strsep(&p, ":");
+ if (*q == '\0') {
+ printk("%s: invalid token (scanning direction): %s\n", DRIVER_NAME, p);
+ continue;
+ }
+ policy |= strncmp(q, "in", strlen(q)) == 0 ? GPIO_INPUT : 0;
+ policy |= strncmp(q, "out", strlen(q)) == 0 ? GPIO_OUTPUT : 0;
+ if (policy == 0) {
+ printk("%s: invalid token (scanning policy): %s\n", DRIVER_NAME, p);
+ continue;
+ }
+ if (policy & GPIO_OUTPUT) {
+ q = strsep(&p, ":");
+ if (*q == '\0') {
+ printk("%s: invalid token (scanning init_level): %s\n", DRIVER_NAME, p);
+ continue;
+ }
+ init_level = 2;
+ if (strncmp(q, "hi", strlen(q)) == 0)
+ init_level = 1;
+ if (strncmp(q, "lo", strlen(q)) == 0)
+ init_level = 0;
+ if (init_level == 2) {
+ printk("%s: invalid token (scanning level_value): %s\n", DRIVER_NAME, p);
+ continue;
+ }
+ }
+ policy |= GPIO_USER;
+ if (request_gpio(pin_nr, "kernel", policy, init_level) != 0) {
+ printk(KERN_ERR
+ "%s: could not register GPIO pins from commandline!\n",
+ DRIVER_NAME);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+static int __init gpio_init(void)
+{
+ int ret;
+
+ printk(KERN_INFO "Initialising gpio device class.\n");
+
+ class_register(&gpio_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;
+ }
+
+ if (!create_proc_read_entry ("gpio", 0, 0, gpio_read_proc, NULL)) {
+ printk(KERN_ERR "%s: couldn't register proc entry, exiting\n", DRIVER_NAME);
+ goto out_proc;
+ }
+
+ /* parse command line parameters */
+ printk(KERN_INFO "%s: mapping=", DRIVER_NAME);
+ if (strcmp(mapping,"")) {
+ printk("%s\n", mapping);
+ if (gpio_setup(mapping) != 0) {
+ printk(KERN_ERR "%s: could not register ('mapping=...'), exiting\n", DRIVER_NAME);
+ }
+ } else {
+ printk("EMPTY\n");
+ };
+
+ return ret;
+
+out_proc:
+ sysdev_unregister(&gpio_sys_device);
+out_sysdev_register:
+ sysdev_class_unregister(&gpio_sysclass);
+out_sysdev_class:
+ class_unregister(&gpio_class);
+ return ret;
+}
+
+module_init(gpio_init);
Index: kernel/Makefile
===================================================================
--- a/kernel/Makefile (.../vanilla/linux-2.6.8-rc2) (revision 205)
+++ b/kernel/Makefile (.../linux-pxa/trunks/linux-2.6.8-rc2-trunk) (revision 205)
@@ -23,6 +23,7 @@
obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
obj-$(CONFIG_AUDIT) += audit.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
+obj-$(CONFIG_GPIO) += gpio.o
ifneq ($(CONFIG_IA64),y)
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
Index: include/asm-arm/arch-pxa/gpio.h
===================================================================
--- a/include/asm-arm/arch-pxa/gpio.h (revision 0)
+++ b/include/asm-arm/arch-pxa/gpio.h (revision 205)
@@ -0,0 +1,49 @@
+/*
+ * 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>
+
+#define DEBUG 1
+
+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
Index: include/linux/gpio.h
===================================================================
--- a/include/linux/gpio.h (revision 0)
+++ b/include/linux/gpio.h (revision 205)
@@ -0,0 +1,27 @@
+/*
+ * 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_KERNEL (1<<0)
+#define GPIO_USER (1<<1)
+#define GPIO_INPUT (1<<2)
+#define GPIO_OUTPUT (1<<3)
+
+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: init/Kconfig
===================================================================
--- a/init/Kconfig (.../vanilla/linux-2.6.8-rc2) (revision 205)
+++ b/init/Kconfig (.../linux-pxa/trunks/linux-2.6.8-rc2-trunk) (revision 205)
@@ -294,6 +294,18 @@
If unsure, say N.
+config GPIO
+ bool "GPIO pin support"
+ default y if ARM || PPC
+ default n
+ help
+ Enabling this option adds support for generic GPIO pins. Most
+ System-on-Chip processors have this kind of pins.
+
+ FIXME: write more documentation.
+
+ If unsure, say N.
+
endmenu # General setup
next prev parent reply other threads:[~2004-09-03 7:34 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-09-02 20:33 [PATCH 2.6.8.1 0/2] leds: new class for led devices John Lenz
2004-09-02 20:34 ` [PATCH 2.6.8.1 1/2] " John Lenz
2004-09-02 20:38 ` [PATCH 2.6.8.1 2/2] " John Lenz
2004-09-03 4:54 ` [PATCH 2.6.8.1 0/2] " Kalin KOZHUHAROV
2004-09-03 11:32 ` Geert Uytterhoeven
2004-09-03 12:06 ` Jan-Benedict Glaw
2004-09-03 18:47 ` John Lenz
2004-09-03 22:25 ` Russell King
2004-09-03 23:19 ` John Lenz
2004-09-04 11:12 ` Pavel Machek
2004-09-04 20:53 ` Russell King
2004-09-04 21:41 ` Pavel Machek
2004-09-06 7:36 ` John Lenz
2004-09-03 13:17 ` Robert Schwebel
2004-09-03 13:31 ` Robert Schwebel [this message]
2004-09-03 8:00 ` Oliver Neukum
2004-09-03 13:51 ` Pavel Machek
2004-09-03 18:38 ` John Lenz
2004-09-03 18:55 ` Russell King
2004-09-03 19:09 ` John Lenz
2004-09-03 19:51 ` Russell King
2004-09-03 20:35 ` John Lenz
2004-09-04 11:09 ` Pavel Machek
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20040903133152.GH1369@pengutronix.de \
--to=robert@schwebel.de \
--cc=lenz@cs.wisc.edu \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.