2006-08-01 21:25:38

by Jim Cromie

[permalink] [raw]
Subject: Re: [RFC] Proposal: common kernel-wide GPIO interface

Robert Schwebel wrote:
> Chris,
>
> On Fri, Jul 28, 2006 at 09:44:40PM +0100, Chris Boot wrote:
>
>> I propose to develop a common way of registering and accessing GPIO pins on
>> various devices.
>>
>
> I've attached the gpio framework we have developed a while ago; it is
> not ready for upstream, only tested on pxa and has probably several
> other drawbacks, but may be a start for your activities. One of the
> problems we've recently seen is that for example on PowerPCs you don't
> have such a clear "this is gpio pin x" nomenclature, so the question
> would be how to do the mapping here.
>
> Robert
>
this is cool to see. Using a class-driver is very different from the
vtable-approach
that I used (struct nsc_gpio_ops) in pc8736x_gpio and scx200_gpio.

Are any of the limitation youve cited above related to the
/sys/class/gpio paths below ?

+ To set pin 63 to low (to start the motor) do a:
+ $ echo 0 > /sys/class/gpio/gpio63/level
+ Or to stop the motor again:
+ $ echo 1 > /sys/class/gpio/gpio63/level
+ To get the level of the key (pin 8) do:
+ $ cat /sys/class/gpio/gpio8/level
+ The result will be 1 or 0.
+
+ To add new GPIO pins at runtime (lets say pin 88 should be an input)
+ you can do a:
+ $ echo 88:in > /sys/class/gpio/map_gpio
+ The same with a new GPIO pin 95, it should be an output and at high level:
+ $ echo 95:out:hi > /sys/class/gpio/map_gpio
+





2006-08-02 07:28:51

by Robert Schwebel

[permalink] [raw]
Subject: Re: [RFC] Proposal: common kernel-wide GPIO interface

On Tue, Aug 01, 2006 at 03:25:32PM -0600, Jim Cromie wrote:
> >I've attached the gpio framework we have developed a while ago; it is
> >not ready for upstream, only tested on pxa and has probably several
> >other drawbacks, but may be a start for your activities. One of the
> >problems we've recently seen is that for example on PowerPCs you don't
> >have such a clear "this is gpio pin x" nomenclature, so the question
> >would be how to do the mapping here.
> >
> >Robert
> >
> this is cool to see. Using a class-driver is very different from the
> vtable-approach
> that I used (struct nsc_gpio_ops) in pc8736x_gpio and scx200_gpio.
>
> Are any of the limitation youve cited above related to the
> /sys/class/gpio paths below ?
>
> + To set pin 63 to low (to start the motor) do a:
> + $ echo 0 > /sys/class/gpio/gpio63/level
> + Or to stop the motor again:
> + $ echo 1 > /sys/class/gpio/gpio63/level
> + To get the level of the key (pin 8) do:
> + $ cat /sys/class/gpio/gpio8/level
> + The result will be 1 or 0.
> +
> + To add new GPIO pins at runtime (lets say pin 88 should be an
> input)
> + you can do a:
> + $ echo 88:in > /sys/class/gpio/map_gpio
> + The same with a new GPIO pin 95, it should be an output and at
> high level:
> + $ echo 95:out:hi > /sys/class/gpio/map_gpio
> +

Please re-read my original mail.

Robert
--
Dipl.-Ing. Robert Schwebel | http://www.pengutronix.de
Pengutronix - Linux Solutions for Science and Industry
Handelsregister: Amtsgericht Hildesheim, HRA 2686
Hannoversche Str. 2, 31134 Hildesheim, Germany
Phone: +49-5121-206917-0 | Fax: +49-5121-206917-9

2006-08-02 17:59:05

by Lennart Sorensen

[permalink] [raw]
Subject: Re: [RFC] Proposal: common kernel-wide GPIO interface

On Tue, Aug 01, 2006 at 03:25:32PM -0600, Jim Cromie wrote:
> this is cool to see. Using a class-driver is very different from the
> vtable-approach
> that I used (struct nsc_gpio_ops) in pc8736x_gpio and scx200_gpio.
>
> Are any of the limitation youve cited above related to the
> /sys/class/gpio paths below ?
>
> + To set pin 63 to low (to start the motor) do a:
> + $ echo 0 > /sys/class/gpio/gpio63/level
> + Or to stop the motor again:
> + $ echo 1 > /sys/class/gpio/gpio63/level
> + To get the level of the key (pin 8) do:
> + $ cat /sys/class/gpio/gpio8/level
> + The result will be 1 or 0.
> +
> + To add new GPIO pins at runtime (lets say pin 88 should be an
> input)
> + you can do a:
> + $ echo 88:in > /sys/class/gpio/map_gpio
> + The same with a new GPIO pin 95, it should be an output and at
> high level:
> + $ echo 95:out:hi > /sys/class/gpio/map_gpio
> +

