Messages in this thread Patch in this message | | | Date | Tue, 18 Jul 2006 11:45:03 -0600 | From | Jim Cromie <> | Subject | [RFC-patch] add GPIO-Sysfs interface to scx200_gpio, pc8736x_gpio |
| |
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 majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |