[RFC-patch] add GPIO-Sysfs interface to scx200_gpio, pc8736x_gpio

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

 




heres a rough, RFC, proto-patch  which adds a GPIO-sysfs interface
into scx200_gpio, and pc8736x_gpio.

Im going on vacation, and thought I might solicit an inbox full of bright ideas
to bring me back to (a much sharper) focus upon my return.

heres a bit of testing which raises a few questions:
$> cat remod
#!/bin/bash
loudly() {
   echo
   echo running: $*
   eval $*
}
rmmod pc8736x_gpio
modprobe pc8736x_gpio
cd ~/pinlab
check123
#loudly cat /dev/led
#loudly cat /dev/gpio-17
loudly cat /sys/devices/platform/scx200_gpio.0/bit_0.20_value
loudly cat /sys/devices/platform/pc8736x_gpio.0/bit_2.1_value
loudly scansimple -d /dev/led
loudly scansimple -d /dev/gpio-17
loudly scansimple -d /sys/devices/platform/scx200_gpio.0/bit_0.20_value
loudly scansimple -d /sys/devices/platform/pc8736x_gpio.0/bit_2.1_value


soekris:~/pinlab# remod
[ 5729.928000] lockd: cannot monitor 192.168.42.1
[ 5729.932000] lockd: failed to monitor 192.168.42.1
[ 5729.940000] lockd: cannot monitor 192.168.42.1
[ 5729.944000] lockd: failed to monitor 192.168.42.1
[ 5730.468000] platform pc8736x_gpio.0: NatSemi pc8736x GPIO Driver Initializing
[ 5730.508000] platform pc8736x_gpio.0: GPIO ioport 6600 reserved
[ 5730.512000] platform pc8736x_gpio.0: pc8736x_sysfs_init creating pin-attr nodes
[ 5730.532000] platform pc8736x_gpio.0: creating gpio port-attrs
/dev/gpio-[0..31]: 11111111-10001011-01111001-10111000
/dev/scxio-[0..31]: 11100000-00001110-01000000-00000000

running: cat /sys/devices/platform/scx200_gpio.0/bit_0.20_value
[ 5732.836000] platform scx200_gpio.0: nsc_gpio_sysfs_get(V) = 0
0

running: cat /sys/devices/platform/pc8736x_gpio.0/bit_2.1_value
[ 5732.864000] platform pc8736x_gpio.0: nsc_gpio_sysfs_get(V) = 1
1

running: scansimple -d /dev/led
opened /dev/led, for 1 loops, 1000000 samples
read 1000000 samples in 4.5966 sec, rate: 217552.5750 samples/sec

running: scansimple -d /dev/gpio-17
opened /dev/gpio-17, for 1 loops, 1000000 samples
read 1000000 samples in 6.5261 sec, rate: 153230.0295 samples/sec

running: scansimple -d /sys/devices/platform/scx200_gpio.0/bit_0.20_value
opened /sys/devi[ 5744.104000] platform scx200_gpio.0: nsc_gpio_sysfs_get(V) = 0
ces/platform/scx200_gpio.0/bit_0.20_value, for 1 loops, 1000000 samples
read 1000000 samples in 3.8179 sec, rate: 261926.3556 samples/sec

running: scansimple -d /sys/devices/platform/pc8736x_gpio.0/bit_2.1_value
opened /sys/devi[ 5747.956000] platform pc8736x_gpio.0: nsc_gpio_sysfs_get(V) = 1
ces/platform/pc8736x_gpio.0/bit_2.1_value, for 1 loops, 1000000 samples
read 1000000 samples in 3.5392 sec, rate: 282549.9708 samples/sec


Q1- do the device-file read rates look reasonable ?
   217k reads/sec, to the led ( a device-file to scx200-gpio )
   153k reads/sec to pc8736x_gpio