How do you deal with having multiple places that provide GPIOs then? I
may have 8 pins on a PCI UART chip, 22 on my super io chip, 16 on my
cpu, etc. How would this be mapped if you only have one map_gpio
method? It is much simpler to code for knowing pin 0 to 7 of device
uartgpio is where my UART pins are, and some other device has 22 pins
for the super io chip. If they all ended up in one place with
consequative numbers it would be a real pain.

Sometimes it is also nice to be able to control multiple pins as a block,
which only a few gpio interfaces seem to provide (they all seem to think
they should only be moved one pin at a time, which makes for a lot more
system calls to get things done).

Right now I am working on adding some stuff to the jsm driver to use an
Exar uart along with using the gpios, and so far I added gpio access
similar to how scx200_gpio does things, using minors 0 to 7 for the 8
pins on the first uart, 8 to 15 for the second, and so on. What to name
the /dev entries is a different issue. I can identify which device to
look for based on the /sys info for which pci slot the uart is connected
to. I am not sure how this would tie into a generic gpio design.

Does your gpio design deal with all the things gpios often do:
input/output/tristate
high/low
generate interrupt
edge/level trigger
high or low level/leading or trailing edge trigger

--
Len Sorensen

2006-08-02 20:48:20

by Jim Cromie

[permalink] [raw]
Subject: Re: [RFC] Proposal: common kernel-wide GPIO interface

Lennart Sorensen wrote:
> On Tue, Aug 01, 2006 at 03:25:32PM -0600, Jim Cromie wrote:
>
>> this is cool to see. Using a class-driver is very different from the
>> vtable-approach
>> that I used (struct nsc_gpio_ops) in pc8736x_gpio and scx200_gpio.
>>
>> Are any of the limitation youve cited above related to the
>> /sys/class/gpio paths below ?
>>
>> + To set pin 63 to low (to start the motor) do a:
>> + $ echo 0 > /sys/class/gpio/gpio63/level
>> + Or to stop the motor again:
>> + $ echo 1 > /sys/class/gpio/gpio63/level
>> + To get the level of the key (pin 8) do:
>> + $ cat /sys/class/gpio/gpio8/level
>> + The result will be 1 or 0.
>> +
>> + To add new GPIO pins at runtime (lets say pin 88 should be an
>> input)
>> + you can do a:
>> + $ echo 88:in > /sys/class/gpio/map_gpio
>> + The same with a new GPIO pin 95, it should be an output and at
>> high level:
>> + $ echo 95:out:hi > /sys/class/gpio/map_gpio
>> +
>>
>
> How do you deal with having multiple places that provide GPIOs then?

pc8736x_gpio and scx200_gpio appear here:

soekris:/sys/devices/platform# ls pc8736x_gpio.0/
Display all 292 possibilities? (y or n)

soekris:/sys/devices/platform# ls scx200_gpio.0/
Display all 532 possibilities? (y or n)


soekris:/sys/devices/platform# ls scx200_gpio.0/bit_0.0_*
scx200_gpio.0/bit_0.0_current_output scx200_gpio.0/bit_0.0_pullup_enabled
scx200_gpio.0/bit_0.0_debounced scx200_gpio.0/bit_0.0_status
scx200_gpio.0/bit_0.0_locked scx200_gpio.0/bit_0.0_totem
scx200_gpio.0/bit_0.0_output_enabled scx200_gpio.0/bit_0.0_value


Did you mean to ask that question of Robert ?

I'll rephrase my Q here.

/sys/class/gpio/gpio63/

this suggests that either
- only 1 GPIO device can register (bad)
- reservations might be taken in module-load order, and assigned
numerically (bad-subtle)

Using another path (like /sys/devices/platform/scx200_gpio.%d/ )
which names the driver (or some other structural info) seems much more
stable in the face of combinations of GPIO hardware.

FWIW, I didnt add the .0 to the directories, I think that was added for
me by the device-core,
(warmfuzzy) so Id expect it to handle .1,2,3 etc..


