[RFC - patch] add a gpio-sysfs interface - was: Proposal: common kernel-wide GPIO interface

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

 



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 <[email protected]>
*/

+#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 <[email protected]>,
*/

+#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 <[email protected]>");
-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 <[email protected]>");
+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 <[email protected]> */
+   Copyright (c) 2001,2002 Christer Weinigel <[email protected]>
+*/
+
+#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 <[email protected]>");
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 <[email protected]>
+   Copyright (c) 2005,2006 Jim Cromie <[email protected]>

   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


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

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