# cat /proc/cpuinfo
processor       : 0
vendor_id       : Geode by NSC
cpu family      : 5
model           : 9
model name      : Unknown
stepping        : 1
cpu MHz         : 266.648

Roughly, I think this looks ok, the scx is on-chip gpio, on pci interface, other is isa bus.
However, I dont have experience to compare it against.


Q2 - my sysfs interface looks broken ?

- its too fast
   250k reads/sec to sysfs-interface to pc8736x_gpio.
   277k to scx200_gpio

- its evidently not re-reading the pin, this appears just once, not repeatedly.
   [ 3534.552000] platform scx200_gpio.0: nsc_gpio_sysfs_get(V) = 1


Q3 - why does cat /dev/led read forever ?
   A- no EOF, and cat re-reads til EOF.
   also - no newline, read() length always 1 (later-Q)


Q4 - I added an lseek call to scansimple.

   it slows down the read-rate. (about 1/2 speed)
   it *doesnt* seem to re-trigger the underlying accessor.

running: scansimple -b -d /sys/devices/platform/scx200_gpio.0/bit_0.20_value
opened /sys/devi[ 6117.828000] platform scx200_gpio.0: nsc_gpio_sysfs_get(V) = 0
ces/platform/scx200_gpio.0/bit_0.20_value, for 1 loops, 1000000 samples
read 1000000 samples in 7.2937 sec, rate: 137104.5667 samples/sec

running: scansimple -b -d /sys/devices/platform/pc8736x_gpio.0/bit_2.1_value
opened /sys/devi[ 6125.160000] platform pc8736x_gpio.0: nsc_gpio_sysfs_get(V) = 1
ces/platform/pc8736x_gpio.0/bit_2.1_value, for 1 loops, 1000000 samples
read 1000000 samples in 7.1696 sec, rate: 139476.9601 samples/sec

Q5 - should I be looking for pollable sysfs ?
If so, can someone recommend a best-practice, nearest-fit module I can review ?

Q6 - my sysfs callbacks are appending newline.
   - cargo cult (just copying from somewhere)
   - is it proper ?  (looking for commonality)

Q7 - WRT gpio_ops, Ive s/u8/u32/ most params & returntypes, assuming that
   - is cost-less - (all stack args are full word, never byte)
   - casting up from u8 to u32 is fine. (no baroque type games)

Q8 - its tempting to add a 'command' attr,
   - which responds to writes identically to the device-file interface
   - reads return the string produced by gpio_dump.
   - defensible as a compatibility-hack (or not?)


tia
- jimc

ps - theres (no doubt) plenty thats not working..


diff -ruNp -X dontdiff -X exclude-diffs ag-0/drivers/char/nsc_gpio.c ag-1/drivers/char/nsc_gpio.c
--- ag-0/drivers/char/nsc_gpio.c	2006-07-15 01:08:50.000000000 -0600
+++ ag-1/drivers/char/nsc_gpio.c	2006-07-18 09:11:12.000000000 -0600
@@ -13,10 +13,12 @@
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
#include <linux/nsc_gpio.h>
#include <linux/platform_device.h>
+#include <linux/io.h>
#include <asm/uaccess.h>
-#include <asm/io.h>

#define NAME "nsc_gpio"

@@ -39,62 +41,79 @@ void nsc_gpio_dump(struct nsc_gpio_ops *
		 amp->gpio_get(index), amp->gpio_current(index));
}