> I
> may have 8 pins on a PCI UART chip, 22 on my super io chip, 16 on my
> cpu, etc. How would this be mapped if you only have one map_gpio
> method? It is much simpler to code for knowing pin 0 to 7 of device
> uartgpio is where my UART pins are, and some other device has 22 pins
> for the super io chip. If they all ended up in one place with
> consequative numbers it would be a real pain.
>
> Sometimes it is also nice to be able to control multiple pins as a block,
> which only a few gpio interfaces seem to provide (they all seem to think
> they should only be moved one pin at a time, which makes for a lot more
> system calls to get things done).
>
Both GPIO chips Ive touched have port-wide read and write.
I consider it an essential minimum feature in the driver, for hardware
that supports it.
Other pin features (OE, etc) are only controllable per-pin.
If we synthesize port-wide from per-pin, then we get a bit/port agnostic
interface.
( driver users must still be cognizant of the limitations of synthetic
OutputEnable,
where tri-stating would take many bus cycles )

> Right now I am working on adding some stuff to the jsm driver to use an
> Exar uart along with using the gpios, and so far I added gpio access
> similar to how scx200_gpio does things, using minors 0 to 7 for the 8
> pins on the first uart, 8 to 15 for the second, and so on. What to name
> the /dev entries is a different issue. I can identify which device to
> look for based on the /sys info for which pci slot the uart is connected
> to. I am not sure how this would tie into a generic gpio design.
>
> Does your gpio design
I want to separate my answers -

- 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, via struct nsc_gpio_ops,
but it seems a bad idea to expose them as device-files. ;-)

- This thread is about a new interface, I think we're all tacitly
agreeing on :
a sysfs based GPIO-attr representation
some of us want/demand a port-interface where hardware has portwide
read/write
a reservation scheme.

- Im working on a patch, which rendered the ls output I pasted above.
bits_ and ports_ agnostic
interfaces are nearly identical - its 0/1 vs 0xFF (hw dependent width)
no reservations yet :-/


> deal with all the things gpios often do:
>
char-dev interfaces in scx200_gpio 18-rc are compatible with legacy,
pc87360 is new (and same).
my sysfs-gpio patch actually has a half-baked compatibly hack on the
_status attr,
platform# more scx200_gpio.0/bit_0.0_status
io00: 0x0044 TS OD PUE EDGE LO DEBOUNCE io:1/1

> input/output/tristate
> high/low
>
not yet on these: patches/clues welcome.
> generate interrupt
> edge/level trigger
> high or low level/leading or trailing edge trigger
>
> --
> Len Sorensen
>
>

thanks for the input
Jim Cromie

2006-08-03 13:57:04

by Lennart Sorensen

[permalink] [raw]
Subject: Re: [RFC] Proposal: common kernel-wide GPIO interface

On Wed, Aug 02, 2006 at 02:48:33PM -0600, Jim Cromie wrote:
> pc8736x_gpio and scx200_gpio appear here:
>
> soekris:/sys/devices/platform# ls pc8736x_gpio.0/
> Display all 292 possibilities? (y or n)
>
> soekris:/sys/devices/platform# ls scx200_gpio.0/
> Display all 532 possibilities? (y or n)
>
>
> soekris:/sys/devices/platform# ls scx200_gpio.0/bit_0.0_*
> scx200_gpio.0/bit_0.0_current_output scx200_gpio.0/bit_0.0_pullup_enabled
> scx200_gpio.0/bit_0.0_debounced scx200_gpio.0/bit_0.0_status
> scx200_gpio.0/bit_0.0_locked scx200_gpio.0/bit_0.0_totem
> scx200_gpio.0/bit_0.0_output_enabled scx200_gpio.0/bit_0.0_value

Hmm, that certainly is different than the old /dev/... interface. Could
be nice.

> Did you mean to ask that question of Robert ?

I might have. :)

> I'll rephrase my Q here.
>
> /sys/class/gpio/gpio63/
>
> this suggests that either
> - only 1 GPIO device can register (bad)

Unacceptably bad. I currently use anywhere from 2 to 4 different
devices with GPIOs.

> - reservations might be taken in module-load order, and assigned
> numerically (bad-subtle)

Too messy.

> Using another path (like /sys/devices/platform/scx200_gpio.%d/ )
> which names the driver (or some other structural info) seems much more
> stable in the face of combinations of GPIO hardware.

That I think is perfectly easy to work with, as long as I can somehow
find which device (usually on PCI bus in my case) goes to which
directory. I suspect that would be the case.

> FWIW, I didnt add the .0 to the directories, I think that was added for
> me by the device-core,
> (warmfuzzy) so Id expect it to handle .1,2,3 etc..

That sounds nice and convinient.

