lkml.org 
[lkml]   [2006]   [Aug]   [8]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[RFC - patch] add a gpio-sysfs interface - was: Proposal: common kernel-wide GPIO interface
    Jim Cromie wrote:
    >
    > nsc_gpio, pc8736x_gpio , scx200_gpio went thru mm into mainline-rc -
    > they support the legacy gpio-bit
    > access via char-device-file. They expose port-wide read/write inside
    > the kernel,

    correction - in-vtable portwide-accessors are in a to-be-applied patch
    from Chris Boot to me.
    Its in-my-queue for post 18, since we're now rc.


    Im attaching a newer version of a patch sent earlier this thread.

    It adds a sysfs-gpio interface, for both bits & ports.
    - same feature-set for both.
    - tried port-synthesis -
    for pin-features that are implemented as per-bit only,
    do set of queries/updates, and bundle them for presentation.
    (having explored this, Im hedging ..)

    the set of pin_* and port_* attributes are supported by 8 sysfs-callbacks,
    split on following axes: bit/port, value/config, read/write (always,
    diff fn-sigs).
    *_value callbacks are simple/fast
    both my gpio chips are port-based, bit-wide methods add masking
    *_conf callbacks handle many attributes
    _bit_conf - natural match - fetches bit, returns bit
    _port_conf - synthesize port from bits

    The 8 callbacks are mapped into place by 2 macros in nsc_gpio.h,
    one each for bits/ports. BUG: the mapping is inelegant. see code for
    more comments.


    WRT port-synthesis from bit-wide properties

    - different chips expose different properties as bit-vectors
    pc87366 gives data-in, data-out-rd/wr only,
    sc-1100 also shows interrupt-enable, gpio-event-status as bit-vectors

    - port-synthesis lets us hide the difference, if done correctly.
    BUG : Due to the macro design (built for static decl & initialization),
    its cumbersome to do the mapping.

    - alternative (and simpler) approach is to :
    selectively create attr-files, thus exposing only the features desired
    let user-space figure it out

    this is all restated in patch comments.


    Patch does nothing wrt reservations or class_dev design,
    Chris, Im hoping you're focussing here, it would be really cool if my
    callbacks
    were to just drop right into gpio class impl ;-)



    The following cut-pastes demonstrate some level of working-ness,
    1 - pin reads via char-device-files
    2 - same pins, read as sys-attr files, bit/port (values)
    3 - demonstrates bitpos=1 exposing bit-in-port (values)
    4 - config-features work - bit-read/write works, effect visible in
    port-reads
    5 - port-wide synthesis mostly works - isnt working yet
    6 - currently nsc_gpio.c defines DEBUG 1, giving output cut-pasted below


    pc8736x-gpio port reads:

    running: cat /sys/devices/platform/pc8736x_gpio.0/port_0_value
    /sys/devices/platform/pc8736x_gpio.0/port_1_value
    /sys/devices/platform/pc8736x_gpio.0/port_2_value
    /sys/devices/platform/pc8736x_gpio.0/port_3_value
    0xff
    0xd1
    0x9e
    0x1d
    ...

    and bit-reads: (bitpos=1 exposes more info)

    running: cat /sys/devices/platform/pc8736x_gpio.0/bit_1.0_value
    /sys/devices/platform/pc8736x_gpio.0/bit_1.1_value
    /sys/devices/platform/pc8736x_gpio.0/bit_1.2_value
    /sys/devices/platform/pc8736x_gpio.0/bit_1.3_value
    /sys/devices/platform/pc8736x_gpio.0/bit_1.4_value
    /sys/devices/platform/pc8736x_gpio.0/bit_1.5_value
    /sys/devices/platform/pc8736x_gpio.0/bit_1.6_value
    /sys/devices/platform/pc8736x_gpio.0/bit_1.7_value
    0x1
    0x0
    0x0
    0x0
    0x10
    0x0
    0x40
    0x80

    test port-synthesis - cmd-letter is borrowed from set understood by
    char-dev-write-handler

    running: cat /sys/devices/platform/pc8736x_gpio.0/port_0_output_enabled
    [ 2039.060925] platform pc8736x_gpio.0: get-portconf bit:0.0 cfg:44
    cmd'O' res:0
    [ 2039.068312] platform pc8736x_gpio.0: get-portconf bit:0.1 cfg:44
    cmd'O' res:0
    [ 2039.075703] platform pc8736x_gpio.0: get-portconf bit:0.2 cfg:44
    cmd'O' res:0
    [ 2039.083053] platform pc8736x_gpio.0: get-portconf bit:0.3 cfg:44
    cmd'O' res:0
    [ 2039.090434] platform pc8736x_gpio.0: get-portconf bit:0.4 cfg:44
    cmd'O' res:0
    [ 2039.097789] platform pc8736x_gpio.0: get-portconf bit:0.5 cfg:44
    cmd'O' res:0
    [ 2039.105140] platform pc8736x_gpio.0: get-portconf bit:0.6 cfg:44
    cmd'O' res:0
    [ 2039.112487] platform pc8736x_gpio.0: get-portconf bit:0.7 cfg:44
    cmd'O' res:0
    [ 2039.119875] platform pc8736x_gpio.0: get-portconf() idx=0 cmd='O' ret:0
    0x0

    running: cat /sys/devices/platform/pc8736x_gpio.0/port_0_pullup_enabled
    [ 2039.185437] platform pc8736x_gpio.0: get-portconf bit:0.0 cfg:44
    cmd'P' res:0
    [ 2039.192829] platform pc8736x_gpio.0: get-portconf bit:0.1 cfg:44
    cmd'P' res:0
    [ 2039.200181] platform pc8736x_gpio.0: get-portconf bit:0.2 cfg:44
    cmd'P' res:0
    [ 2039.207531] platform pc8736x_gpio.0: get-portconf bit:0.3 cfg:44
    cmd'P' res:0
    [ 2039.214887] platform pc8736x_gpio.0: get-portconf bit:0.4 cfg:44
    cmd'P' res:0
    [ 2039.222244] platform pc8736x_gpio.0: get-portconf bit:0.5 cfg:44
    cmd'P' res:0
    [ 2039.229633] platform pc8736x_gpio.0: get-portconf bit:0.6 cfg:44
    cmd'P' res:0
    [ 2039.236995] platform pc8736x_gpio.0: get-portconf bit:0.7 cfg:44
    cmd'P' res:0
    [ 2039.244300] platform pc8736x_gpio.0: get-portconf() idx=0 cmd='P' ret:0
    0x0

    soekris:/sys/devices/platform# echo 1 >
    pc8736x_gpio.0/bit_0.1_pullup_enabled
    [ 2760.304169] platform pc8736x_gpio.0: GPIO1 pullup enabled=1
    [ 2760.310014] platform pc8736x_gpio.0: set-bitconf() idx:1 cmd:'P' buf:'1
    [ 2760.310059] ' set:1
    soekris:/sys/devices/platform#
    soekris:/sys/devices/platform# cat pc8736x_gpio.0/port_0_pullup_enabled
    [ 2780.140281] platform pc8736x_gpio.0: get-portconf bit:0.0 cfg:44
    cmd'P' res:0
    [ 2780.147761] platform pc8736x_gpio.0: get-portconf bit:0.1 cfg:46
    cmd'P' res:2
    [ 2780.155139] platform pc8736x_gpio.0: get-portconf bit:0.2 cfg:44
    cmd'P' res:0
    [ 2780.162585] platform pc8736x_gpio.0: get-portconf bit:0.3 cfg:44
    cmd'P' res:0
    [ 2780.169960] platform pc8736x_gpio.0: get-portconf bit:0.4 cfg:44
    cmd'P' res:0
    [ 2780.177379] platform pc8736x_gpio.0: get-portconf bit:0.5 cfg:44
    cmd'P' res:0
    [ 2780.184749] platform pc8736x_gpio.0: get-portconf bit:0.6 cfg:44
    cmd'P' res:0
    [ 2780.192158] platform pc8736x_gpio.0: get-portconf bit:0.7 cfg:44
    cmd'P' res:0
    [ 2780.199484] platform pc8736x_gpio.0: get-portconf() idx=0 cmd='P' ret:2
    0x2




    TIA for any comments, feedback.


    diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/Documentation/gpio-model.txt roll3-m2/Documentation/gpio-model.txt
    --- ../linux-2.6.18-rc3-mm2-sk/Documentation/gpio-model.txt 1969-12-31 17:00:00.000000000 -0700
    +++ roll3-m2/Documentation/gpio-model.txt 2006-08-08 16:47:12.000000000 -0600
    @@ -0,0 +1,130 @@
    +
    +Sysfs GPIO Model
    +
    +GPIO Hardware Design Overview
    +
    +GPIO (General Purpose IO) Hardware provides digital IO on pins
    +(physical connections to external circuitry). The pins can be read
    +and written, and are configurable for adaptability to a variety of
    +external circuit designs. The typical GPIO feature set looks like
    +this:
    +
    + input:
    + - input (read pin value)
    + - input (read pin driven-value)
    + - input debounce (hw noise conditioning)
    +
    + output:
    + - data-out
    + - output-enable !tristate
    +
    + config: (used less frequently)
    + - pullup-resistor
    + - output-enable
    + - open collector (can only pull to zero)
    + - open emmitor (can only pull to high)
    + - interrupt generation & control
    +
    +
    +Hardware Ports Organization
    +
    +Many GPIO hardware units organize GPIO bits (aka pins) into ports (aka
    +banks) with an implementation-dependent width. The pin values are
    +written and read on a port-wide basis, ensuring that all pins in a
    +port change *simultaneously*, ie not as result of separate bus cycles.
    +This is essential for some applications. Port-wide access is
    +typically just for data.
    +
    +Config features control the GPIO's electrical properties, and are
    +rarely used (data-access dominates). These features are usually
    +implemented per-bit, but with some variations.
    +
    +
    +GPIO-Sysfs Features
    +
    +All GPIO device-attr-files have 3-part names:
    +
    + <prefix>_<id>_<suffix>.
    +
    + prefix 'bit_' or 'port_'
    + id 1.3 or 2 respectively
    + suffix feature-name (value, totem, etc)
    +
    +for example:
    +
    +soekris:/sys/devices/platform# ls pc8736x_gpio.0/bit_0.1_*
    +pc8736x_gpio.0/bit_0.1_current pc8736x_gpio.0/bit_0.1_pullup_enabled
    +pc8736x_gpio.0/bit_0.1_debounced pc8736x_gpio.0/bit_0.1_status
    +pc8736x_gpio.0/bit_0.1_locked pc8736x_gpio.0/bit_0.1_totem
    +pc8736x_gpio.0/bit_0.1_output_enabled pc8736x_gpio.0/bit_0.1_value
    +
    +
    +Suffixes:
    +
    +The Suffix displays the feature-name. They are named for one of the
    +states, rather than the property-name, since a state-name=X is
    +self-explanatory.
    +
    +And, matching my hardware:
    + _output_enabled vs _tristate
    + _totem vs _pushpull
    +
    +The <suffix> should be user replaceable later, with a means to add
    +logical inversion selectively.
    +
    +
    +Bit vs Port features.
    +
    +Depending upon hardware, each GPIO feature is controllable via either
    +a bit-wide or port-wide interface. The model allows driver to
    +abstract this, and to synthesize port-wide features where needed.
    +
    +- driver can choose to expose features only as naturally supported.
    + User space can deal then recognize when port-wide control of
    + output-enable, interrupts, etc are supported by the hardware.
    +
    +- driver can synthesize port-features by banging the per-bit access.
    + This was done ad-hoc, got reasonable results.
    +
    +
    +Port, Bit Reservation (a few thoughts)
    +
    +Assuming port-wide GPIOs (seeing a bias?), each hardware port is
    +trivially reserved by masking in bits as they're taken.
    +
    +simplifing constraints:
    + - user-ports are always subset of single hw-port
    + - no hw-port spanning
    + - contiguous bit-blocks only (0x5,0x9 disallowed)
    +
    +
    +Various Hardware Notes
    +
    +General rules
    +
    +- GPIOs generally power-up into safe states (tristate/input-only),
    + interrupts are off, etc.
    +
    +- GPIOs can have diminishing feature set across provided resources. A
    + designer rarely needs homogeneous features, often needs very few
    + pins which must generate interrupts.
    +
    +- when a pin doesnt support feature, it provides a 'ghost-bit', which
    + reads as disabled, and ignores writes/ enablements. This is not
    + universal however..
    +
    +PC87366
    +
    +- has 4 8-bit ports, with diminishing feature set.
    +- pins in all 4 share common *basic-config* register defn
    +- ports 2,3 lack interrupts
    +-- *and* they lack register too.
    +-- they should have given it a ghost register.
    +
    +SC-1100
    +
    +This GPIO-port also supports Interrupt-Enable & Status.
    +
    +Sadly, it doesnt expose port-wide output-enable, which means
    +bus-logic (switching all N-lines to hiZ) is prohibitively slow.
    +
    diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/drivers/char/nsc_gpio.c roll3-m2/drivers/char/nsc_gpio.c
    --- ../linux-2.6.18-rc3-mm2-sk/drivers/char/nsc_gpio.c 2006-07-30 11:29:18.000000000 -0600
    +++ roll3-m2/drivers/char/nsc_gpio.c 2006-08-08 16:54:01.000000000 -0600
    @@ -7,36 +7,112 @@
    Copyright (c) 2005 Jim Cromie <jim.cromie@gmail.com>
    */

    +#define DRVNAME "nsc_gpio"
    +
    +#include <linux/device.h>
    #include <linux/config.h>
    #include <linux/fs.h>
    #include <linux/module.h>
    #include <linux/errno.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    -#include <linux/nsc_gpio.h>
    #include <linux/platform_device.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
    +#include <linux/nsc_gpio.h>

    -#define NAME "nsc_gpio"
    +static int noports = 0;
    +module_param(noports, int, 0);
    +MODULE_PARM_DESC(noports, "sysgpio 0:both 1:no-status 2:no-sysfs-flags");
    +
    +static int bitpos = 0;
    +module_param(bitpos, int, 0);
    +MODULE_PARM_DESC(bitpos, "1 to expose bit position in values");

    -void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index)
    +static char statbuf[256];
    +static ssize_t nsc_gpio_status(struct nsc_gpio_ops *amp, unsigned index)
    {
    /* retrieve current config w/o changing it */
    u32 config = amp->gpio_config(index, ~0, 0);
    +
    + return sprintf (
    + statbuf, "i/o%02u:%d/%d 0x%04x %s %s %s %s %s %s %s",
    + index,
    + !!(amp->gpio_get(index)), /* boolean display */
    + amp->gpio_current(index), /* someday show [01z] */
    + config,
    + (config & 1) ? "OE" : "TS", /* output-enabled/tristate */
    + (config & 2) ? "PP" : "OD", /* push pull / open drain */
    + (config & 4) ? "PUE" : "PUD", /* pull up enabled/disabled */
    + (config & 8) ? "LOCKED" : "", /* locked / unlocked */
    + (config & 16) ? "LEVEL" : "EDGE",/* level/edge input */
    + (config & 32) ? "HI" : "LO", /* trigger on rise/fall edge */
    + (config & 64) ? "DEBOUNCE" : "" /* debounce */
    + );
    +}
    +
    +void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index)
    +{
    + nsc_gpio_status(amp,index);
    + dev_info(amp->dev, "%s\n", statbuf);
    +}
    +
    +/* the pin-mode-change 'commands' of the legacy device-file-interface,
    + are reused in the sysfs-interface.
    +*/
    +static int command_write(struct nsc_gpio_ops *amp, char c, unsigned m)
    +{
    + struct device *dev = amp->dev;
    + int err = 0;

    - /* user requested via 'v' command, so its INFO */
    - dev_info(amp->dev, "io%02u: 0x%04x %s %s %s %s %s %s %s\tio:%d/%d\n",
    - index, config,
    - (config & 1) ? "OE" : "TS", /* output-enabled/tristate */
    - (config & 2) ? "PP" : "OD", /* push pull / open drain */
    - (config & 4) ? "PUE" : "PUD", /* pull up enabled/disabled */
    - (config & 8) ? "LOCKED" : "", /* locked / unlocked */
    - (config & 16) ? "LEVEL" : "EDGE",/* level/edge input */
    - (config & 32) ? "HI" : "LO", /* trigger on rise/fall edge */
    - (config & 64) ? "DEBOUNCE" : "", /* debounce */
    + switch (c) {

    - amp->gpio_get(index), amp->gpio_current(index));
    + /* cases are a mix of old command letters, and new PF_CMD_*
    + symbols, for Off & On actions respectively.
    + */
    + case '0':
    + amp->gpio_set(m, 0);
    + break;
    + case '1':
    + amp->gpio_set(m, 1);
    + break;
    + case PF_CMD_OUT_ENA:
    + dev_dbg(dev, "GPIO%d output enabled\n", m);
    + amp->gpio_config(m, ~1, 1);
    + break;
    + case 'o':
    + dev_dbg(dev, "GPIO%d output disabled\n", m);
    + amp->gpio_config(m, ~1, 0);
    + break;
    + case PF_CMD_TOTEM:
    + dev_dbg(dev, "GPIO%d output is push pull\n", m);
    + amp->gpio_config(m, ~2, 2);
    + break;
    + case 't':
    + dev_dbg(dev, "GPIO%d output is open drain\n", m);
    + amp->gpio_config(m, ~2, 0);
    + break;
    + case PF_CMD_PULLUP:
    + dev_dbg(dev, "GPIO%d pull up enabled\n", m);
    + amp->gpio_config(m, ~4, 4);
    + break;
    + case 'p':
    + dev_dbg(dev, "GPIO%d pull up disabled\n", m);
    + amp->gpio_config(m, ~4, 0);
    + break;
    + case 'v':
    + /* View Current pin settings */
    + amp->gpio_dump(amp, m);
    + break;
    + case '\n':
    + /* end of settings string, do nothing */
    + break;
    + default:
    + dev_err(dev, "io%2d bad setting: chr<0x%2x>\n",
    + m, (int)c);
    + err++;
    + }
    + return err;
    }

    ssize_t nsc_gpio_write(struct file *file, const char __user *data,
    @@ -44,57 +120,14 @@ ssize_t nsc_gpio_write(struct file *file
    {
    unsigned m = iminor(file->f_dentry->d_inode);
    struct nsc_gpio_ops *amp = file->private_data;
    - struct device *dev = amp->dev;
    - size_t i;
    - int err = 0;
    + int i, err = 0;

    for (i = 0; i < len; ++i) {
    char c;
    if (get_user(c, data + i))
    return -EFAULT;
    - switch (c) {
    - case '0':
    - amp->gpio_set(m, 0);
    - break;
    - case '1':
    - amp->gpio_set(m, 1);
    - break;
    - case 'O':
    - dev_dbg(dev, "GPIO%d output enabled\n", m);
    - amp->gpio_config(m, ~1, 1);
    - break;
    - case 'o':
    - dev_dbg(dev, "GPIO%d output disabled\n", m);
    - amp->gpio_config(m, ~1, 0);
    - break;
    - case 'T':
    - dev_dbg(dev, "GPIO%d output is push pull\n", m);
    - amp->gpio_config(m, ~2, 2);
    - break;
    - case 't':
    - dev_dbg(dev, "GPIO%d output is open drain\n", m);
    - amp->gpio_config(m, ~2, 0);
    - break;
    - case 'P':
    - dev_dbg(dev, "GPIO%d pull up enabled\n", m);
    - amp->gpio_config(m, ~4, 4);
    - break;
    - case 'p':
    - dev_dbg(dev, "GPIO%d pull up disabled\n", m);
    - amp->gpio_config(m, ~4, 0);
    - break;
    - case 'v':
    - /* View Current pin settings */
    - amp->gpio_dump(amp, m);
    - break;
    - case '\n':
    - /* end of settings string, do nothing */
    - break;
    - default:
    - dev_err(dev, "io%2d bad setting: chr<0x%2x>\n",
    - m, (int)c);
    - err++;
    - }
    +
    + err += command_write(amp, c, m);
    }
    if (err)
    return -EINVAL; /* full string handled, report error */
    @@ -121,15 +154,414 @@ EXPORT_SYMBOL(nsc_gpio_write);
    EXPORT_SYMBOL(nsc_gpio_read);
    EXPORT_SYMBOL(nsc_gpio_dump);

    +#define BITVAL(a) "%u\n", !!(a) /* force int to boolean */
    +#define PORTVAL(a) "0x%x\n", (a) /* expose bitval pos in port */
    +
    +/* 8 sysfs-callbacks, partitioned on 3 axes:
    + get/set: as reqd by 2 different callback fn-signatures
    + value/: for port-wide
    + /feature: mostly use-once, slower, synthesized port-access
    + bit/port: for values, separate for simplicity, speed
    +
    + This is arguably over-optimization, may clash with client-module
    + feature to bit/port mappings.
    +*/
    +ssize_t nsc_gpio_sysfs_get_bitval(struct device *dev,
    + struct device_attribute *devattr, char *buf)
    +{
    + struct nsc_gpio_ops *amp = dev->driver_data;
    + struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
    + int idx = attr->bitnum;
    + int func = attr->fn_slct;
    + unsigned res = -1;
    +
    + BUG_ON(!amp);
    +
    + switch (func) {
    + case PF_CMD_VALUE:
    + res = amp->gpio_get(idx);
    + break;
    + case PF_CMD_CURR:
    + res = amp->gpio_current(idx);
    + break;
    + }
    + return bitpos ?
    + sprintf(buf, PORTVAL(res)) :
    + sprintf(buf, BITVAL(res));
    +}
    +ssize_t nsc_gpio_sysfs_get_portval(struct device *dev,
    + struct device_attribute *devattr, char *buf)
    +{
    + struct nsc_gpio_ops *amp = dev->driver_data;
    + struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
    + int idx = attr->portnum;
    + int func = attr->fn_slct;
    + unsigned res = -1;
    +
    + BUG_ON(!amp);
    + switch (func) {
    + case PF_CMD_VALUE:
    + res = amp->gpio_get_port(idx);
    + break;
    + case PF_CMD_CURR:
    + res = amp->gpio_current_port(idx);
    + break;
    + }
    + return sprintf(buf, PORTVAL(res));
    +}
    +EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_bitval);
    +EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_portval);
    +
    +ssize_t nsc_gpio_sysfs_set_bitval(struct device *dev,
    + struct device_attribute *devattr,
    + const char *buf, size_t count)
    +{
    + struct nsc_gpio_ops *amp = dev->driver_data;
    + struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
    + int idx = attr->bitnum;
    + long setting = simple_strtol(buf, NULL, 10);
    +
    + /* CURR is VALUE for writes */
    + BUG_ON(!amp);
    + amp->gpio_set(idx, setting);
    + return count;
    +}
    +ssize_t nsc_gpio_sysfs_set_portval(struct device *dev,
    + struct device_attribute *devattr,
    + const char *buf, size_t count)
    +{
    + struct nsc_gpio_ops *amp = dev->driver_data;
    + struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
    + int idx = attr->portnum;
    + long setting = simple_strtol(buf, NULL, 10);
    +
    + BUG_ON(!amp);
    + amp->gpio_setclr_port(idx, setting, ~setting);
    + return count;
    +}
    +EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_bitval);
    +EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_portval);
    +
    +ssize_t nsc_gpio_sysfs_get_bitconf(struct device *dev,
    + struct device_attribute *devattr,
    + char *buf)
    +{
    + struct nsc_gpio_ops *amp = dev->driver_data;
    + struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
    + int idx = attr->bitnum;
    + int func = attr->fn_slct;
    + u32 config, res = 0;
    +
    + BUG_ON(!amp);
    + config = amp->gpio_config(idx, ~0, 0);
    +
    + switch (func) {
    +
    + case PF_CMD_CURR:
    + res = amp->gpio_current(idx);
    + return sprintf(buf, BITVAL(res));
    +
    + case PF_CMD_STATUS:
    + nsc_gpio_status(amp, idx);
    + return sprintf(buf, "%s\n", statbuf);
    +
    + case PF_CMD_OUT_ENA:
    + res = config & PF_MASK_OUT_ENA;
    + break;
    + case PF_CMD_TOTEM:
    + res = config & PF_MASK_TOTEM;
    + break;
    + case PF_CMD_PULLUP:
    + res = config & PF_MASK_PULLUP;
    + break;
    + case PF_CMD_LOCKED:
    + res = config & PF_MASK_LOCKED;
    + break;
    + case PF_CMD_DEBOUNCE:
    + res = config & PF_MASK_DEBOUNCE;
    + break;
    + case PF_CMD_INT_ENA:
    + res = config & PF_MASK_INT_ENA;
    + break;
    + case PF_CMD_INT_TRIG:
    + res = config & PF_MASK_INT_TRIG;
    + break;
    +
    + default:
    + dev_err(dev, "unknown cmd '%c'\n", func);
    + }
    + dev_dbg(dev, "get-bitconf() idx:%d cmd:'%c' conf%02x res:%02x\n",
    + idx, func, config, res);
    +
    + return sprintf(buf, BITVAL(res));
    +}
    +EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_bitconf);
    +EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_bitconf);
    +
    +
    +#define replace(Mask,Val) ~Mask, Val ? Mask : 0
    +
    +ssize_t nsc_gpio_sysfs_set_bitconf(struct device *dev,
    + struct device_attribute *devattr,
    + const char *buf, size_t count)
    +{
    + struct nsc_gpio_ops *amp = dev->driver_data;
    + struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
    + int idx = attr->bitnum;
    + int func = attr->fn_slct;
    + long setting = simple_strtol(buf, NULL, 10);
    +
    + BUG_ON(!amp);
    +
    + switch (func) {
    +
    + case PF_CMD_STATUS:
    + {
    + /* device-file write interface. Kludge, but small */
    + int i, err = 0;
    + dev_warn(dev, "try to set status to: %s\n", buf);
    + for (i = 0; buf[i]; i++)
    + err += command_write(amp, buf[i], idx);
    + if (err)
    + dev_warn(dev, "%d errs on cmd %s\n", err, buf);
    + break;
    + }
    + case PF_CMD_OUT_ENA:
    + dev_info(dev, "GPIO%d output enabled=%ld\n", idx, setting);
    + amp->gpio_config(idx, replace(1, setting));
    + // ~1, setting ? 1 : 0);
    + break;
    + case PF_CMD_PULLUP:
    + dev_info(dev, "GPIO%d pullup enabled=%ld\n", idx, setting);
    + amp->gpio_config(idx, ~2, setting ? 2 : 0);
    + break;
    + case PF_CMD_TOTEM:
    + dev_info(dev, "GPIO%d totem-pole enabled=%ld\n", idx, setting);
    + amp->gpio_config(idx, ~4, setting ? 4 : 0);
    + break;
    + case 'L':
    + dev_info(dev, "GPIO%d pin locked=%ld\n", idx, setting);
    + amp->gpio_config(idx, ~8, setting ? 8 : 0);
    + break;
    + case 'D':
    + dev_info(dev, "GPIO%d debounced=%ld\n", idx, setting);
    + amp->gpio_config(idx, ~PF_MASK_DEBOUNCE,
    + setting ? PF_MASK_DEBOUNCE : 0);
    + break;
    + default:
    + dev_err(dev, "sysfs unknown cmd '%c'\n", func);
    + }
    + dev_info(dev, "set-bitconf() idx:%d cmd:'%c' buf:'%s' set:%lx\n",
    + idx, func, buf, setting);
    +
    + return strlen(buf);
    +}
    +
    +#if 10
    +ssize_t nsc_gpio_sysfs_get_portconf(struct device *dev,
    + struct device_attribute *devattr,
    + char *buf)
    +{
    + struct nsc_gpio_ops *amp = dev->driver_data;
    + struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
    + int idx = attr->portnum;
    + int func = attr->fn_slct;
    + u32 config, res = 0, ret = 0;
    + char *s = buf;
    + int bit, width = amp->port_size;
    +
    + BUG_ON(!amp);
    + BUG_ON(!width);
    +
    + for (bit = 0; bit < width; bit++) {
    +
    + config = amp->gpio_config(bit + idx * width, ~0, 0);
    +
    + switch ((char)func) {
    + case PF_CMD_CURR:
    + res = amp->gpio_current(bit);
    + sprintf(s, BITVAL(res));
    + case PF_CMD_STATUS:
    + nsc_gpio_status(amp, bit);
    + sprintf(s, "%s\n", statbuf);
    + case PF_CMD_OUT_ENA:
    + res = config & PF_MASK_OUT_ENA;
    + break;
    + case PF_CMD_PULLUP:
    + res = config & PF_MASK_PULLUP;
    + break;
    + case PF_CMD_TOTEM:
    + res = config & PF_MASK_TOTEM;
    + break;
    + case PF_CMD_LOCKED:
    + res = config & PF_MASK_LOCKED;
    + break;
    + case PF_CMD_DEBOUNCE:
    + res = config & PF_MASK_DEBOUNCE;
    + break;
    + case PF_CMD_INT_ENA:
    + res = config & PF_MASK_INT_ENA;
    + break;
    + case PF_CMD_INT_TRIG:
    + res = config & PF_MASK_INT_TRIG;
    + break;
    +
    + default:
    + dev_err(dev, "unknown cmd '%c'\n", func);
    + res = 0;
    + }
    + res = res ? 1<<bit : 0;
    + ret |= res;
    +
    + dev_dbg(dev, "get-portconf bit:%d.%d cfg:%x cmd'%c' res:%x\n",
    + idx, bit, config, func, res);
    + }
    + dev_dbg(dev, "get-portconf(%d) idx=%d cmd='%c' ret:%x\n",
    + width, idx, func, ret);
    +
    + return sprintf(buf, PORTVAL(ret));
    +}
    +
    +ssize_t nsc_gpio_sysfs_set_portconf(struct device *dev,
    + struct device_attribute *devattr,
    + const char *buf, size_t count)
    +{
    + struct nsc_gpio_ops *amp = dev->driver_data;
    + struct gpio_dev_attr *attr = to_gpio_dev_attr(devattr);
    + int idx = attr->bitnum;
    + int func = attr->fn_slct;
    + int bit, width = amp->port_size;
    + long setting = simple_strtol(buf, NULL, 10);
    +
    + BUG_ON(!amp);
    +
    + for (bit = 0; bit < width; bit++) {
    +
    + switch (func) {
    +
    + case PF_CMD_STATUS:
    + {
    + /* device-file write interface */
    + int i, err = 0;
    + dev_warn(dev, "try to set status to: %s\n", buf);
    + for (i = 0; buf[i]; i++)
    + err += command_write(amp, buf[i], idx);
    + if (err)
    + dev_warn(dev, "%d errs on cmd %s\n", err, buf);
    + break;
    + }
    + case PF_CMD_OUT_ENA:
    + amp->gpio_config(idx, ~PF_MASK_OUT_ENA,
    + setting ? PF_MASK_OUT_ENA : 0);
    + break;
    + case PF_CMD_TOTEM:
    + amp->gpio_config(idx, ~PF_MASK_TOTEM,
    + setting ? PF_MASK_TOTEM : 0);
    + break;
    + case PF_CMD_PULLUP:
    + amp->gpio_config(idx, ~PF_MASK_PULLUP,
    + setting ? PF_MASK_PULLUP : 0);
    + break;
    + case PF_CMD_LOCKED:
    + amp->gpio_config(idx, ~PF_CMD_LOCKED,
    + setting ? PF_MASK_LOCKED : 0);
    + break;
    + case PF_CMD_DEBOUNCE:
    + amp->gpio_config(idx, ~PF_MASK_DEBOUNCE,
    + setting ? PF_MASK_DEBOUNCE : 0);
    + break;
    + default:
    + dev_err(dev, "sysfs unknown cmd '%c'\n", func);
    + }
    + }
    + dev_info(dev, "set-portconf() idx:%d cmd:'%c' buf:'%s' set:%lx\n",
    + idx, func, buf, setting);
    +
    + return strlen(buf);
    +}
    +EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_get_portconf);
    +EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_set_portconf);
    +#endif
    +
    +
    +
    +static u8 legacy = 1, feature_attrs = 1;
    +static int dev_attr_create_errs;
    +static void create_devattr_file(struct device* dev,
    + struct device_attribute *dev_attr)
    +{
    + int err;
    +
    + if (dev_attr->show || dev_attr->store) {
    +
    + err = device_create_file(dev, dev_attr);
    + if (err)
    + dev_attr_create_errs++;
    + }
    + else
    + dev_dbg(dev, "skipping %s\n", dev_attr->attr.name);
    +}
    +
    +/* next 2 functions (and previous kludge) could be replaced by pair of
    + calls to sysfs_create/remove_group, except that collecting
    + attributes into a group would be manual.
    +*/
    +
    +void nsc_gpio_sysfs_bits_init(struct device* dev,
    + struct gpio_attributes pp[], int numdevs)
    +{
    + int i;
    +
    + for (i = 0; i < numdevs; i++) {
    +
    + create_devattr_file(dev, &pp[i].value.dev_attr);
    + create_devattr_file(dev, &pp[i].curr.dev_attr);
    +
    + /* provide legacy device-file emulation here */
    + if (legacy)
    + create_devattr_file(dev, &pp[i].status.dev_attr);
    +
    + /* create separate attr-file per feature */
    + if (feature_attrs) {
    + create_devattr_file(dev, &pp[i].oe.dev_attr);
    + create_devattr_file(dev, &pp[i].pue.dev_attr);
    + create_devattr_file(dev, &pp[i].totem.dev_attr);
    + create_devattr_file(dev, &pp[i].locked.dev_attr);
    + create_devattr_file(dev, &pp[i].bounce.dev_attr);
    + create_devattr_file(dev, &pp[i].int_en.dev_attr);
    + create_devattr_file(dev, &pp[i].int_lvl.dev_attr);
    + }
    + }
    +}
    +
    +void nsc_gpio_sysfs_bits_fini(struct device* dev,
    + struct gpio_attributes pp[], int numdevs)
    +{
    + int i;
    + for (i = 0; i < numdevs; i++) {
    + device_remove_file(dev, &pp[i].value.dev_attr);
    + device_remove_file(dev, &pp[i].curr.dev_attr);
    + /* always remove, whether there or not */
    + device_remove_file(dev, &pp[i].status.dev_attr);
    + device_remove_file(dev, &pp[i].oe.dev_attr);
    + device_remove_file(dev, &pp[i].pue.dev_attr);
    + device_remove_file(dev, &pp[i].totem.dev_attr);
    + device_remove_file(dev, &pp[i].locked.dev_attr);
    + device_remove_file(dev, &pp[i].bounce.dev_attr);
    + }
    +}
    +EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_bits_init);
    +EXPORT_SYMBOL_GPL(nsc_gpio_sysfs_bits_fini);
    +
    static int __init nsc_gpio_init(void)
    {
    - printk(KERN_DEBUG NAME " initializing\n");
    + printk(KERN_DEBUG DRVNAME " initializing\n");
    return 0;
    }

    static void __exit nsc_gpio_cleanup(void)
    {
    - printk(KERN_DEBUG NAME " cleanup\n");
    + printk(KERN_DEBUG DRVNAME " cleanup\n");
    }

    module_init(nsc_gpio_init);
    diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/drivers/char/pc8736x_gpio.c roll3-m2/drivers/char/pc8736x_gpio.c
    --- ../linux-2.6.18-rc3-mm2-sk/drivers/char/pc8736x_gpio.c 2006-08-06 10:18:40.000000000 -0600
    +++ roll3-m2/drivers/char/pc8736x_gpio.c 2006-08-08 14:01:00.000000000 -0600
    @@ -1,5 +1,4 @@
    -/* linux/drivers/char/pc8736x_gpio.c
    -
    +/*
    National Semiconductor PC8736x GPIO driver. Allows a user space
    process to play with the GPIO pins.

    @@ -9,6 +8,8 @@
    Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>,
    */

    +#define DRVNAME "pc8736x_gpio"
    +
    #include <linux/fs.h>
    #include <linux/module.h>
    #include <linux/errno.h>
    @@ -22,11 +23,13 @@
    #include <linux/platform_device.h>
    #include <asm/uaccess.h>

    -#define DEVNAME "pc8736x_gpio"
    -
    -MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
    -MODULE_DESCRIPTION("NatSemi/Winbond PC-8736x GPIO Pin Driver");
    -MODULE_LICENSE("GPL");
    +static int nobits = 0;
    +module_param(nobits, int, 0);
    +MODULE_PARM_DESC(nobits, "nobits=1 to suppress sysfs bits interface");
    +
    +static int noports = 0;
    +module_param(noports, int, 0);
    +MODULE_PARM_DESC(noports, "noports=1 to supress sysfs ports interface");

    static int major; /* default to dynamic major */
    module_param(major, int, 0);
    @@ -144,15 +147,29 @@ static u32 pc8736x_gpio_configure(unsign
    SIO_GPIO_PIN_CONFIG);
    }

    -static int pc8736x_gpio_get(unsigned minor)
    +/* vtable-API functions.
    + this is a gpio-port device, so bitvals are pulled from ports (in princple)
    +*/
    +static u32 pc8736x_gpio_get_port(unsigned port)
    +{
    + u8 val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
    +
    + dev_dbg(&pdev->dev, "_gpio_get(%d from %x) == val %d\n",
    + port, pc8736x_gpio_base + port_offset[port] + PORT_IN,
    + val);
    +
    + return (u32)val;
    +}
    +
    +static u32 pc8736x_gpio_get(unsigned minor)
    {
    - int port, bit, val;
    + int port, bit;
    + u32 val;

    port = minor >> 3;
    bit = minor & 7;
    val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
    - val >>= bit;
    - val &= 1;
    + val &= 1 << bit;

    dev_dbg(&pdev->dev, "_gpio_get(%d from %x bit %d) == val %d\n",
    minor, pc8736x_gpio_base + port_offset[port] + PORT_IN, bit,
    @@ -188,13 +205,21 @@ static void pc8736x_gpio_set(unsigned mi
    pc8736x_gpio_shadow[port] = val;
    }

    -static int pc8736x_gpio_current(unsigned minor)
    +static u32 pc8736x_gpio_current_port(unsigned minor)
    {
    int port, bit;
    minor &= 0x1f;
    port = minor >> 3;
    bit = minor & 7;
    - return ((pc8736x_gpio_shadow[port] >> bit) & 0x01);
    + return (u32)((pc8736x_gpio_shadow[port] >> bit) & 0x01);
    +}
    +static u32 pc8736x_gpio_current(unsigned minor)
    +{
    + int port, bit;
    + minor &= 0x1f;
    + port = minor >> 3;
    + bit = minor & 7;
    + return (u32)((pc8736x_gpio_shadow[port] >> bit) & 0x01);
    }

    static void pc8736x_gpio_change(unsigned index)
    @@ -202,6 +227,26 @@ static void pc8736x_gpio_change(unsigned
    pc8736x_gpio_set(index, !pc8736x_gpio_current(index));
    }

    +static void pc8736x_gpio_setclr_port(unsigned port, u32 bits, u32 mask)
    +{
    + u8 val;
    + u8 curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);
    +
    + val = (curval & ~mask) | (bits & mask);
    +
    + dev_dbg(&pdev->dev, "gpio_set(port:%d addr:%x cur:%x new:%d)\n",
    + port, pc8736x_gpio_base + port_offset[port] + PORT_OUT,
    + curval, val);
    +
    + outb_p(val, pc8736x_gpio_base + port_offset[port] + PORT_OUT);
    +
    + curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);
    + val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
    +
    + dev_dbg(&pdev->dev, "wrote %x, read: %x\n", curval, val);
    + pc8736x_gpio_shadow[port] = val;
    +}
    +
    static struct nsc_gpio_ops pc8736x_gpio_ops = {
    .owner = THIS_MODULE,
    .gpio_config = pc8736x_gpio_configure,
    @@ -209,9 +254,14 @@ static struct nsc_gpio_ops pc8736x_gpio_
    .gpio_get = pc8736x_gpio_get,
    .gpio_set = pc8736x_gpio_set,
    .gpio_change = pc8736x_gpio_change,
    - .gpio_current = pc8736x_gpio_current
    + .gpio_current = pc8736x_gpio_current,
    + .port_size = 8,
    + .gpio_get_port = pc8736x_gpio_get_port,
    + .gpio_setclr_port = pc8736x_gpio_setclr_port,
    + .gpio_current_port = pc8736x_gpio_current_port,
    };

    +/* char-dev API */
    static int pc8736x_gpio_open(struct inode *inode, struct file *file)
    {
    unsigned m = iminor(inode);
    @@ -231,6 +281,61 @@ static const struct file_operations pc87
    .read = nsc_gpio_read,
    };

    +/* sysfs-gpio API */
    +
    +static struct gpio_attributes port0[] = {
    + GPIO_ATTRS(0,0), GPIO_ATTRS(0,1), GPIO_ATTRS(0,2),
    + GPIO_ATTRS(0,3), GPIO_ATTRS(0,4), GPIO_ATTRS(0,5),
    + GPIO_ATTRS(0,6), GPIO_ATTRS(0,7)
    +};
    +
    +static struct gpio_attributes port1[] = {
    + GPIO_ATTRS(1,0), GPIO_ATTRS(1,1), GPIO_ATTRS(1,2),
    + GPIO_ATTRS(1,3), GPIO_ATTRS(1,4), GPIO_ATTRS(1,5),
    + GPIO_ATTRS(1,6), GPIO_ATTRS(1,7)
    +};
    +static struct gpio_attributes port2[] = {
    + GPIO_ATTRS(2,0), GPIO_ATTRS(2,1), GPIO_ATTRS(2,2),
    + GPIO_ATTRS(2,3), GPIO_ATTRS(2,4), GPIO_ATTRS(2,5),
    + GPIO_ATTRS(2,6), GPIO_ATTRS(2,7)
    +};
    +static struct gpio_attributes port3[] = {
    + GPIO_ATTRS(3,0), GPIO_ATTRS(3,1), GPIO_ATTRS(3,2),
    + GPIO_ATTRS(3,3), GPIO_ATTRS(3,4), GPIO_ATTRS(3,5),
    + GPIO_ATTRS(3,6), GPIO_ATTRS(3,7)
    +};
    +
    +static struct gpio_attributes ports[] = {
    + GPIO_PORT_ATTRS(0), GPIO_PORT_ATTRS(1),
    + GPIO_PORT_ATTRS(2), GPIO_PORT_ATTRS(3),
    +};
    +
    +static void __init pc8736x_sysfs_init(struct device* dev)
    +{
    + if (!nobits) {
    + dev_info(dev, "creating gpio-sysfs pin interfaces\n");
    + nsc_gpio_sysfs_bits_init(&pdev->dev, port0, 8);
    + nsc_gpio_sysfs_bits_init(&pdev->dev, port1, 8);
    + nsc_gpio_sysfs_bits_init(&pdev->dev, port2, 8);
    + nsc_gpio_sysfs_bits_init(&pdev->dev, port3, 8);
    + }
    + if (!noports) {
    + dev_info(dev, "creating gpio-sysfs port interfaces\n");
    + nsc_gpio_sysfs_bits_init(&pdev->dev, ports, 4);
    + }
    +}
    +static void pc8736x_sysfs_fini(struct device* dev)
    +{
    + dev_info(dev, "pc8736x_sysfs_fini");
    + nsc_gpio_sysfs_bits_fini(&pdev->dev, port0, 8);
    + nsc_gpio_sysfs_bits_fini(&pdev->dev, port1, 8);
    + nsc_gpio_sysfs_bits_fini(&pdev->dev, port2, 8);
    + nsc_gpio_sysfs_bits_fini(&pdev->dev, port3, 8);
    +
    + nsc_gpio_sysfs_bits_fini(&pdev->dev, ports, 4);
    +}
    +
    +/* device init */
    static void __init pc8736x_init_shadow(void)
    {
    int port;
    @@ -250,7 +355,7 @@ static int __init pc8736x_gpio_init(void
    int rc;
    dev_t devid;

    - pdev = platform_device_alloc(DEVNAME, 0);
    + pdev = platform_device_alloc(DRVNAME, 0);
    if (!pdev)
    return -ENOMEM;

    @@ -288,7 +393,7 @@ static int __init pc8736x_gpio_init(void
    pc8736x_gpio_base = (superio_inb(SIO_BASE_HADDR) << 8
    | superio_inb(SIO_BASE_LADDR));

    - if (!request_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE, DEVNAME)) {
    + if (!request_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE, DRVNAME)) {
    rc = -ENODEV;
    dev_err(&pdev->dev, "GPIO ioport %x busy\n",
    pc8736x_gpio_base);
    @@ -298,9 +403,9 @@ static int __init pc8736x_gpio_init(void

    if (major) {
    devid = MKDEV(major, 0);
    - rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DEVNAME);
    + rc = register_chrdev_region(devid, PC8736X_GPIO_CT, DRVNAME);
    } else {
    - rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DEVNAME);
    + rc = alloc_chrdev_region(&devid, 0, PC8736X_GPIO_CT, DRVNAME);
    major = MAJOR(devid);
    }

    @@ -319,6 +424,10 @@ static int __init pc8736x_gpio_init(void
    cdev_init(&pc8736x_gpio_cdev, &pc8736x_gpio_fileops);
    cdev_add(&pc8736x_gpio_cdev, devid, PC8736X_GPIO_CT);

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

    undo_request_region:
    @@ -335,6 +444,8 @@ static void __exit pc8736x_gpio_cleanup(
    {
    dev_dbg(&pdev->dev, "cleanup\n");

    + pc8736x_sysfs_fini(&pdev->dev);
    +
    cdev_del(&pc8736x_gpio_cdev);
    unregister_chrdev_region(MKDEV(major,0), PC8736X_GPIO_CT);
    release_region(pc8736x_gpio_base, PC8736X_GPIO_RANGE);
    @@ -345,3 +456,7 @@ static void __exit pc8736x_gpio_cleanup(

    module_init(pc8736x_gpio_init);
    module_exit(pc8736x_gpio_cleanup);
    +
    +MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
    +MODULE_DESCRIPTION("NatSemi/Winbond PC-8736x GPIO Pin Driver");
    +MODULE_LICENSE("GPL");
    diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/drivers/char/scx200_gpio.c roll3-m2/drivers/char/scx200_gpio.c
    --- ../linux-2.6.18-rc3-mm2-sk/drivers/char/scx200_gpio.c 2006-08-06 10:18:42.000000000 -0600
    +++ roll3-m2/drivers/char/scx200_gpio.c 2006-08-08 13:20:42.000000000 -0600
    @@ -1,9 +1,11 @@
    -/* linux/drivers/char/scx200_gpio.c
    -
    +/*
    National Semiconductor SCx200 GPIO driver. Allows a user space
    process to play with the GPIO pins.

    - Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */
    + Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
    +*/
    +
    +#define DRVNAME "scx200_gpio"

    #include <linux/device.h>
    #include <linux/fs.h>
    @@ -21,10 +23,6 @@
    #include <linux/scx200_gpio.h>
    #include <linux/nsc_gpio.h>

    -#define DRVNAME "scx200_gpio"
    -
    -static struct platform_device *pdev;
    -
    MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
    MODULE_DESCRIPTION("NatSemi/AMD SCx200 GPIO Pin Driver");
    MODULE_LICENSE("GPL");
    @@ -33,6 +31,14 @@ static int major = 0; /* default to dyn
    module_param(major, int, 0);
    MODULE_PARM_DESC(major, "Major device number");

    +static int nobits = 0;
    +module_param(nobits, int, 0);
    +MODULE_PARM_DESC(nobits, "nobits=1 to suppress sysfs bits interface");
    +
    +static int noports = 0;
    +module_param(noports, int, 0);
    +MODULE_PARM_DESC(noports, "noports=1 to supress sysfs ports interface");
    +
    #define MAX_PINS 32 /* 64 later, when known ok */

    struct nsc_gpio_ops scx200_gpio_ops = {
    @@ -42,10 +48,73 @@ struct nsc_gpio_ops scx200_gpio_ops = {
    .gpio_get = scx200_gpio_get,
    .gpio_set = scx200_gpio_set,
    .gpio_change = scx200_gpio_change,
    - .gpio_current = scx200_gpio_current
    + .gpio_current = scx200_gpio_current,
    + .port_size = 32,
    + .gpio_get_port = scx200_gpio_get_port,
    + .gpio_setclr_port = scx200_gpio_setclr_port,
    +
    + /* add these back to exploit pxa-2xx, which has separate
    + set/clear addresses, avoiding read-modify-write cycles on
    + the pin. (maybe)
    +
    + void (*gpio_set_lo) (unsigned iminor, int state);
    + void (*gpio_set_hi) (unsigned iminor, int state);
    + */
    };
    EXPORT_SYMBOL_GPL(scx200_gpio_ops);

    +static struct gpio_attributes port0[] = {
    + GPIO_ATTRS(0,0), GPIO_ATTRS(0,1), GPIO_ATTRS(0,2),
    + GPIO_ATTRS(0,3), GPIO_ATTRS(0,4), GPIO_ATTRS(0,5),
    + GPIO_ATTRS(0,6), GPIO_ATTRS(0,7), GPIO_ATTRS(0,8),
    + GPIO_ATTRS(0,9), GPIO_ATTRS(0,10), GPIO_ATTRS(0,11),
    + GPIO_ATTRS(0,12), GPIO_ATTRS(0,13), GPIO_ATTRS(0,14),
    + GPIO_ATTRS(0,15), GPIO_ATTRS(0,16), GPIO_ATTRS(0,17),
    + GPIO_ATTRS(0,18), GPIO_ATTRS(0,19), GPIO_ATTRS(0,20),
    + GPIO_ATTRS(0,21), GPIO_ATTRS(0,22), GPIO_ATTRS(0,23),
    + GPIO_ATTRS(0,24), GPIO_ATTRS(0,25), GPIO_ATTRS(0,26),
    + GPIO_ATTRS(0,27), GPIO_ATTRS(0,28), GPIO_ATTRS(0,29),
    + GPIO_ATTRS(0,30), GPIO_ATTRS(0,31),
    +};
    +
    +static struct gpio_attributes port1[] = {
    + GPIO_ATTRS(1,0), GPIO_ATTRS(1,1), GPIO_ATTRS(1,2),
    + GPIO_ATTRS(1,3), GPIO_ATTRS(1,4), GPIO_ATTRS(1,5),
    + GPIO_ATTRS(1,6), GPIO_ATTRS(1,7), GPIO_ATTRS(1,8),
    + GPIO_ATTRS(1,9), GPIO_ATTRS(1,10), GPIO_ATTRS(1,11),
    + GPIO_ATTRS(1,12), GPIO_ATTRS(1,13), GPIO_ATTRS(1,14),
    + GPIO_ATTRS(1,15), GPIO_ATTRS(1,16), GPIO_ATTRS(1,17),
    + GPIO_ATTRS(1,18), GPIO_ATTRS(1,19), GPIO_ATTRS(1,20),
    + GPIO_ATTRS(1,21), GPIO_ATTRS(1,22), GPIO_ATTRS(1,23),
    + GPIO_ATTRS(1,24), GPIO_ATTRS(1,25), GPIO_ATTRS(1,26),
    + GPIO_ATTRS(1,27), GPIO_ATTRS(1,28), GPIO_ATTRS(1,29),
    + GPIO_ATTRS(1,30), GPIO_ATTRS(1,31),
    +};
    +static struct gpio_attributes ports[] = {
    + GPIO_PORT_ATTRS(0), GPIO_PORT_ATTRS(1),
    +};
    +
    +static void __init scx200_sysfs_init(struct device* dev)
    +{
    + if (!nobits) {
    + nsc_gpio_sysfs_bits_init(dev, port0, ARRAY_SIZE(port0));
    + nsc_gpio_sysfs_bits_init(dev, port1, ARRAY_SIZE(port1));
    + }
    + if (!noports)
    + nsc_gpio_sysfs_bits_init(dev, ports, ARRAY_SIZE(ports));
    +}
    +static void scx200_sysfs_fini(struct device* dev)
    +{
    + dev_info(dev, "scx200_sysfs_fini");
    + if (!nobits) {
    + nsc_gpio_sysfs_bits_fini(dev, port0, ARRAY_SIZE(port0));
    + nsc_gpio_sysfs_bits_fini(dev, port1, ARRAY_SIZE(port1));
    + }
    + if (!noports)
    + nsc_gpio_sysfs_bits_fini(dev, ports, ARRAY_SIZE(ports));
    +}
    +
    +/* file API */
    static int scx200_gpio_open(struct inode *inode, struct file *file)
    {
    unsigned m = iminor(inode);
    @@ -69,7 +138,8 @@ static const struct file_operations scx2
    .release = scx200_gpio_release,
    };

    -static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
    +static struct platform_device *pdev; /* for dev_info(&pdev->dev, */
    +static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */

    static int __init scx200_gpio_init(void)
    {
    @@ -92,6 +162,7 @@ static int __init scx200_gpio_init(void)

    /* nsc_gpio uses dev_dbg(), so needs this */
    scx200_gpio_ops.dev = &pdev->dev;
    + scx200_gpio_ops.dev->driver_data = &scx200_gpio_ops;

    if (major) {
    devid = MKDEV(major, 0);
    @@ -108,6 +179,8 @@ static int __init scx200_gpio_init(void)
    cdev_init(&scx200_gpio_cdev, &scx200_gpio_fileops);
    cdev_add(&scx200_gpio_cdev, devid, MAX_PINS);

    + scx200_sysfs_init(&pdev->dev);
    +
    return 0; /* succeed */

    undo_platform_device_add:
    @@ -120,6 +193,7 @@ undo_malloc:

    static void __exit scx200_gpio_cleanup(void)
    {
    + scx200_sysfs_fini(&pdev->dev);
    cdev_del(&scx200_gpio_cdev);
    /* cdev_put(&scx200_gpio_cdev); */

    diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/include/linux/nsc_gpio.h roll3-m2/include/linux/nsc_gpio.h
    --- ../linux-2.6.18-rc3-mm2-sk/include/linux/nsc_gpio.h 2006-07-30 11:31:26.000000000 -0600
    +++ roll3-m2/include/linux/nsc_gpio.h 2006-08-08 16:55:35.000000000 -0600
    @@ -1,19 +1,16 @@
    /**
    - nsc_gpio.c
    -
    National Semiconductor GPIO common access methods.

    - struct nsc_gpio_ops abstracts the low-level access
    - operations for the GPIO units on 2 NSC chip families; the GEODE
    - integrated CPU, and the PC-8736[03456] integrated PC-peripheral
    - chips.
    + struct nsc_gpio_ops abstracts the low-level access operations for
    + the GPIO units on 2 NSC chip families; the GEODE integrated CPU
    + SC-1100, and the PC-8736[03456] integrated PC-peripheral chips.

    The GPIO units on these chips have the same pin architecture, but
    the access methods differ. Thus, scx200_gpio and pc8736x_gpio
    implement their own versions of these routines; and use the common
    file-operations routines implemented in nsc_gpio module.

    - Copyright (c) 2005 Jim Cromie <jim.cromie@gmail.com>
    + Copyright (c) 2005,2006 Jim Cromie <jim.cromie@gmail.com>

    NB: this work was tested on the Geode SC-1100 and PC-87366 chips.
    NSC sold the GEODE line to AMD, and the PC-8736x line to Winbond.
    @@ -21,15 +18,39 @@

    struct nsc_gpio_ops {
    struct module* owner;
    - u32 (*gpio_config) (unsigned iminor, u32 mask, u32 bits);
    + struct device* dev; /* for dev_dbg() support, set in init */
    + u8 port_size; /* 8 or 32 so far. 32 max */
    +
    + /* config ctrl & human desc */
    + u32 (*gpio_config) (unsigned iminor, u32 bits, u32 clr);
    void (*gpio_dump) (struct nsc_gpio_ops *amp, unsigned iminor);
    - int (*gpio_get) (unsigned iminor);
    +
    + /* bit-wide value interface */
    + u32 (*gpio_get) (unsigned iminor);
    void (*gpio_set) (unsigned iminor, int state);
    void (*gpio_change) (unsigned iminor);
    - int (*gpio_current) (unsigned iminor);
    - struct device* dev; /* for dev_dbg() support, set in init */
    + u32 (*gpio_current) (unsigned iminor);
    +
    + /* want to restore set-hi()/set-lo() for PXA, which has
    + separate set and clear registers/insns, allowing PXA to
    + avoid read-modify-write cycles (IIUC). Im missing
    + something though, since RMW cycles dont pertain to single
    + bits, but rather to ports (where an RMW is needed to
    + preserve un-changed bits) void (*gpio_set_hi) (unsigned
    + iminor); void (*gpio_set_lo) (unsigned iminor);
    + */
    +
    + /* port-wide accessors (thanks Chris). For hardware which can
    + exploit it, gpio_setclr_port() separates set and clear
    + params to avoid Read-Modify-Write cycles.
    + */
    + u32 (*gpio_get_port) (unsigned portnum);
    + void (*gpio_set_port) (unsigned portnum, u32 bits);
    + void (*gpio_setclr_port) (unsigned portnum, u32 bits, u32 clr);
    + u32 (*gpio_current_port) (unsigned iminor);
    };

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

    @@ -38,3 +59,226 @@ extern ssize_t nsc_gpio_read(struct file

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

    +
    +/* GPIO-sysfs model */
    +#include <linux/device.h>
    +#include <linux/sysfs.h>
    +
    +/* basic gpio-sysfs object, with 3-D addressing. This allows the use
    + of combined-callbacks which decode the 3-D parameters, and perform
    + the selected operation. (We only use 2 axes, but keeping all 3 has
    + almost no cost, and gives more info and flexibility
    +*/
    +struct gpio_dev_attr {
    + struct device_attribute dev_attr;
    + u8 bitnum; /* bit index */
    + u8 portnum; /* port index */
    + u8 fn_slct; /* feature selector */
    + u8 bit_port_type; /* GPIO_BIT, GPIO_PORT */
    +};
    +#define GPIO_BIT_TYPE 0
    +#define GPIO_PORT_TYPE 1
    +
    +#define to_gpio_dev_attr(d) container_of(d, struct gpio_dev_attr, dev_attr)
    +
    +/* constants for 3 aspects of Pin Features used to initialize the
    + dev_attrs needed for the gpio-object's attr-files
    +
    + PF_SFX - pin-feature dev-attr filename suffix. Might be nice to
    + make these driver-configurable (L8r)
    +
    + PF_CMD - commands: constants are borrowed from fileops write
    + handler. The sysfs callbacks see them in the fn_slct field, so the
    + *_config handlers switch(fn_slct) to fetch the right bits.
    +
    + PF_MASK - pin-feature mask, ie config bits understood by the 2
    + client drivers of this module (scx200_gpio, pc8736x_gpio). This is
    + too coupled to the features exposed by nsc_gpio_ops->gpio_config(),
    + but its a simple re-do of the fileops write-handler.
    +
    + The cmd-set has expanded to cover all pin-features, not just
    + 'config' features, notably 'V' for values and 'C' for
    + current-values.
    +*/
    +#define PF_SFX_OUT_ENA _output_enabled
    +#define PF_SFX_TOTEM _totem
    +#define PF_SFX_PULLUP _pullup_enabled
    +#define PF_SFX_LOCKED _locked
    +#define PF_SFX_DEBOUNCE _debounced
    +#define PF_SFX_INT_ENA _int_enabled /* too exposing !?! */
    +#define PF_SFX_INT_TRIG _int_level_trig
    +
    +#define PF_CMD_OUT_ENA 'O' /* 'o' tristate */
    +#define PF_CMD_TOTEM 'T' /* 't' open-drain */
    +#define PF_CMD_PULLUP 'P' /* 'p' disables pullup */
    +#define PF_CMD_LOCKED 'L' /* no unlock */
    +/* these are newer than 18-rcX */
    +#define PF_CMD_DEBOUNCE 'D' /* 'd' disables */
    +#define PF_CMD_INT_ENA 'I' /* too exposing !?! */
    +#define PF_CMD_INT_TRIG 'E' /* 'e' level-triggered */
    +
    +#define PF_MASK_OUT_ENA 1 /* !tristate */
    +#define PF_MASK_TOTEM 2 /* !open-drain */
    +#define PF_MASK_PULLUP 4 /* !open-drain */
    +#define PF_MASK_LOCKED 8 /* no unlock possible */
    +#define PF_MASK_INT_ENA 16 /* some pins cant do this */
    +#define PF_MASK_INT_TRIG 32 /* level !edge */
    +#define PF_MASK_DEBOUNCE 64
    +
    +/* constants for expanded feature-set */
    +#define PF_SFX_VALUE _value /* input on pin */
    +#define PF_SFX_CURR _current /* last written value */
    +#define PF_SFX_STATUS _status /* _config ?? */
    +
    +#define PF_CMD_VALUE 'V' /* values-in often port-wide */
    +#define PF_CMD_CURR 'C' /* current values-out */
    +#define PF_CMD_STATUS 'S' /* possible chardev emulation */
    +
    +#define PF_MASK_VALUE 0 /* not for gpio_config() */
    +#define PF_MASK_CURR 0 /* use <0 if differences needed */
    +#define PF_MASK_STATUS 0
    +
    +
    +struct gpio_attributes {
    + struct gpio_dev_attr value; /* hw often is port-wide here */
    + struct gpio_dev_attr curr; /* driven value, may be != value */
    +
    + /* pin-features, which are mostly use-once, so hw often
    + provides it per-pin only. */
    + struct gpio_dev_attr oe; /* output-enable, !tristate */
    + struct gpio_dev_attr totem; /* !open-drain */
    + struct gpio_dev_attr pue; /* pullup-enabled */
    + struct gpio_dev_attr bounce; /* debounce circuit active */
    + struct gpio_dev_attr locked; /* once locked, no unlock */
    + struct gpio_dev_attr status; /* device-file compat-hack */
    +
    + struct gpio_dev_attr int_en; /* interrupt enable */
    + struct gpio_dev_attr int_lvl; /* int on level !edge */
    +};
    +
    +/* GPIO_ATTRS and GPIO_PORT_ATTRS macros let driver declare the
    + interfaces for the underlying hardware:
    +
    + static struct gpio_attributes port_0[] = {
    + GPIO_ATTRS(0,0), GPIO_ATTRS(0,1), ... GPIO_ATTRS(0,31) };
    +
    + static struct gpio_attributes port_set[] = {
    + GPIO_PORT_ATTRS(0,32), GPIO_PORT_ATTRS(1,32) };
    +*/
    +
    +#define GPIO_PIN(Port, Bit, Suffix, Mode, Show, Store, Cmd) \
    + { .dev_attr = __ATTR( bit_ ## Port.Bit ## Suffix, \
    + Mode, Show, Store), \
    + .bitnum = Bit, \
    + .portnum = Port, \
    + .fn_slct = Cmd, \
    + .bit_port_type = GPIO_BIT_TYPE }
    +
    +#define SYSFS_CB_RD(Access) nsc_gpio_sysfs_get_ ## Access
    +#define SYSFS_CB_WR(Access) nsc_gpio_sysfs_set_ ## Access
    +#define SYSFS_CB_NULL (void*)0
    +
    +#define GPIO_ATTR(Portnum, Bitnum, Feat, Suffix, Access) \
    + GPIO_PIN(Portnum, Bitnum, Suffix, \
    + S_IWUSR | S_IRUGO, \
    + SYSFS_CB_RD(Access), SYSFS_CB_WR(Access), Feat)
    +
    +/* WARNING: this macro hardwires one of 2 sysfs-callbacks (port-wide
    + vs bit-wide access) to each attr. This is broken, since each GPIO
    + driver (ie author) must determine whether each feature is exposed
    + per-pin or port-wide. ATM, they can only copy and modify this
    + macro. But then, theyre already wed to the nsc_gpio_ops ;-)
    +*/
    +#define GPIO_ATTRS(Port, Idx) { \
    + .value = GPIO_ATTR(Port, Idx, PF_CMD_VALUE, PF_SFX_VALUE, bitval), \
    + .curr = GPIO_ATTR(Port, Idx, PF_CMD_CURR, PF_SFX_CURR, bitval), \
    + .oe = GPIO_ATTR(Port, Idx, PF_CMD_OUT_ENA, PF_SFX_OUT_ENA, bitconf), \
    + .pue = GPIO_ATTR(Port, Idx, PF_CMD_PULLUP, PF_SFX_PULLUP, bitconf), \
    + .totem = GPIO_ATTR(Port, Idx, PF_CMD_TOTEM, PF_SFX_TOTEM, bitconf), \
    + .locked = GPIO_ATTR(Port, Idx, PF_CMD_LOCKED, PF_SFX_LOCKED, bitconf), \
    + .bounce = GPIO_ATTR(Port, Idx, PF_CMD_DEBOUNCE, PF_SFX_DEBOUNCE,bitconf), \
    + .status = GPIO_ATTR(Port, Idx, PF_CMD_STATUS, PF_SFX_STATUS, bitconf), \
    + .int_en = GPIO_ATTR(Port, Idx, PF_CMD_INT_ENA, PF_SFX_INT_ENA, bitconf), \
    + .int_lvl= GPIO_ATTR(Port, Idx, PF_CMD_INT_TRIG, PF_SFX_INT_TRIG,bitconf) \
    +}
    +
    +/* port-wide sysfs access */
    +
    +#define GPIO_PORT(Port, Sfx, Mode, Show, Store, Cmd) \
    + { .dev_attr = __ATTR( port_ ## Port ## Sfx, \
    + Mode, Show, Store), \
    + .portnum = Port, \
    + .bitnum = -1, \
    + .fn_slct = Cmd, \
    + .bit_port_type = GPIO_PORT_TYPE }
    +
    +#define GPIO_PORT_ATTR(Portnum, Cmd, Suffix, Access) \
    + GPIO_PORT( Portnum, Suffix, \
    + S_IWUSR | S_IRUGO, \
    + SYSFS_CB_RD(Access), SYSFS_CB_WR(Access), Cmd)
    +
    +/* WARNING: same caveats as above apply, only moreso */
    +
    +#define GPIO_PORT_ATTRS(Port) { \
    + .value = GPIO_PORT_ATTR(Port, PF_CMD_VALUE, PF_SFX_VALUE, portval), \
    + .curr = GPIO_PORT_ATTR(Port, PF_CMD_CURR, PF_SFX_CURR, portval), \
    + .oe = GPIO_PORT_ATTR(Port, PF_CMD_OUT_ENA, PF_SFX_OUT_ENA, portconf), \
    + .pue = GPIO_PORT_ATTR(Port, PF_CMD_PULLUP, PF_SFX_PULLUP, portconf), \
    + .totem = GPIO_PORT_ATTR(Port, PF_CMD_TOTEM, PF_SFX_TOTEM, portconf), \
    + .locked = GPIO_PORT_ATTR(Port, PF_CMD_LOCKED, PF_SFX_LOCKED, portconf), \
    + .bounce = GPIO_PORT_ATTR(Port, PF_CMD_DEBOUNCE, PF_SFX_DEBOUNCE,portconf), \
    + .status = GPIO_PORT_ATTR(Port, PF_CMD_STATUS, PF_SFX_STATUS, portconf), \
    + .int_en = GPIO_PORT_ATTR(Port, PF_CMD_INT_ENA, PF_SFX_INT_ENA, portconf), \
    + .int_lvl= GPIO_PORT_ATTR(Port, PF_CMD_INT_TRIG, PF_SFX_INT_TRIG,portconf) \
    +}
    +
    +/* attr-file callbacks, named per glob-name:
    + nsc_gpio_sysfs_{get,set}_{bit,port}{val,conf}
    +
    + -bitval,portval separate for speed, simplicity
    + -bitconf is combo-callback, handling slower, per-pin properties
    + -portconf - synthesize port-wide from bit-wide
    + -get,set as required by fn-prototypes
    +*/
    +extern ssize_t nsc_gpio_sysfs_get_bitval(struct device *dev,
    + struct device_attribute *devattr,
    + char *buf);
    +extern ssize_t nsc_gpio_sysfs_set_bitval(struct device *dev,
    + struct device_attribute *devattr,
    + const char *buf, size_t count);
    +
    +extern ssize_t nsc_gpio_sysfs_get_portval(struct device *dev,
    + struct device_attribute *devattr,
    + char *buf);
    +extern ssize_t nsc_gpio_sysfs_set_portval(struct device *dev,
    + struct device_attribute *devattr,
    + const char *buf, size_t count);
    +
    +
    +extern ssize_t nsc_gpio_sysfs_get_bitconf(struct device *dev,
    + struct device_attribute *devattr,
    + char *buf);
    +extern ssize_t nsc_gpio_sysfs_set_bitconf(struct device *dev,
    + struct device_attribute *devattr,
    + const char *buf, size_t count);
    +
    +extern ssize_t nsc_gpio_sysfs_get_portconf(struct device *dev,
    + struct device_attribute *devattr,
    + char *buf);
    +extern ssize_t nsc_gpio_sysfs_set_portconf(struct device *dev,
    + struct device_attribute *devattr,
    + const char *buf, size_t count);
    +
    +
    +/* device-create-file for all attrs, all bits in port.
    + called by higher level client _inits unless nobits=1.
    + Also called for ports-init !!
    +*/
    +extern void nsc_gpio_sysfs_bits_init(struct device* dev,
    + struct gpio_attributes pp[],
    + int numdevs);
    +
    +extern void nsc_gpio_sysfs_bits_fini(struct device* dev,
    + struct gpio_attributes pp[],
    + int numdevs);
    +
    diff -ruNp -X dontdiff -X exclude-diffs ../linux-2.6.18-rc3-mm2-sk/include/linux/scx200_gpio.h roll3-m2/include/linux/scx200_gpio.h
    --- ../linux-2.6.18-rc3-mm2-sk/include/linux/scx200_gpio.h 2006-08-06 10:19:38.000000000 -0600
    +++ roll3-m2/include/linux/scx200_gpio.h 2006-08-08 12:51:09.000000000 -0600
    @@ -18,7 +18,7 @@ extern struct nsc_gpio_ops scx200_gpio_o

    /* returns the value of the GPIO pin */

    -static inline int scx200_gpio_get(unsigned index) {
    +static inline u32 scx200_gpio_get(unsigned index) {
    __SCx200_GPIO_BANK;
    __SCx200_GPIO_IOADDR + 0x04;
    __SCx200_GPIO_INDEX;
    @@ -30,7 +30,7 @@ static inline int scx200_gpio_get(unsign
    driven if the GPIO is configured as an output, it might not be the
    state of the GPIO right now if the GPIO is configured as an input) */

    -static inline int scx200_gpio_current(unsigned index) {
    +static inline u32 scx200_gpio_current(unsigned index) {
    __SCx200_GPIO_BANK;
    __SCx200_GPIO_INDEX;

    @@ -83,6 +83,30 @@ static inline void scx200_gpio_change(un
    __SCx200_GPIO_OUT;
    }

    +/* return the value of a whole port (bank) */
    +static inline u32 scx200_gpio_get_port(unsigned port)
    +{
    + unsigned bank = port & 0x1f;
    + __SCx200_GPIO_IOADDR + 0x04;
    +
    + return inl(ioaddr);
    +}
    +
    +/* return the value of a whole port (bank) */
    +static inline void scx200_gpio_setclr_port(unsigned port, u32 bits, u32 mask)
    +{
    + unsigned bank = port & 0x1f;
    + __SCx200_GPIO_IOADDR;
    + __SCx200_GPIO_SHADOW;
    + *shadow = (*shadow & ~mask) | (bits & mask);
    + __SCx200_GPIO_OUT;
    +}
    +
    +static inline void scx200_gpio_set_port(unsigned port, u32 bits)
    +{
    + scx200_gpio_setclr_port(port, bits, ~bits);
    +}
    +
    #undef __SCx200_GPIO_BANK
    #undef __SCx200_GPIO_IOADDR
    #undef __SCx200_GPIO_SHADOW

    -
    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/

    \
     
     \ /
      Last update: 2006-08-09 01:05    [W:0.126 / U:97.276 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site