+/* the pin-mode-change 'commands' of the legacy device-file-interface,
+   now refactored for reuse by a (tbd) sysfs-interface.  Includes some
+   updates which arent (currently;-) exposed via sysfs.
+*/
+static int common_write (struct nsc_gpio_ops *amp, char c, unsigned m)
+{
+	struct device *dev = amp->dev;
+	int err = 0;
+	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 'c':
+		/* view pin's current values: driven and read */
+		dev_info(dev, "io%02d: driven %d, input %d\n",
+		m, amp->gpio_current(m), amp->gpio_get(m));
+		break;
+	case '\n':
+		/* end of settings string, do nothing */
+		break;
+	default:
+		dev_err(dev, "GPIO-%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,
		       size_t len, loff_t *ppos)
{
	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 += common_write(amp, c, m);
	}
	if (err)
		return -EINVAL;	/* full string handled, report error */
@@ -102,7 +121,7 @@ ssize_t nsc_gpio_write(struct file *file
	return len;
}

-ssize_t nsc_gpio_read(struct file *file, char __user * buf,
+ssize_t nsc_gpio_read(struct file *file, char __user *buf,
		      size_t len, loff_t * ppos)
{
	unsigned m = iminor(file->f_dentry->d_inode);
@@ -121,8 +140,121 @@ EXPORT_SYMBOL(nsc_gpio_write);
EXPORT_SYMBOL(nsc_gpio_read);
EXPORT_SYMBOL(nsc_gpio_dump);

+/* now sysfs versions.  We use separate read and write callbacks,
+   which use 2D addressing, on index & nr, to select the bit-numbers
+   and pin-features
+   Slightly complicating things, these declarations must be made in
+   the client modules of this one, ie scx200 & pc8736x _gpio.  They
+   also must set device.driver_data to amp, as thats needed by
+   sysfs_set_value()
+*/
+ssize_t nsc_gpio_sysfs_set(struct device *dev,
+			   struct device_attribute *devattr, const char *buf,
+			   size_t count)
+{
+	struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	int idx = attr->index;
+	int func = attr->nr;
+	struct nsc_gpio_ops *amp = dev->driver_data;
+
+	int err = common_write(amp, func, idx);
+	if (err)
+		return -EINVAL;	/* full string handled, report error */
+	
+	return strlen(buf);
+}
+
+ssize_t nsc_gpio_sysfs_get(struct device *dev,
+			   struct device_attribute *devattr, char *buf)
+{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+	int idx = attr->index;
+	int func = attr->nr;
+	unsigned res = -1;
+	u32 config;
+	struct nsc_gpio_ops *amp = dev->driver_data;
+
+	if (!amp) {
+		dev_err(dev, "nsc_gpio_sysfs_get(), no amp\n");
+		return 0;
+	}
+	/* fetch value - fast path */
+	if (func == 'V') {
+		res = amp->gpio_get(idx);
+		dev_warn(dev, "nsc_gpio_sysfs_get(V) = %d\n", res);
+		return sprintf(buf, "%u\n", !!res);
+	}
+
+	config = amp->gpio_config(idx, ~0, 0);
+
+	dev_dbg(dev, "nsc_gpio_sysfs_get(), bitconf=%02x, cmd='%c'\n",
+		config, func);
+
+	switch ((char)func) {
+	case 'V':
+		dev_warn(dev, "shouldnt get here (V) = %d, %d\n", res, config);
+		break;
+	case 'O':
+		res = config & 1;
+		break;
+	case 'P':
+		res = config & 2;
+		break;
+	case 'T':
+		res = config & 4;
+		break;
+	case 'L':
+		res = config & 8;
+		break;
+	case 'D':
+		res = config & PF_DEBOUNCE;
+		break;
+
+	default:
+		dev_err(dev, "unknown cmd '%c'\n", func);
+	}
+	dev_dbg(dev, "bit[%d].cmd('%c')\n", idx, func);
+
+	return sprintf(buf, "%u\n", !!res);
+}
+
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get);
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set);
+
+void nsc_gpio_sysfs_port_init(struct device* dev,
+			      struct gpio_attributes pp[],
+			      int numdevs)
+{
+	int i, err = 0;
+
+	for (i = 0; i < numdevs; i++) {
+
+		err = device_create_file(dev, &pp[i].value.dev_attr);
+		if (err) dev_err(dev, "got err %d\n", err);
+
+		err = device_create_file(dev, &pp[i].output_enabled.dev_attr);
+		if (err) dev_err(dev, "got err %d\n", err);
+
+		err = device_create_file(dev, &pp[i].pullup_enabled.dev_attr);
+		if (err) dev_err(dev, "got err %d\n", err);
+
+		err = device_create_file(dev, &pp[i].totem_pole.dev_attr);
+		if (err) dev_err(dev, "got err %d\n", err);
+
+		err = device_create_file(dev, &pp[i].locked.dev_attr);
+		if (err) dev_err(dev, "got err %d\n", err);
+
+		err = device_create_file(dev, &pp[i].debounced.dev_attr);
+		if (err) dev_err(dev, "got err %d\n", err);
+
+	}
+}
+
+EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_port_init);
+
static int __init nsc_gpio_init(void)
{
+	// struct device *dev;
	printk(KERN_DEBUG NAME " initializing\n");
	return 0;
}
diff -ruNp -X dontdiff -X exclude-diffs ag-0/drivers/char/pc8736x_gpio.c ag-1/drivers/char/pc8736x_gpio.c
--- ag-0/drivers/char/pc8736x_gpio.c	2006-07-15 01:08:50.000000000 -0600
+++ ag-1/drivers/char/pc8736x_gpio.c	2006-07-18 09:21:19.000000000 -0600
@@ -18,6 +18,8 @@
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/mutex.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
#include <linux/nsc_gpio.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
@@ -32,6 +34,14 @@ static int major;		/* default to dynamic
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");
+
static DEFINE_MUTEX(pc8736x_gpio_config_lock);
static unsigned pc8736x_gpio_base;
static u8 pc8736x_gpio_shadow[4];
@@ -144,7 +154,7 @@ static u32 pc8736x_gpio_configure(unsign
					 SIO_GPIO_PIN_CONFIG);
}

-static int pc8736x_gpio_get(unsigned minor)
+static u32 pc8736x_gpio_get(unsigned minor)
{
	int port, bit, val;

@@ -188,16 +198,6 @@ static void pc8736x_gpio_set(unsigned mi
	pc8736x_gpio_shadow[port] = val;
}

-static void pc8736x_gpio_set_high(unsigned index)
-{
-	pc8736x_gpio_set(index, 1);
-}
-
-static void pc8736x_gpio_set_low(unsigned index)
-{
-	pc8736x_gpio_set(index, 0);
-}
-
static int pc8736x_gpio_current(unsigned minor)
{
	int port, bit;
@@ -212,6 +212,37 @@ static void pc8736x_gpio_change(unsigned
	pc8736x_gpio_set(index, !pc8736x_gpio_current(index));
}

+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 void pc8736x_gpio_set_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,
@@ -219,7 +250,9 @@ 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,
+	.gpio_get_port	= pc8736x_gpio_get_port,
+	.gpio_set_port	= pc8736x_gpio_set_port,
};
EXPORT_SYMBOL(pc8736x_gpio_ops);

@@ -242,16 +275,52 @@ static const struct file_operations pc87
	.read	= nsc_gpio_read,
};

+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)
+{
+	dev_info(dev, "pc8736x_sysfs_init creating pin-attr nodes\n");
+	if (!nobits) {
+		nsc_gpio_sysfs_port_init(&pdev->dev, port0, 8);
+		nsc_gpio_sysfs_port_init(&pdev->dev, port1, 8);
+		nsc_gpio_sysfs_port_init(&pdev->dev, port2, 8);
+		nsc_gpio_sysfs_port_init(&pdev->dev, port3, 8);
+	}
+	if (!noports) {
+		dev_info(dev, "creating gpio port-attrs\n");
+		nsc_gpio_sysfs_port_init(&pdev->dev, ports, 4);
+	}
+}
+
static void __init pc8736x_init_shadow(void)
{
	int port;
-
+	
	/* read the current values driven on the GPIO signals */
	for (port = 0; port < 4; ++port)
		pc8736x_gpio_shadow[port]
-		    = inb_p(pc8736x_gpio_base + port_offset[port]
-			    + PORT_OUT);
-
+			= inb_p(pc8736x_gpio_base + port_offset[port]
+				+ PORT_OUT);
}

static struct cdev pc8736x_gpio_cdev;
@@ -330,6 +399,11 @@ static int __init pc8736x_gpio_init(void
	cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops);
	cdev_add(&pc8736x_gpio_cdev, devid, PC8736X_GPIO_CT);

+	pc8736x_sysfs_init(&pdev->dev);
+
+	/* provide info wheresysfs callbacks can get them */
+	pc8736x_gpio_ops.dev->driver_data = &pc8736x_gpio_ops;
+
	return 0;

undo_request_region:
diff -ruNp -X dontdiff -X exclude-diffs ag-0/drivers/char/scx200_gpio.c ag-1/drivers/char/scx200_gpio.c
--- ag-0/drivers/char/scx200_gpio.c	2006-07-15 01:08:50.000000000 -0600
+++ ag-1/drivers/char/scx200_gpio.c	2006-07-18 09:21:25.000000000 -0600
@@ -33,6 +33,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,9 +50,11 @@ 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,
+	.gpio_get_port	= scx200_gpio_get_port,
+	.gpio_set_port	= scx200_gpio_set_port,
};
-EXPORT_SYMBOL(scx200_gpio_ops);
+EXPORT_SYMBOL_GPL(scx200_gpio_ops);