> Both GPIO chips Ive touched have port-wide read and write.
> I consider it an essential minimum feature in the driver, for hardware
> that supports it.
> Other pin features (OE, etc) are only controllable per-pin.
> If we synthesize port-wide from per-pin, then we get a bit/port agnostic
> interface.
> ( driver users must still be cognizant of the limitations of synthetic
> OutputEnable,
> where tri-stating would take many bus cycles )

I only consider the change of state on output pins, or reading state of
input pins to be a requirement for port-wide. Changing between
input/output, and enable and tristate and such, I have no problem with
doing bit by bit, since that is setup stuff.

> - 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, via struct nsc_gpio_ops,
> but it seems a bad idea to expose them as device-files. ;-)

Well I haven't seen a gpio driver that did port wide yet. I treat the
ppdev as 8 gpio pins, so at least for that I get port wide.

> - This thread is about a new interface, I think we're all tacitly
> agreeing on :
> a sysfs based GPIO-attr representation
> some of us want/demand a port-interface where hardware has portwide
> read/write
> a reservation scheme.

Sounds lovely to me.

> - Im working on a patch, which rendered the ls output I pasted above.
> bits_ and ports_ agnostic
> interfaces are nearly identical - its 0/1 vs 0xFF (hw dependent width)
> no reservations yet :-/

I look forward to seeing it.

> char-dev interfaces in scx200_gpio 18-rc are compatible with legacy,
> pc87360 is new (and same).
> my sysfs-gpio patch actually has a half-baked compatibly hack on the
> _status attr,
> platform# more scx200_gpio.0/bit_0.0_status
> io00: 0x0044 TS OD PUE EDGE LO DEBOUNCE io:1/1

Keeping the legacy for at least a while is probably a requirement since
people are already using those interfaces on that hardware.

> not yet on these: patches/clues welcome.
> >generate interrupt
> >edge/level trigger
> >high or low level/leading or trailing edge trigger

Well tying into interrupt handlers would be tricky I suspect. On the
other hand someone might have a use for the feature. I just mentioned
them because it is a feature of some of the devices I use, although I
don't use them for anything other than input/output myself.

--
Len Sorensen

2006-08-03 15:43:09

by Robert Schwebel

[permalink] [raw]
Subject: Re: [RFC] Proposal: common kernel-wide GPIO interface

On Thu, Aug 03, 2006 at 09:55:58AM -0400, Lennart Sorensen wrote:
> > I'll rephrase my Q here.
> >
> > /sys/class/gpio/gpio63/
> >
> > this suggests that either - only 1 GPIO device can register (bad)
>
> Unacceptably bad. I currently use anywhere from 2 to 4 different
> devices with GPIOs.

Sure, it would probably be nice to have GPIO devices in sysfs dirs of
their respective parent. The variant I've sent was just a quick-n-dirty
framework we hacked for a project.

Robert
--
Dipl.-Ing. Robert Schwebel | http://www.pengutronix.de
Pengutronix - Linux Solutions for Science and Industry
Handelsregister: Amtsgericht Hildesheim, HRA 2686
Hannoversche Str. 2, 31134 Hildesheim, Germany
Phone: +49-5121-206917-0 | Fax: +49-5121-206917-9

2006-08-08 23:01:28

by Jim Cromie

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

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

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

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

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

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

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

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

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

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

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

@@ -9,6 +8,8 @@
Copyright (c) 2001,2002 Christer Weinigel <[email protected]>,
*/

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

- Copyright (c) 2001,2002 Christer Weinigel <[email protected]> */
+ Copyright (c) 2001,2002 Christer Weinigel <[email protected]>
+*/
+
+#define DRVNAME "scx200_gpio"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

- Copyright (c) 2005 Jim Cromie <[email protected]>
+ Copyright (c) 2005,2006 Jim Cromie <[email protected]>

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

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

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

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

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

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

/* returns the value of the GPIO pin */

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

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

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

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


2006-08-09 17:11:58

by Jim Cromie

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

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

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

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

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

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

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

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

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

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

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

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

@@ -9,6 +8,8 @@
Copyright (c) 2001,2002 Christer Weinigel <[email protected]>,
*/

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

- Copyright (c) 2001,2002 Christer Weinigel <[email protected]> */
+ Copyright (c) 2001,2002 Christer Weinigel <[email protected]>
+*/
+
+#define DRVNAME "scx200_gpio"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

- Copyright (c) 2005 Jim Cromie <[email protected]>
+ Copyright (c) 2005,2006 Jim Cromie <[email protected]>

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

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

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

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

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

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

/* returns the value of the GPIO pin */

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

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

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

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


Attachments:
diff.roll3-m2-good-1 (47.36 kB)
exer-gpio.sh (2.30 kB)
Download all attachments