static int scx200_gpio_open(struct inode *inode, struct file *file)
{
@@ -69,7 +79,45 @@ static const struct file_operations scx2
	.release = scx200_gpio_release,
};

-struct cdev scx200_gpio_cdev;  /* use 1 cdev for all pins */
+static struct cdev scx200_gpio_cdev;  /* use 1 cdev for all pins */
+
+/* insert sysfs decl and init-func here */
+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_port_init(dev, port0, 32);
+		nsc_gpio_sysfs_port_init(dev, port1, 32);
+	}
+	if (!noports)
+		nsc_gpio_sysfs_port_init(dev, ports, 2);
+}

static int __init scx200_gpio_init(void)
{
@@ -92,6 +140,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 +157,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:
diff -ruNp -X dontdiff -X exclude-diffs ag-0/drivers/leds/leds-net48xx.c ag-1/drivers/leds/leds-net48xx.c
--- ag-0/drivers/leds/leds-net48xx.c	2006-07-15 01:08:58.000000000 -0600
+++ ag-1/drivers/leds/leds-net48xx.c	2006-07-17 13:28:49.000000000 -0600
@@ -16,6 +16,7 @@
#include <linux/leds.h>
#include <linux/err.h>
#include <asm/io.h>
+#include <linux/nsc_gpio.h>
#include <linux/scx200_gpio.h>

#define DRVNAME "net48xx-led"
@@ -26,10 +27,7 @@ static struct platform_device *pdev;
static void net48xx_error_led_set(struct led_classdev *led_cdev,
		enum led_brightness value)
{
-	if (value)
-		scx200_gpio_set_high(NET48XX_ERROR_LED_GPIO);
-	else
-		scx200_gpio_set_low(NET48XX_ERROR_LED_GPIO);
+	scx200_gpio_ops.gpio_set(NET48XX_ERROR_LED_GPIO, value ? 1 : 0);
}

static struct led_classdev net48xx_error_led = {
@@ -81,7 +79,8 @@ static int __init net48xx_led_init(void)
{
	int ret;

-	if (!scx200_gpio_present()) {
+	/* small hack, but scx200_gpio doesn't set .dev if the probe fails */
+	if (!scx200_gpio_ops.dev) {
		ret = -ENODEV;
		goto out;
	}
diff -ruNp -X dontdiff -X exclude-diffs ag-0/include/linux/nsc_gpio.h ag-1/include/linux/nsc_gpio.h
--- ag-0/include/linux/nsc_gpio.h	2006-07-15 01:09:44.000000000 -0600
+++ ag-1/include/linux/nsc_gpio.h	2006-07-18 09:17:30.000000000 -0600
@@ -1,6 +1,4 @@
/**
-   nsc_gpio.c
-
   National Semiconductor GPIO common access methods.

   struct nsc_gpio_ops abstracts the low-level access
@@ -19,17 +17,31 @@
   NSC sold the GEODE line to AMD, and the PC-8736x line to Winbond.
*/

+/* pin-feature to config-bit mapping is common to both chips
+   some ports' pins dont support upper nibble ops.
+*/
+#define PF_OUTPUT_ENA		1	/* !tristate */
+#define PF_TOTEM		2	/* !open-drain */
+#define PF_PULLUP		4
+#define PF_LOCKED		8
+#define PF_INTERRUPT_ENA	16
+#define PF_INTERRUPT_TGR	32
+#define PF_DEBOUNCE		64
+
struct nsc_gpio_ops {
	struct module*	owner;
-	u32	(*gpio_config)	(unsigned iminor, u32 mask, u32 bits);
-	void	(*gpio_dump)	(struct nsc_gpio_ops *amp, unsigned iminor);
-	int	(*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_config)		(unsigned iminor, u32 mask, u32 bits);
+	void	(*gpio_dump)		(struct nsc_gpio_ops *amp, unsigned iminor);
+	u32	(*gpio_get)		(unsigned iminor);
+	void	(*gpio_set)		(unsigned iminor, int state);
+	void	(*gpio_change)		(unsigned iminor);
+	int	(*gpio_current)		(unsigned iminor);
+	u32	(*gpio_get_port)	(unsigned port);
+	void	(*gpio_set_port)	(unsigned port, u32 bits, u32 mask);
+	struct device*	dev;		/* for dev_dbg() support */
};

+/* fileops routines */
extern ssize_t nsc_gpio_write(struct file *file, const char __user *data,
			      size_t len, loff_t *ppos);

@@ -38,3 +50,95 @@ extern ssize_t nsc_gpio_read(struct file

extern void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index);

+
+/* the 'bits' sysfs interface uses a single/combo callback function
+   which does 2D indexing; on index & nr, selecting bit number and
+   pin-feature/attribute
+*/
+extern ssize_t nsc_gpio_sysfs_get(struct device *dev,
+				  struct device_attribute *devattr,
+				  char *buf);
+
+extern ssize_t nsc_gpio_sysfs_set(struct device *dev,
+				  struct device_attribute *devattr,
+				  const char *buf, size_t count);
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+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;
+};
+
+#define GPIO_PIN(_grp, _idx, _pre, _post, _mode, _show, _store, _nr)	\
+	{	.dev_attr = __ATTR(_pre## _grp._idx ##_post,		\
+				   _mode, _show, _store),		\
+		.index = _idx, .nr = _nr }
+
+#define GPIO_ATTR(Portnum, Bitnum, FnSym, AttrNm)	\
+	GPIO_PIN(Portnum, Bitnum, bit_, AttrNm,		\
+		 S_IWUSR | S_IRUGO,			\
+		 nsc_gpio_sysfs_get, nsc_gpio_sysfs_set, FnSym )
+
+/* The original scx200_gpio driver accessed pins via device-files.
+   and set pin features in response to command-strings written to it.
+*/
+#define PIN_VAL(Port, Bit)	GPIO_ATTR(Port, Bit, 'V', _value)
+#define PIN_CURR(Port, Bit)	GPIO_ATTR(Port, Bit, 'C', _current_output)
+#define PIN_OE(Port, Bit)	GPIO_ATTR(Port, Bit, 'O', _output_enabled)
+#define PIN_PUE(Port, Bit)	GPIO_ATTR(Port, Bit, 'P', _pullup_enabled)
+#define PIN_PP(Port, Bit)	GPIO_ATTR(Port, Bit, 'T', _totem)
+#define PIN_LOCKED(Port, Bit)	GPIO_ATTR(Port, Bit, 'L', _locked)
+#define PIN_DEBOUNCE(Port, Bit)	GPIO_ATTR(Port, Bit, 'D', _debounced)
+
+/* initializer for bits, ie pin arrays */
+#define GPIO_ATTRS(Port, Idx) {			\
+	.value		= PIN_VAL(Port, Idx),	\
+	.curr		= PIN_CURR(Port, Idx),	\
+	.output_enabled	= PIN_OE(Port, Idx),	\
+	.pullup_enabled	= PIN_PUE(Port, Idx),	\
+	.totem_pole	= PIN_PP(Port, Idx),	\
+	.locked		= PIN_LOCKED(Port, Idx), \
+	.debounced	= PIN_DEBOUNCE(Port, Idx) }
+
+extern void nsc_gpio_sysfs_port_init(struct device* dev,
+				     struct gpio_attributes pp[],
+				     int numdevs);
+
+
+
+/* port-wide sysfs access */
+
+#define GPIO_PORT(_grp, _pre, _post, _mode, _show, _store, _nr)	\
+	{	.dev_attr = __ATTR(_pre## _grp ##_post,		\
+				   _mode, _show, _store),	\
+		.index = _grp, .nr = _nr }
+
+#define GPIO_PORT_ATTR(Portnum, FnSym, AttrNm)	\
+	GPIO_PORT(Portnum, port_, AttrNm,	\
+		  S_IWUSR | S_IRUGO,		\
+		  nsc_gpio_sysfs_get, nsc_gpio_sysfs_set, FnSym )
+
+#define PORT_VAL(Port)		GPIO_PORT_ATTR(Port, 'V', _value)
+#define PORT_CURR(Port)		GPIO_PORT_ATTR(Port, 'C', _current_output)
+#define PORT_OE(Port)		GPIO_PORT_ATTR(Port, 'O', _output_enabled)
+#define PORT_PUE(Port)		GPIO_PORT_ATTR(Port, 'P', _pullup_enabled)
+#define PORT_PP(Port)		GPIO_PORT_ATTR(Port, 'T', _totem)
+#define PORT_LOCKED(Port)	GPIO_PORT_ATTR(Port, 'L', _locked)
+#define PORT_DEBOUNCE(Port)	GPIO_PORT_ATTR(Port, 'D', _debounced)
+
+#define GPIO_PORT_ATTRS(Port) {			\
+	.value		= PORT_VAL(Port),	\
+	.curr		= PORT_CURR(Port),	\
+	.output_enabled	= PORT_OE(Port),	\
+	.pullup_enabled	= PORT_PUE(Port),	\
+	.totem_pole	= PORT_PP(Port),	\
+	.locked		= PORT_LOCKED(Port),	\
+	.debounced	= PORT_DEBOUNCE(Port) }
+
diff -ruNp -X dontdiff -X exclude-diffs ag-0/include/linux/scx200_gpio.h ag-1/include/linux/scx200_gpio.h
--- ag-0/include/linux/scx200_gpio.h	2006-07-06 13:20:28.000000000 -0600
+++ ag-1/include/linux/scx200_gpio.h	2006-07-17 23:04:38.000000000 -0600
@@ -4,6 +4,7 @@ u32 scx200_gpio_configure(unsigned index

extern unsigned scx200_gpio_base;
extern long scx200_gpio_shadow[2];
+extern struct nsc_gpio_ops scx200_gpio_ops;

#define scx200_gpio_present() (scx200_gpio_base!=0)

@@ -82,6 +83,26 @@ 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_set_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;
+}
+
+
#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