Skip to contentSkip to conversation

GPIO implementation framework

PATCH In review David Brownell 6 participants 60 messages Oct 30, 2007 – Apr 7, 2008 1 message not in the archive

Subsystems (9)
lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): [email protected]

#1 lore raw

PATCH 1/4 GPIO implementation framework

Provides new implementation infrastructure that platforms may choose to use when implementing the GPIO programming interface. Platforms can update their GPIO support to use this. The downside is slower access to non-inlined GPIOs; rarely a problem except when bitbanging some protocol. The upside is:

* Providing two features which were "want to have (but OK to defer)" when GPIO interfaces were first discussed in November 2006:

- A "struct gpio_chip" to plug in GPIOs that aren't directly supported
by SOC platforms, but come from FPGAs or other multifunction devices (like UCB-1x00 GPIOs).

- Full support for message-based GPIO expanders, needing a gpio_chip
hookup; previous support for this part of the programming interface was just stubs. (One example: the widely used pcf8574 I2C chips, with 8 GPIOs each.)

* Including a non-stub implementation of the gpio_{request,free}() calls, which makes those calls much more useful. The diagnostic labels are also recorded given DEBUG_FS, so /sys/kernel/debug/gpio can show a snapshot of all GPIOs known to this infrastructure.

The driver programming interfaces introduced in 2.6.21 do not change at all; this new infrastructure is entirely below the covers.

One open issue is how to handle IRQs reported through GPIO expanders. For example, I2C chips may be able to report IRQs, but the genirq framework won't much like the need to manage them in can-sleep contexts or the way their irq_chip structures can be removed.

This opens the door to an augmented programming interface, addressing GPIOs by chip and index. That could be used as a performance tweak (lookup once and cache, avoiding locking and lookup overheads) or to support transient GPIOs which aren't registered in the integer GPIO namespace (a USB-to-GPIO adapter, GPIOs coupled to some other type of add-on card, etc).

---
 Documentation/gpio.txt     |   12 -
 include/asm-generic/gpio.h |  127 +++++++++++
 lib/Kconfig                |    6 
 lib/Makefile               |    2 
 lib/gpiolib.c              |  500 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 644 insertions(+), 3 deletions(-)
Documentation/gpio.txt +9 -3
@@ -63,7 +63,9 @@ and that can be critical for glue logic.
 Plus, this doesn't define an implementation framework, just an interface.
 One platform might implement it as simple inline functions accessing chip
 registers; another might implement it by delegating through abstractions
-used for several very different kinds of GPIO controller.
+used for several very different kinds of GPIO controller.  (There is some
+library code supporting such an implementation strategy, but drivers using
+the interface should not care if that's how the interface is implemented.)
 
 That said, if the convention is supported on their platform, drivers should
 use it when possible.  Platforms should declare GENERIC_GPIO support in
@@ -223,6 +225,9 @@ Note that requesting a GPIO does NOT cau
 way; it just marks that GPIO as in use.  Separate code must handle any
 pin setup (e.g. controlling which pin the GPIO uses, pullup/pulldown).
 
+Also note that it's your responsibility to have stopped using a GPIO
+before you free it.
+
 
 GPIOs mapped to IRQs
 --------------------
@@ -238,7 +243,7 @@ map between them using calls like:
 
 Those return either the corresponding number in the other namespace, or
 else a negative errno code if the mapping can't be done.  (For example,
-some GPIOs can't used as IRQs.)  It is an unchecked error to use a GPIO
+some GPIOs can't be used as IRQs.)  It is an unchecked error to use a GPIO
 number that wasn't set up as an input using gpio_direction_input(), or
 to use an IRQ number that didn't originally come from gpio_to_irq().
 
@@ -299,6 +304,7 @@ Related to multiplexing is configuration
 pulldowns integrated on some platforms.  Not all platforms support them,
 or support them in the same way; and any given board might use external
 pullups (or pulldowns) so that the on-chip ones should not be used.
+(When a circuit needs 5 kOhm, on-chip 100 kOhm resistors won't do.)
 
 There are other system-specific mechanisms that are not specified here,
 like the aforementioned options for input de-glitching and wire-OR output.
@@ -312,4 +318,4 @@ Dynamic definition of GPIOs is not curre
 a side effect of configuring an add-on board with some GPIO expanders.
 
 These calls are purely for kernel space, but a userspace API could be built
-on top of it.
+on top of them.
lib/Kconfig +6 -0
@@ -141,4 +141,10 @@ config HAS_DMA
 config CHECK_SIGNATURE
 	bool
 
+#
+# gpiolib is selected if needed
+#
+config GPIO_LIB
+	boolean
+
 endmenu
include/asm-generic/gpio.h +127 -0

137 lines not on this page · raw message

lib/Makefile +2 -0
@@ -68,6 +68,8 @@ obj-$(CONFIG_FAULT_INJECTION) += fault-i
 
 lib-$(CONFIG_GENERIC_BUG) += bug.o
 
+lib-$(CONFIG_GPIO_LIB) += gpiolib.o
+
 hostprogs-y	:= gen_crc32table
 clean-files	:= crc32table.h
 
lib/gpiolib.c new file +500 -0

500 lines not on this page · raw message

#2 lore raw
Quoted 5 lines · David Brownell

Provides new implementation infrastructure that platforms may choose to use when implementing the GPIO programming interface.  Platforms can update their GPIO support to use this.  The downside is slower access to non-inlined GPIOs; rarely a problem except when bitbanging some protocol.

I was asked just what that overhead *is* ... and it surprised me. A summary of the results is appended to this note.

Fortuntely it turns out those problems all go away if the gpiolib code uses a *raw* spinlock to guard its table lookups. With a raw spinlock, any performance impact of gpiolib seems to be well under a microsecond in this bitbang context (and not objectionable). Preempt became free; enabling debug options had only a minor cost.

That's as it should be, since the only substantive changes were to grab and release a lock, do one table lookup a bit differently, and add one indirection function call ... changes which should not have any visible performance impact on per-bit codepaths, and one might expect to cost on the order of one dozen instructions.

So the next version of this code will include a few minor bugfixes, and will also use a raw spinlock to protect that table. A raw lock seems appropriate there in any case, since non-sleeping GPIOs should be accessible from hardirq contexts even on RT kernels.

If anyone has any strong arguments against using a raw spinlock to protect that table, it'd be nice to know them sooner rather than later.

- Dave

SUMMARY:

Using the i2c-gpio driver on a preempt kernel with all the usual kernel debug options enabled, the per-bit times (*) went up in a bad way: from about 6.4 usec/bit (original GPIO code on this board) up to about 11.2 usec/bit (just switching to gpiolib), which is well into "objectionable overhead" territory for bit access.

Just enabling preempt shot the time up to 7.4 usec/bit ... which is also objectionable (it's all-the-time overhead that is clearly needless), but much less so.

Converting the table lock to be a raw spinlock essentially removed all non-debug overheads. It took enabling all those debug options plus internal gpiolib debugging overhead to get those times up to the 7.4 usec/bit that previously applied even with just preempt.

(*) Those times being eyeballed medians; I didn't make time to find
a way to export a few thousand measurements from the tool and do the math. The typical range was +/- one usec.

The numbers include udelay() calls, so the relevant point is the time *delta* attributable only to increased gpiolib costs, not the base time (with udelays). The delta probably reflects on the order of four GPIO calls: set two different bits, clear one of them, and read it to make sure it cleared.

Quoted 21 lines

The upside is:

  * Providing two features which were "want to have (but OK to defer)" when GPIO interfaces were first discussed in November 2006:

    -   A "struct gpio_chip" to plug in GPIOs that aren't directly supported by SOC platforms, but come from FPGAs or other multifunction devices (like UCB-1x00 GPIOs).

    -   Full support for message-based GPIO expanders, needing a gpio_chip hookup; previous support for this part of the programming interface was just stubs.  (One example: the widely used pcf8574 I2C chips, with 8 GPIOs each.)

  * Including a non-stub implementation of the gpio_{request,free}() calls, which makes those calls much more useful.  The diagnostic labels are also recorded given DEBUG_FS, so /sys/kernel/debug/gpio can show a snapshot of all GPIOs known to this infrastructure.

The driver programming interfaces introduced in 2.6.21 do not change at all; this new infrastructure is entirely below the covers.

#3 lore raw

Hi David,

I hope I was not late giving my humble feedback on this framework :-)

Can we use "per gpio based" structure instead of "per gpio_chip" based one, just like what the generic IRQ layer is doing nowadays? So that

a. you don't have to declare per gpio_chip "can_sleep", "is_out" and "requested".
Those will be just bits of properties of a single GPIO.

b. and furthur more, one can avoid the use of ARCH_GPIOS_PER_CHIP, which leads to many holes

c. gpio_to_chip() will be made easy and straight forward

d. granularity of spin_lock()/_unlock() can be made small (per GPIO instead of per gpio_chip)

What do you think?

- eric

Quoted 88 lines · David Brownell
Quoted 5 lines · David Brownell

Provides new implementation infrastructure that platforms may choose to use when implementing the GPIO programming interface. Platforms can update their GPIO support to use this. The downside is slower access to non-inlined GPIOs; rarely a problem except when bitbanging some protocol.

I was asked just what that overhead *is* ... and it surprised me. A summary of the results is appended to this note.

Fortuntely it turns out those problems all go away if the gpiolib code uses a *raw* spinlock to guard its table lookups. With a raw spinlock, any performance impact of gpiolib seems to be well under a microsecond in this bitbang context (and not objectionable). Preempt became free; enabling debug options had only a minor cost.

That's as it should be, since the only substantive changes were to grab and release a lock, do one table lookup a bit differently, and add one indirection function call ... changes which should not have any visible performance impact on per-bit codepaths, and one might expect to cost on the order of one dozen instructions.

So the next version of this code will include a few minor bugfixes, and will also use a raw spinlock to protect that table. A raw lock seems appropriate there in any case, since non-sleeping GPIOs should be accessible from hardirq contexts even on RT kernels.

If anyone has any strong arguments against using a raw spinlock to protect that table, it'd be nice to know them sooner rather than later.

- Dave

SUMMARY:

Using the i2c-gpio driver on a preempt kernel with all the usual kernel debug options enabled, the per-bit times (*) went up in a bad way: from about 6.4 usec/bit (original GPIO code on this board) up to about 11.2 usec/bit (just switching to gpiolib), which is well into "objectionable overhead" territory for bit access.

Just enabling preempt shot the time up to 7.4 usec/bit ... which is also objectionable (it's all-the-time overhead that is clearly needless), but much less so.

Converting the table lock to be a raw spinlock essentially removed all non-debug overheads. It took enabling all those debug options plus internal gpiolib debugging overhead to get those times up to the 7.4 usec/bit that previously applied even with just preempt.

(*) Those times being eyeballed medians; I didn't make time to find
a way to export a few thousand measurements from the tool and do the math. The typical range was +/- one usec.

The numbers include udelay() calls, so the relevant point is the time *delta* attributable only to increased gpiolib costs, not the base time (with udelays). The delta probably reflects on the order of four GPIO calls: set two different bits, clear one of them, and read it to make sure it cleared.

Quoted 21 lines

The upside is:

* Providing two features which were "want to have (but OK to defer)" when GPIO interfaces were first discussed in November 2006:

-A "struct gpio_chip" to plug in GPIOs that aren't directly supported by SOC platforms, but come from FPGAs or other multifunction devices (like UCB-1x00 GPIOs).

-Full support for message-based GPIO expanders, needing a gpio_chip hookup; previous support for this part of the programming interface was just stubs. (One example: the widely used pcf8574 I2C chips, with 8 GPIOs each.)

* Including a non-stub implementation of the gpio_{request,free}() calls, which makes those calls much more useful. The diagnostic labels are also recorded given DEBUG_FS, so /sys/kernel/debug/gpio can show a snapshot of all GPIOs known to this infrastructure.

The driver programming interfaces introduced in 2.6.21 do not change at all; this new infrastructure is entirely below the covers.

Signature · 2 lines
Cheers
- eric
#4 lore raw
Quoted 6 lines · eric miao

Hi David,

I hope I was not late giving my humble feedback on this framework :-)

Can we use "per gpio based" structure instead of "per gpio_chip" based one, just like what the generic IRQ layer is doing nowadays?

We "can" do most anything. What would that improve though?

Each irq_chip handles multiple IRQs ... just like this patch has each gpio_chip handling multiple GPIOs. (Not that I think GPIO code should closely model IRQ code; they need to address very different problems.)

I can't tell what you intend to suggest as a "per-GPIO" data structure; since I can think of at least three different ways to do such a thing, you should be more concrete. I'd think it should be in *addition* to a gpio_chip structure though.

Quoted 5 lines

So that

a. you don't have to declare per gpio_chip "can_sleep", "is_out" and "requested".
Those will be just bits of properties of a single GPIO.

The can_sleep value is a per-controller thing. The other bits are indeed per-GPIO.

So do you mean a structure with two bits, plus a pointer to a gpio_chip, plus likely other stuff (what?) to make it work? What would the hot-path costs be (for getting/setting values of an on-chip GPIO)?

Quoted 2 lines

b. and furthur more, one can avoid the use of ARCH_GPIOS_PER_CHIP, which leads to many holes

Why should holes (in the GPIO number sequence) be a problem? In this code, they don't cost much space at all. They'd cost more if there were a per-GPIO structure though...

The only downside of GPIOS_PER_CHIP that I know of right now is that it complicates mixing gpio bank sizes; it's a ceiling, some controllers would allocate more than they need. The upside of that is efficiency, and a closer match to how underlying hardware works.

Of course, GPIOS_PER_CHIP *could* be decoupled from how the table of gpio_chip pointers is managed. If the table were to group GPIOs in units of 8, a gpio_chip with 32 GPIOs could take four adjacent entries while an 8-bit GPIO expander could take just one. That'd be a very easy patch, supporting a more dense allocation of GPIO numbers... although it would increase static memory consumption by typically NR_GPIOS/4 pointers.

Quoted 1 line

c. gpio_to_chip() will be made easy and straight forward

I'd say "return chips[gpio / ARCH_GPIOS_PER_CHIP]" already meets both criteria!

There's also "efficient" to consider; this way doesn't cost much memory or add levels of indirection (compared to most platforms, which already use a similar array).

Quoted 2 lines

d. granularity of spin_lock()/_unlock() can be made small (per GPIO instead of per gpio_chip)

Why would per-GPIO locking be needed though? Look again...

The locking is there fundamentally because gpio_chip structures may need to be unregistered; that's not a per-gpio issue. Even when a gpio is marked in chip->requested under that lock, that's part of ensuring that the unregistration is prevented so long as the GPIO is in active use.

Plus, fine grained locking is rarely a good idea; it normally increases locking overhead by involving multiple locks. Only add extra locks if a single lock sees too much contention; and even then, only if that contention can't be removed by using a smarter design.

- Dave

Quoted 40 lines

What do you think?

- eric

Quoted 34 lines · David Brownell
Quoted 5 lines · David Brownell

Provides new implementation infrastructure that platforms may choose to use when implementing the GPIO programming interface. Platforms can update their GPIO support to use this. The downside is slower access to non-inlined GPIOs; rarely a problem except when bitbanging some protocol.

I was asked just what that overhead *is* ... and it surprised me. A summary of the results is appended to this note.

Fortuntely it turns out those problems all go away if the gpiolib code uses a *raw* spinlock to guard its table lookups. With a raw spinlock, any performance impact of gpiolib seems to be well under a microsecond in this bitbang context (and not objectionable). Preempt became free; enabling debug options had only a minor cost.

That's as it should be, since the only substantive changes were to grab and release a lock, do one table lookup a bit differently, and add one indirection function call ... changes which should not have any visible performance impact on per-bit codepaths, and one might expect to cost on the order of one dozen instructions.

So the next version of this code will include a few minor bugfixes, and will also use a raw spinlock to protect that table. A raw lock seems appropriate there in any case, since non-sleeping GPIOs should be accessible from hardirq contexts even on RT kernels.

If anyone has any strong arguments against using a raw spinlock to protect that table, it'd be nice to know them sooner rather than later.

- Dave

#5 lore raw
Quoted 15 lines · David Brownell
Quoted 6 lines · eric miao

Hi David,

I hope I was not late giving my humble feedback on this framework :-)

Can we use "per gpio based" structure instead of "per gpio_chip" based one, just like what the generic IRQ layer is doing nowadays?

We "can" do most anything. What would that improve though?

Each irq_chip handles multiple IRQs ... just like this patch has each gpio_chip handling multiple GPIOs. (Not that I think GPIO code should closely model IRQ code; they need to address very different problems.)

Quoted 5 lines

I can't tell what you intend to suggest as a "per-GPIO" data structure; since I can think of at least three different ways to do such a thing, you should be more concrete. I'd think it should be in *addition* to a gpio_chip structure though.

exactly, I'd say a "struct gpio_desc" as

Quoted 7 lines
Quoted 6 lines

So that

a. you don't have to declare per gpio_chip "can_sleep", "is_out" and "requested".
Those will be just bits of properties of a single GPIO.

Quoted 8 lines

The can_sleep value is a per-controller thing. The other bits are indeed per-GPIO.

So do you mean a structure with two bits, plus a pointer to a gpio_chip, plus likely other stuff (what?) to make it work? What would the hot-path costs be (for getting/setting values of an on-chip GPIO)?

the cost is as trivial as the current one.

Quoted 8 lines
Quoted 3 lines

b. and furthur more, one can avoid the use of ARCH_GPIOS_PER_CHIP, which leads to many holes

Why should holes (in the GPIO number sequence) be a problem? In this code, they don't cost much space at all. They'd cost more if there were a per-GPIO structure though...

well, I don't think holes are problems, but think about the restriction ARCH_GPIOS_PER_CHIP enforces the numbering of GPIOs, don't you think we need a more flexible numbering scheme, so one can later adjust gpio_to_irq() and irq_to_gpio() easily??

Quoted 36 lines

The only downside of GPIOS_PER_CHIP that I know of right now is that it complicates mixing gpio bank sizes; it's a ceiling, some controllers would allocate more than they need. The upside of that is efficiency, and a closer match to how underlying hardware works.

Of course, GPIOS_PER_CHIP *could* be decoupled from how the table of gpio_chip pointers is managed. If the table were to group GPIOs in units of 8, a gpio_chip with 32 GPIOs could take four adjacent entries while an 8-bit GPIO expander could take just one. That'd be a very easy patch, supporting a more dense allocation of GPIO numbers... although it would increase static memory consumption by typically NR_GPIOS/4 pointers.

Quoted 1 line

c. gpio_to_chip() will be made easy and straight forward

I'd say "return chips[gpio / ARCH_GPIOS_PER_CHIP]" already meets both criteria!

There's also "efficient" to consider; this way doesn't cost much memory or add levels of indirection (compared to most platforms, which already use a similar array).

Quoted 2 lines

d. granularity of spin_lock()/_unlock() can be made small (per GPIO instead of per gpio_chip)

Why would per-GPIO locking be needed though? Look again...

The locking is there fundamentally because gpio_chip structures may need to be unregistered; that's not a per-gpio issue. Even when a gpio is marked in chip->requested under that lock, that's part of ensuring that the unregistration is prevented so long as the GPIO is in active use.

Quoted 6 lines

Plus, fine grained locking is rarely a good idea; it normally increases locking overhead by involving multiple locks. Only add extra locks if a single lock sees too much contention; and even then, only if that contention can't be removed by using a smarter design.

well, I don't see much benefit for now, either. But binding/unbinding the gpio_chip to the gpio_desc could possibly be made locking free (RCU maybe), since removing a gpio_chip is really *rare*.

Quoted 46 lines

- Dave

Quoted 40 lines

What do you think?

- eric

Quoted 34 lines · David Brownell
Quoted 5 lines · David Brownell

Provides new implementation infrastructure that platforms may choose to use when implementing the GPIO programming interface. Platforms can update their GPIO support to use this. The downside is slower access to non-inlined GPIOs; rarely a problem except when bitbanging some protocol.

I was asked just what that overhead *is* ... and it surprised me. A summary of the results is appended to this note.

Fortuntely it turns out those problems all go away if the gpiolib code uses a *raw* spinlock to guard its table lookups. With a raw spinlock, any performance impact of gpiolib seems to be well under a microsecond in this bitbang context (and not objectionable). Preempt became free; enabling debug options had only a minor cost.

That's as it should be, since the only substantive changes were to grab and release a lock, do one table lookup a bit differently, and add one indirection function call ... changes which should not have any visible performance impact on per-bit codepaths, and one might expect to cost on the order of one dozen instructions.

So the next version of this code will include a few minor bugfixes, and will also use a raw spinlock to protect that table. A raw lock seems appropriate there in any case, since non-sleeping GPIOs should be accessible from hardirq contexts even on RT kernels.

If anyone has any strong arguments against using a raw spinlock to protect that table, it'd be nice to know them sooner rather than later.

- Dave

Signature · 2 lines
Cheers
- eric
#6 lore raw

Here comes a bunch of patches to illustrate my idea, some are not related to the point I mentioned, and they are not mature for now :-)

Subject: [PATCH 1/5] add gpio_is_onchip() for commonly used gpio range checking

---
 lib/gpiolib.c |   32 ++++++++++++++++++++++----------
 1 files changed, 22 insertions(+), 10 deletions(-)
lib/gpiolib.c +22 -10
index f2ae689..c627efb 100644
@@ -33,6 +33,14 @@ static DEFINE_SPINLOCK(gpio_lock);
 static struct gpio_chip *chips[DIV_ROUND_UP(ARCH_NR_GPIOS,
 					ARCH_GPIOS_PER_CHIP)];
 
+static inline int gpio_is_onchip(unsigned gpio, struct gpio_chip *chip)
+{
+	if (chip && gpio >= chip->base && gpio < chip->base + chip->ngpio)
+		return 1;
+	else
+		return 0;
+}
+
 /* Warn when drivers omit gpio_request() calls -- legal but
  * ill-advised when setting direction, and otherwise illegal.
  */
@@ -139,11 +147,11 @@ int gpio_request(unsigned gpio, const char *label)
 
 	spin_lock_irqsave(&gpio_lock, flags);
 	chip = gpio_to_chip(gpio);
-	if (!chip)
+
+	if (!gpio_is_onchip(gpio, chip))
 		goto done;
+
 	gpio -= chip->base;
-	if (gpio >= chip->ngpio)
-		goto done;
 
 	/* NOTE:  gpio_request() can be called in early boot,
 	 * before IRQs are enabled.
@@ -176,11 +184,11 @@ void gpio_free(unsigned gpio)
 	spin_lock_irqsave(&gpio_lock, flags);
 
 	chip = gpio_to_chip(gpio);
-	if (!chip)
+
+	if (!gpio_is_onchip(gpio, chip))
 		goto done;
+
 	gpio -= chip->base;
-	if (gpio >= chip->ngpio)
-		goto done;
 
 #ifdef CONFIG_DEBUG_FS
 	if (chip->requested[gpio])
@@ -218,9 +226,11 @@ int gpio_direction_input(unsigned gpio)
 	chip = gpio_to_chip(gpio);
 	if (!chip || !chip->get || !chip->direction_input)
 		goto fail;
-	gpio -= chip->base;
-	if (gpio >= chip->ngpio)
+
+	if (!gpio_is_onchip(gpio, chip))
 		goto fail;
+
+	gpio -= chip->base;
 	gpio_ensure_requested(chip, gpio);
 
 	/* now we know the gpio is valid and chip won't vanish */
@@ -250,9 +260,11 @@ int gpio_direction_output(unsigned gpio, int value)
 	chip = gpio_to_chip(gpio);
 	if (!chip || !chip->get || !chip->direction_output)
 		goto fail;
-	gpio -= chip->base;
-	if (gpio >= chip->ngpio)
+	
+	if (!gpio_is_onchip(gpio, chip))
 		goto fail;
+
+	gpio -= chip->base;
 	gpio_ensure_requested(chip, gpio);
 
 	/* now we know the gpio is valid and chip won't vanish */
Signature · 1 line
1.5.2.5.GIT
#7 lore raw

so that requested will always be used, only *requested_str will be used for DEBUG_FS tracking assistance

Subject: [PATCH 2/5] define gpio_chip.requested_str as a debugfs tracking string

---
 include/asm-generic/gpio.h |   11 ++---------
 lib/gpiolib.c              |   34 ++++++++++++++--------------------
 2 files changed, 16 insertions(+), 29 deletions(-)
include/asm-generic/gpio.h +2 -9
index d00a287..ba3e336 100644
@@ -73,13 +73,10 @@ struct gpio_chip {
 
 	/* other fields are modified by the gpio library only */
 	DECLARE_BITMAP(is_out, ARCH_GPIOS_PER_CHIP);
+	DECLARE_BITMAP(requested, ARCH_GPIOS_PER_CHIP);
 
 #ifdef CONFIG_DEBUG_FS
-	/* fat bits */
-	const char		*requested[ARCH_GPIOS_PER_CHIP];
-#else
-	/* thin bits */
-	DECLARE_BITMAP(requested, ARCH_GPIOS_PER_CHIP);
+	const char		*requested_str[ARCH_GPIOS_PER_CHIP];
 #endif
 };
 
@@ -89,11 +86,7 @@ struct gpio_chip {
 static inline int
 gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
 {
-#ifdef CONFIG_DEBUG_FS
-	return chip->requested[offset] != NULL;
-#else
 	return test_bit(offset, chip->requested);
-#endif
 }
 
 /* add/remove chips */
lib/gpiolib.c +0 -0
index c627efb..d52c7f1 100644
@@ -48,12 +48,11 @@ static void gpio_ensure_requested(struct gpio_chip

*chip, unsigned offset)
{
int requested;

+ requested = test_and_set_bit(offset, chip->requested);
+
#ifdef CONFIG_DEBUG_FS
- requested = (int) chip->requested[offset];
if (!requested)
- chip->requested[offset] = "(auto)";
-#else
- requested = test_and_set_bit(offset, chip->requested);
+ chip->requested_str[offset] = "(auto)";
#endif

if (!requested)
@@ -158,16 +157,13 @@ int gpio_request(unsigned gpio, const char *label)
*/

status = 0;
-#ifdef CONFIG_DEBUG_FS
- if (!label)
- label = "?";
- if (chip->requested[gpio])
- status = -EBUSY;
- else
- chip->requested[gpio] = label;
-#else
+
if (test_and_set_bit(gpio, chip->requested))
status = -EBUSY;
+
+#ifdef CONFIG_DEBUG_FS
+ if (status == 0)
+ chip->requested_str[gpio] = (label == NULL) ? "?" : label;
#endif

done:
@@ -190,14 +186,12 @@ void gpio_free(unsigned gpio)

gpio -= chip->base;

-#ifdef CONFIG_DEBUG_FS
- if (chip->requested[gpio])
- chip->requested[gpio] = NULL;
- else
- chip = NULL;
-#else
if (!test_and_clear_bit(gpio, chip->requested))
chip = NULL;
+
+#ifdef CONFIG_DEBUG_FS
+ if (chip != NULL)
+ chip->requested_str[gpio] = NULL;
#endif
WARN_ON(extra_checks && chip == NULL);
done:
@@ -400,14 +394,14 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
unsigned gpio;
int is_out;

- if (!chip->requested[i])
+ if (!chip->requested_str[i])
continue;

gpio = chip->base + i; is_out = test_bit(i, chip->is_out);

seq_printf(s, " gpio-%-3d (%-12s) %s %s",
- gpio, chip->requested[i],
+ gpio, chip->requested_str[i],
is_out ? "out" : "in ",
chip->get
? (chip->get(chip, i) ? "hi" : "lo")

Signature · 1 line
1.5.2.5.GIT
#8 lore raw

Here comes the point of "struct gpio_desc"

Subject: [PATCH 3/5] use a per GPIO "struct gpio_desc" and chain "gpio_chip" to a list

---
 include/asm-generic/gpio.h |    7 +++++
 lib/gpiolib.c              |   64 ++++++++++++++++++++++----------------------
 2 files changed, 39 insertions(+), 32 deletions(-)
include/asm-generic/gpio.h +7 -0
index ba3e336..783adcf 100644
@@ -23,6 +23,12 @@
 
 struct seq_file;
 
+struct gpio_chip;
+
+struct gpio_desc {
+	struct gpio_chip *chip;
+};
+
 /**
  * struct gpio_chip - abstract a GPIO controller
  * @label: for diagnostics
@@ -76,6 +82,7 @@ struct gpio_chip {
 	DECLARE_BITMAP(requested, ARCH_GPIOS_PER_CHIP);
 
 #ifdef CONFIG_DEBUG_FS
+	struct list_head	node;
 	const char		*requested_str[ARCH_GPIOS_PER_CHIP];
 #endif
 };
lib/gpiolib.c +5 -6
index d52c7f1..57e0d10 100644
@@ -25,13 +25,12 @@
 #define	extra_checks	0
 #endif
 
-/* gpio_lock protects modification to the table of chips and to
- * gpio_chip->requested.  If a gpio is requested, its gpio_chip
- * is not removable.
- */
 static DEFINE_SPINLOCK(gpio_lock);
-static struct gpio_chip *chips[DIV_ROUND_UP(ARCH_NR_GPIOS,
-					ARCH_GPIOS_PER_CHIP)];
+struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
+
+#ifdef CONFIG_DEBUG_FS
+static LIST_HEAD(gpio_chip_list);
+#endif
 
 static inline int gpio_is_onchip(unsigned gpio, struct gpio_chip *chip)
 {
@@ -63,7 +62,7 @@ static void gpio_ensure_requested(struct gpio_chip

*chip, unsigned offset)
/* caller holds gpio_lock */
static inline struct gpio_chip *gpio_to_chip(unsigned gpio) {
- return chips[gpio / ARCH_GPIOS_PER_CHIP];
+ return gpio_desc[gpio].chip;
}

/**
@@ -78,7 +77,6 @@ static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
int gpiochip_add(struct gpio_chip *chip)
{
unsigned long flags;
- int status = 0;
unsigned id;

if (chip->base < 0 || (chip->base % ARCH_GPIOS_PER_CHIP) != 0)
@@ -89,13 +87,21 @@ int gpiochip_add(struct gpio_chip *chip)
return -EINVAL;

spin_lock_irqsave(&gpio_lock, flags);
- id = chip->base / ARCH_GPIOS_PER_CHIP;
- if (chips[id] == NULL)
- chips[id] = chip;
- else
- status = -EBUSY;
+
+ for (id = chip->base; id < chip->base + chip->ngpio; id++)
+ if (gpio_desc[id].chip) {
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return -EBUSY;
+ }
+
+ for (id = chip->base; id < chip->base + chip->ngpio; id++)
+ gpio_desc[id].chip = chip;
+
+#ifdef CONFIG_DEBUG_FS
+ list_add(&chip->node, &gpio_chip_list);
+#endif
spin_unlock_irqrestore(&gpio_lock, flags);
- return status;
+ return 0;
}
EXPORT_SYMBOL_GPL(gpiochip_add);

@@ -108,28 +114,26 @@ EXPORT_SYMBOL_GPL(gpiochip_add);
int gpiochip_remove(struct gpio_chip *chip) {
unsigned long flags;
- int status = 0;
int offset;
- unsigned id = chip->base / ARCH_GPIOS_PER_CHIP;
+ unsigned id;

spin_lock_irqsave(&gpio_lock, flags);

- for (offset = 0; offset < chip->ngpio; offset++) {
+ for (offset = 0; offset < chip->ngpio; offset++)
if (gpiochip_is_requested(chip, offset)) {
- status = -EBUSY;
- break;
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ return -EBUSY;
}
- }

- if (status == 0) {
- if (chips[id] == chip)
- chips[id] = NULL;
- else
- status = -EINVAL;
- }
+ for (id = chip->base; id < chip->base + chip->ngpio; id++)
+ gpio_desc[id].chip = NULL;
+
+#ifdef CONFIG_DEBUG_FS
+ list_del(&chip->node);
+#endif

spin_unlock_irqrestore(&gpio_lock, flags);
- return status;
+ return 0;
}
EXPORT_SYMBOL_GPL(gpiochip_remove);

@@ -463,11 +467,7 @@ static int gpiolib_show(struct seq_file *s, void *unused)

/* REVISIT this isn't locked against gpio_chip removal ... */

- for (id = 0; id < ARRAY_SIZE(chips); id++) {
- chip = chips[id];
- if (!chip)
- continue;
-
+ list_for_each_entry(chip, &gpio_chip_list, node) {
seq_printf(s, "%sGPIOs %d-%d, %s%s:\n",
started ? "\n" : "", chip->base, chip->base + chip->ngpio - 1,

Signature · 1 line
1.5.2.5.GIT
#9 lore raw

Subject: [PATCH] move per GPIO "is_out" to "struct gpio_desc"

---
 include/asm-generic/gpio.h |    4 +---
 lib/gpiolib.c              |   18 +++++++++++-------
 2 files changed, 12 insertions(+), 10 deletions(-)
include/asm-generic/gpio.h +1 -3
index 783adcf..da67038 100644
@@ -27,6 +27,7 @@ struct gpio_chip;
 
 struct gpio_desc {
 	struct gpio_chip *chip;
+	unsigned is_out:1;
 };
 
 /**
@@ -47,8 +48,6 @@ struct gpio_desc {
  *	(base + ngpio - 1).
  * @can_sleep: flag must be set iff get()/set() methods sleep, as they
  *	must while accessing GPIO expander chips over I2C or SPI
- * @is_out: bit array where bit N is true iff GPIO with offset N has been
- *	 called successfully to configure this as an output
  *
  * A gpio_chip can help platforms abstract various sources of GPIOs so
  * they can all be accessed through a common programing interface.
@@ -78,7 +77,6 @@ struct gpio_chip {
 	unsigned		can_sleep:1;
 
 	/* other fields are modified by the gpio library only */
-	DECLARE_BITMAP(is_out, ARCH_GPIOS_PER_CHIP);
 	DECLARE_BITMAP(requested, ARCH_GPIOS_PER_CHIP);
 
 #ifdef CONFIG_DEBUG_FS
lib/gpiolib.c +10 -6
index 57e0d10..a089597 100644
@@ -217,11 +217,14 @@ int gpio_direction_input(unsigned gpio)
 {
 	unsigned long		flags;
 	struct gpio_chip	*chip;
+	struct gpio_desc	*desc;
 	int			status = -EINVAL;
 
 	spin_lock_irqsave(&gpio_lock, flags);
 
-	chip = gpio_to_chip(gpio);
+	desc = &gpio_desc[gpio];
+	chip = desc->chip;
+
 	if (!chip || !chip->get || !chip->direction_input)
 		goto fail;
 
@@ -238,8 +241,7 @@ int gpio_direction_input(unsigned gpio)
 	might_sleep_if(extra_checks && chip->can_sleep);
 
 	status = chip->direction_input(chip, gpio);
-	if (status == 0)
-		clear_bit(gpio, chip->is_out);
+	desc->is_out = 0;
 	return status;
 fail:
 	spin_unlock_irqrestore(&gpio_lock, flags);
@@ -251,11 +253,14 @@ int gpio_direction_output(unsigned gpio, int value)
 {
 	unsigned long		flags;
 	struct gpio_chip	*chip;
+	struct gpio_desc	*desc;
 	int			status = -EINVAL;
 
 	spin_lock_irqsave(&gpio_lock, flags);
 
-	chip = gpio_to_chip(gpio);
+	desc = &gpio_desc[gpio];
+	chip = desc->chip;
+
 	if (!chip || !chip->get || !chip->direction_output)
 		goto fail;
 	
@@ -272,8 +277,7 @@ int gpio_direction_output(unsigned gpio, int value)
 	might_sleep_if(extra_checks && chip->can_sleep);
 
 	status = chip->direction_output(chip, gpio, value);
-	if (status == 0)
-		set_bit(gpio, chip->is_out);
+	desc->is_out = 1;
 	return status;
 fail:
 	spin_unlock_irqrestore(&gpio_lock, flags);
@@ -402,7 +406,7 @@ static void gpiolib_dbg_show(struct seq_file *s,

struct gpio_chip *chip)
continue;

gpio = chip->base + i;
- is_out = test_bit(i, chip->is_out);
+ is_out = gpio_desc[gpio].is_out;

seq_printf(s, " gpio-%-3d (%-12s) %s %s",
gpio, chip->requested_str[i],

Signature · 1 line
1.5.2.5.GIT
#10 lore raw

Subject: [PATCH 5/5] move per GPIO "requested" to "struct gpio_desc"

---
 include/asm-generic/gpio.h |   17 +++---------
 lib/gpiolib.c              |   62 ++++++++++++++++++++++++-------------------
 2 files changed, 39 insertions(+), 40 deletions(-)
include/asm-generic/gpio.h +4 -13
index da67038..7e70c67 100644
@@ -28,6 +28,10 @@ struct gpio_chip;
 struct gpio_desc {
 	struct gpio_chip *chip;
 	unsigned is_out:1;
+	unsigned requested:1;
+#ifdef CONFIG_DEBUG_FS
+	const char *requested_str;
+#endif
 };
 
 /**
@@ -76,24 +80,11 @@ struct gpio_chip {
 	u16			ngpio;
 	unsigned		can_sleep:1;
 
-	/* other fields are modified by the gpio library only */
-	DECLARE_BITMAP(requested, ARCH_GPIOS_PER_CHIP);
-
 #ifdef CONFIG_DEBUG_FS
 	struct list_head	node;
-	const char		*requested_str[ARCH_GPIOS_PER_CHIP];
 #endif
 };
 
-/* returns true iff a given gpio signal has been requested;
- * primarily for code dumping gpio_chip state.
- */
-static inline int
-gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
-{
-	return test_bit(offset, chip->requested);
-}
-
 /* add/remove chips */
 extern int gpiochip_add(struct gpio_chip *chip);
 extern int __must_check gpiochip_remove(struct gpio_chip *chip);
lib/gpiolib.c +0 -0
index a089597..f9e1c88 100644
@@ -43,20 +43,19 @@ static inline int gpio_is_onchip(unsigned gpio,

struct gpio_chip *chip)
/* Warn when drivers omit gpio_request() calls -- legal but
* ill-advised when setting direction, and otherwise illegal. */
-static void gpio_ensure_requested(struct gpio_chip *chip, unsigned offset) +static void gpio_ensure_requested(unsigned gpio)
{
- int requested;
+ int requested;

- requested = test_and_set_bit(offset, chip->requested);
+ requested = gpio_desc[gpio].requested;

#ifdef CONFIG_DEBUG_FS
if (!requested)
- chip->requested_str[offset] = "(auto)";
+ gpio_desc[gpio].requested_str = "(auto)";
#endif

if (!requested)
- printk(KERN_DEBUG "GPIO-%d autorequested\n",
- chip->base + offset);
+ pr_debug("GPIO-%d autorequested\n", gpio);
}

/* caller holds gpio_lock */
@@ -114,13 +113,12 @@ EXPORT_SYMBOL_GPL(gpiochip_add);
int gpiochip_remove(struct gpio_chip *chip) {
unsigned long flags;
- int offset;
unsigned id;

spin_lock_irqsave(&gpio_lock, flags);

- for (offset = 0; offset < chip->ngpio; offset++)
- if (gpiochip_is_requested(chip, offset)) {
+ for (id = chip->base; id < chip->base + chip->ngpio; id++)
+ if (gpio_desc[id].requested) {
spin_unlock_irqrestore(&gpio_lock, flags); return -EBUSY;
}
@@ -145,11 +143,14 @@ EXPORT_SYMBOL_GPL(gpiochip_remove);
int gpio_request(unsigned gpio, const char *label)
{
struct gpio_chip *chip;
+ struct gpio_desc *desc;
int status = -EINVAL;
unsigned long flags;

spin_lock_irqsave(&gpio_lock, flags);
- chip = gpio_to_chip(gpio);
+
+ desc = &gpio_desc[gpio];
+ chip = desc->chip;

if (!gpio_is_onchip(gpio, chip))
goto done;
@@ -162,12 +163,14 @@ int gpio_request(unsigned gpio, const char *label)

status = 0;

- if (test_and_set_bit(gpio, chip->requested))
+ if (desc->requested == 0)
+ desc->requested = 1;
+ else
status = -EBUSY;

#ifdef CONFIG_DEBUG_FS
if (status == 0)
- chip->requested_str[gpio] = (label == NULL) ? "?" : label;
+ desc->requested_str = (label == NULL) ? "?" : label;
#endif

done:
@@ -180,9 +183,11 @@ void gpio_free(unsigned gpio)
{
unsigned long flags;
struct gpio_chip *chip;
+ struct gpio_desc *desc;

spin_lock_irqsave(&gpio_lock, flags);

+ desc = &gpio_desc[gpio];
chip = gpio_to_chip(gpio);

if (!gpio_is_onchip(gpio, chip))
@@ -190,12 +195,13 @@ void gpio_free(unsigned gpio)

gpio -= chip->base;

- if (!test_and_clear_bit(gpio, chip->requested))
+ if (desc->requested == 0)
+ desc->requested = 1;
+ else
chip = NULL;

#ifdef CONFIG_DEBUG_FS
- if (chip != NULL)
- chip->requested_str[gpio] = NULL;
+ desc->requested_str = NULL;
#endif
WARN_ON(extra_checks && chip == NULL);
done:
@@ -231,8 +237,8 @@ int gpio_direction_input(unsigned gpio)
if (!gpio_is_onchip(gpio, chip))
goto fail;

+ gpio_ensure_requested(gpio);
gpio -= chip->base;
- gpio_ensure_requested(chip, gpio);

/* now we know the gpio is valid and chip won't vanish */

@@ -267,8 +273,8 @@ int gpio_direction_output(unsigned gpio, int value)
if (!gpio_is_onchip(gpio, chip))
goto fail;

+ gpio_ensure_requested(gpio);
gpio -= chip->base;
- gpio_ensure_requested(chip, gpio);

/* now we know the gpio is valid and chip won't vanish */

@@ -304,7 +310,7 @@ int __gpio_get_value(unsigned gpio)
spin_lock_irqsave(&gpio_lock, flags); chip = gpio_to_chip(gpio);
if (extra_checks)
- gpio_ensure_requested(chip, gpio - chip->base);
+ gpio_ensure_requested(gpio);
spin_unlock_irqrestore(&gpio_lock, flags);

if (unlikely(chip->can_sleep)) {
@@ -323,7 +329,7 @@ void __gpio_set_value(unsigned gpio, int value)
spin_lock_irqsave(&gpio_lock, flags);
chip = gpio_to_chip(gpio);
if (extra_checks)
- gpio_ensure_requested(chip, gpio - chip->base);
+ gpio_ensure_requested(gpio);
spin_unlock_irqrestore(&gpio_lock, flags);

if (unlikely(chip->can_sleep))
@@ -341,7 +347,7 @@ int __gpio_cansleep(unsigned gpio)
spin_lock_irqsave(&gpio_lock, flags);
chip = gpio_to_chip(gpio);
if (extra_checks)
- gpio_ensure_requested(chip, gpio - chip->base);
+ gpio_ensure_requested(gpio);
spin_unlock_irqrestore(&gpio_lock, flags); return chip->can_sleep;
}
@@ -363,7 +369,7 @@ int gpio_get_value_cansleep(unsigned gpio)
spin_lock_irqsave(&gpio_lock, flags);
chip = gpio_to_chip(gpio);
if (extra_checks)
- gpio_ensure_requested(chip, gpio - chip->base);
+ gpio_ensure_requested(gpio);
spin_unlock_irqrestore(&gpio_lock, flags);

return chip->get(chip, gpio - chip->base);
@@ -380,7 +386,7 @@ void gpio_set_value_cansleep(unsigned gpio, int value)
spin_lock_irqsave(&gpio_lock, flags);
chip = gpio_to_chip(gpio);
if (extra_checks)
- gpio_ensure_requested(chip, gpio - chip->base);
+ gpio_ensure_requested(gpio);
spin_unlock_irqrestore(&gpio_lock, flags);

chip->set(chip, gpio - chip->base, value);
@@ -401,21 +407,23 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
for (i = 0; i < chip->ngpio; i++) {
unsigned gpio;
int is_out;
+ struct gpio_desc *desc;

- if (!chip->requested_str[i])
+ if (!desc->requested)
continue;

gpio = chip->base + i;
- is_out = gpio_desc[gpio].is_out;
+ desc = &gpio_desc[gpio];
+ is_out = desc->is_out;

seq_printf(s, " gpio-%-3d (%-12s) %s %s",
- gpio, chip->requested_str[i],
- is_out ? "out" : "in ",
+ gpio, desc->requested_str,
+ desc->is_out ? "out" : "in ",
chip->get
? (chip->get(chip, i) ? "hi" : "lo") : "? ");

- if (!is_out) {
+ if (!desc->is_out) {
int irq = gpio_to_irq(gpio);
struct irq_desc *desc = irq_desc + irq;

Signature · 1 line
1.5.2.5.GIT
#11 lore raw

include/asm-generic/gpio.h struct gpio_chip; · line 34

Quoted 10 lines · eric miao

Subject: [PATCH 5/5] move per GPIO "requested" to "struct gpio_desc"

struct gpio_desc {

include/asm-generic/gpio.h +4 -0
 	struct gpio_chip *chip;
 	unsigned is_out:1;
+	unsigned requested:1;
+#ifdef CONFIG_DEBUG_FS
+	const char *requested_str;
+#endif

A better name for this would be "label", matching what's passed from gpio_request(). Ndls abrviatns r bd.

Note that this means (on typical 32-bit embedded hardware) twelve bytes per GPIO, which if you assume 256 GPIOs means an extra 3 KB static memory compared to the patch I sent.

Review static inline int gpio_is_onchip(unsigned gpio, · line 47

Quoted 7 lines
(quoted diff) +1 -1
@@ -43,20 +43,19 @@ static inline int gpio_is_onchip(unsigned gpio,
 struct gpio_chip *chip)
 /* Warn when drivers omit gpio_request() calls -- legal but
  * ill-advised when setting direction, and otherwise illegal.
  */
-static void gpio_ensure_requested(struct gpio_chip *chip, unsigned offset)
+static void gpio_ensure_requested(unsigned gpio)

Simpler to pass a gpio_desc pointer ...

Review static inline int gpio_is_onchip(unsigned gpio, · line 49

Quoted 4 lines
(quoted diff) +1 -2
 	if (!requested)
-		printk(KERN_DEBUG "GPIO-%d autorequested\n",
-				chip->base + offset);
+		pr_debug("GPIO-%d autorequested\n", gpio);

Leave the printk in ... this is the sort of thing we want to see fixed, which becomes unlikely once you hide such diagnostics. And for that matter, what would be enabling the "-DDEBUG" that would trigger a pr_debug() message?

... overall the main downside of these patches seems to be that it consumes more static memory.

#12 lore raw
Quoted 4 lines · David Brownell
Quoted 2 lines · eric miao

Subject: [PATCH 5/5] move per GPIO "requested" to "struct gpio_desc"

Quoted 11 lines

include/asm-generic/gpio.h struct gpio_chip; · line 34

Quoted 7 lines
 struct gpio_desc {
      struct gpio_chip *chip;
      unsigned is_out:1;
include/asm-generic/gpio.h +4 -0
+     unsigned requested:1;
+#ifdef CONFIG_DEBUG_FS
+     const char *requested_str;
+#endif

A better name for this would be "label", matching what's passed from gpio_request(). Ndls abrviatns r bd.

Fine.

Quoted 5 lines

Note that this means (on typical 32-bit embedded hardware) twelve bytes per GPIO, which if you assume 256 GPIOs means an extra 3 KB static memory compared to the patch I sent.

Note this reduces the memory in gpio_chip, so it consumes almost same memory as the patch you sent.

Quoted 17 lines

Review static inline int gpio_is_onchip(unsigned gpio, · line 47

Quoted 8 lines
(quoted diff) +1 -1
@@ -43,20 +43,19 @@ static inline int gpio_is_onchip(unsigned gpio,
 struct gpio_chip *chip)
 /* Warn when drivers omit gpio_request() calls -- legal but
  * ill-advised when setting direction, and otherwise illegal.
  */
-static void gpio_ensure_requested(struct gpio_chip *chip, unsigned offset)
+static void gpio_ensure_requested(unsigned gpio)

Simpler to pass a gpio_desc pointer ...

Review static inline int gpio_is_onchip(unsigned gpio, · line 49

Quoted 4 lines
(quoted diff) +1 -2
      if (!requested)
-             printk(KERN_DEBUG "GPIO-%d autorequested\n",
-                             chip->base + offset);
+             pr_debug("GPIO-%d autorequested\n", gpio);
Quoted 5 lines

Leave the printk in ... this is the sort of thing we want to see fixed, which becomes unlikely once you hide such diagnostics. And for that matter, what would be enabling the "-DDEBUG" that would trigger a pr_debug() message?

line length issue, just ignore this if you prefer.

Quoted 5 lines

... overall the main downside of these patches seems to be that it consumes more static memory.

Not really, since it reduces the holes. That all depend on your ARCH_NR_GPIOS.

Signature · 2 lines
Cheers
- eric
#13 lore raw
Quoted 12 lines
Quoted 12 lines

include/asm-generic/gpio.h struct gpio_chip; · line 34

Quoted 8 lines
 struct gpio_desc {
      struct gpio_chip *chip;
      unsigned is_out:1;
include/asm-generic/gpio.h +4 -0
+     unsigned requested:1;
+#ifdef CONFIG_DEBUG_FS
+     const char *requested_str;
+#endif

Note that this means (on typical 32-bit embedded hardware) twelve bytes per GPIO, which if you assume 256 GPIOs means an extra 3 KB static memory compared to the patch I sent.

Actually, 2K is a more accurate number -- ignore DEBUG_FS.

Quoted 2 lines

Note this reduces the memory in gpio_chip, so it consumes almost same memory as the patch you sent.

No; the amount of space shaved from a typical (32-bit banks) gpio_chip is *exactly* the cost of one gpio_desc: two words. In one case, two bitmaps. In the other, a pointer, two bits, and internal struct padding.

So unless each bank has only a single GPIO, this approach does cost more memory. Both for the extra memory associated with each gpio_chip that's used, and for unused gpio_desc.

That's not necessarily a bad thing, though it's always worth avoiding bloat.

- Dave

#14 lore raw

Y, the IRQ <--> GPIO mapping is another thing I'm concerned about. Other than that, all the other part of the gpiolib is a great work, actually, I've been waiting
for this for quite a long time and just don't have time for a hands-on until recently.

So let's get more feedback on this.

Quoted 24 lines · David Brownell
Quoted 12 lines
Quoted 12 lines

include/asm-generic/gpio.h struct gpio_chip; · line 34

Quoted 8 lines
 struct gpio_desc {
      struct gpio_chip *chip;
      unsigned is_out:1;
include/asm-generic/gpio.h +4 -0
+     unsigned requested:1;
+#ifdef CONFIG_DEBUG_FS
+     const char *requested_str;
+#endif

Note that this means (on typical 32-bit embedded hardware) twelve bytes per GPIO, which if you assume 256 GPIOs means an extra 3 KB static memory compared to the patch I sent.

Actually, 2K is a more accurate number -- ignore DEBUG_FS.

Quoted 2 lines

Note this reduces the memory in gpio_chip, so it consumes almost same memory as the patch you sent.

No; the amount of space shaved from a typical (32-bit banks) gpio_chip is *exactly* the cost of one gpio_desc: two words. In one case, two bitmaps. In the other, a pointer, two bits, and internal struct padding.

Quoted 7 lines

So unless each bank has only a single GPIO, this approach does cost more memory. Both for the extra memory associated with each gpio_chip that's used, and for unused gpio_desc.

That's not necessarily a bad thing, though it's always worth avoiding bloat.

Well, absolutely agree on this.

Quoted 2 lines

- Dave

Signature · 2 lines
Cheers
- eric
#15 lore raw
In reply to David Brownell
Quoted 10 lines · David Brownell

Review

Quoted 4 lines · eric miao
(quoted diff) +1 -2
 	if (!requested)
-		printk(KERN_DEBUG "GPIO-%d autorequested\n",
-				chip->base + offset);
+		pr_debug("GPIO-%d autorequested\n", gpio);

Leave the printk in ... this is the sort of thing we want to see fixed, which becomes unlikely once you hide such diagnostics. And for that matter, what would be enabling the "-DDEBUG" that would trigger a pr_debug() message?

The original code isn't correct either. Either this is a debug message and indeed pr_debug() should be used, or it's not and KERN_DEBUG should be replaced by a lower log level.

Signature · 1 line
Jean Delvare
#16 lore raw
Quoted 12 lines · Jean Delvare
Quoted 10 lines · David Brownell

Review

Quoted 4 lines · eric miao
(quoted diff) +1 -2
 	if (!requested)
-		printk(KERN_DEBUG "GPIO-%d autorequested\n",
-				chip->base + offset);
+		pr_debug("GPIO-%d autorequested\n", gpio);

Leave the printk in ... this is the sort of thing we want to see fixed, which becomes unlikely once you hide such diagnostics. And for that matter, what would be enabling the "-DDEBUG" that would trigger a pr_debug() message?

Quoted 1 line

The original code isn't correct either.

It's perfectly correct. That it's an idiom you don't seem to *like* but is distinct from correctness.

Quoted 3 lines

Either this is a debug message
and indeed pr_debug() should be used, or it's not and KERN_DEBUG should be replaced by a lower log level.

KERN_DEBUG is what says the message level is "debug". Both styles log messages at that priority level.

Which is distinct from saying that the message should vanish from non-debug builds ... that's what pr_debug and friends do, by relying implicitly on "-DDEBUG".

In this case, the original code was saying that the message should NOT just vanish. One reason the patch was incorrect was that even on its own terms, it was wrong ... since it used the "-DDEBUG" mechanism wrong, and prevented the message from *EVER* appearing.

- Dave

#17 lore raw

Hi David,

Quoted 22 lines · David Brownell
Quoted 12 lines · Jean Delvare
Quoted 10 lines · David Brownell

Review

Quoted 4 lines · eric miao
(quoted diff) +1 -2
 	if (!requested)
-		printk(KERN_DEBUG "GPIO-%d autorequested\n",
-				chip->base + offset);
+		pr_debug("GPIO-%d autorequested\n", gpio);

Leave the printk in ... this is the sort of thing we want to see fixed, which becomes unlikely once you hide such diagnostics. And for that matter, what would be enabling the "-DDEBUG" that would trigger a pr_debug() message?

Quoted 1 line

The original code isn't correct either.

It's perfectly correct. That it's an idiom you don't seem to *like* but is distinct from correctness.

Quoted 3 lines

Either this is a debug message
and indeed pr_debug() should be used, or it's not and KERN_DEBUG should be replaced by a lower log level.

Quoted 6 lines

KERN_DEBUG is what says the message level is "debug". Both styles log messages at that priority level.

Which is distinct from saying that the message should vanish from non-debug builds ... that's what pr_debug and friends do, by relying implicitly on "-DDEBUG".

Were you trying to be funny or something? You aren't really suggesting that I don't know what a debug level is, and how pr_debug() works, are you?

Quoted 5 lines

In this case, the original code was saying that the message should NOT just vanish. One reason the patch was incorrect was that even on its own terms, it was wrong ... since it used the "-DDEBUG" mechanism wrong, and prevented the message from *EVER* appearing.

It's perfectly correct: developers can add "#define DEBUG" manually before they build the code. That it's an idiom you don't seem to *like* is distinct from correctness.

OK, can we stop now? David, you need to learn how to work with the community. The way you keep discussing every word of every reply is very unpleasant and not constructive at all. Whenever someone reviews your code, he or she is giving you something. You should be thankful for the help you received. Instead of that, it looks like you try to defend yourself against him/her. Please stop working against people reviewing your code, and start working _with_ them.

Thanks,

Signature · 1 line
Jean Delvare
#18 lore raw
In reply to eric miao
Quoted 4 lines · eric miao

Here comes the point of "struct gpio_desc"

Subject: [PATCH 3/5] use a per GPIO "struct gpio_desc" and chain "gpio_chip" to a list

I see what it does, but don't see the "why" ... surely you can come up with a one sentence description of why this would be better?

And I'd been so glad to *get rid of* that list, too.

lib/gpiolib.c line 31

Quoted 9 lines
lib/gpiolib.c +4 -4
+struct gpio_desc {
+	struct gpio_chip *chip;
+};
+
 
-/* gpio_lock protects modification to the table of chips and to
- * gpio_chip->requested.  If a gpio is requested, its gpio_chip
- * is not removable.
- */

But it still protects data. Don't remove documentation for what locks protect ... update it! Otherwise someonels going to come by and make a change which breaks the locking model. Usually in some subtle (hard-to-debug) way.

Review

Quoted 10 lines
(quoted diff) +1 -5
-	for (id = 0; id < ARRAY_SIZE(chips); id++) {
-		chip = chips[id];
-		if (!chip)
-			continue;
-
+	list_for_each_entry(chip, &gpio_chip_list, node) {
 		seq_printf(s, "%sGPIOs %d-%d, %s%s:\n",
 				started ? "\n" : "",
 				chip->base, chip->base + chip->ngpio - 1,

Note that this now produces the debug info in a relatively random order ... ordered by registration rather than anything useful, and hence awkward to read.

It'd be better if you just scanned your new gpio_desc[] table in numeric order, and start a new section whenever you find a new gpio_chip.

That'd get rid of that otherwise-useless list, too.

- Dave

#19 lore raw
Quoted 11 lines · David Brownell
Quoted 4 lines · eric miao

Here comes the point of "struct gpio_desc"

Subject: [PATCH 3/5] use a per GPIO "struct gpio_desc" and chain "gpio_chip" to a list

I see what it does, but don't see the "why" ... surely you can come up with a one sentence description of why this would be better?

And I'd been so glad to *get rid of* that list, too.

I'll be happy too.

Quoted 12 lines

lib/gpiolib.c line 31

Quoted 11 lines
lib/gpiolib.c +4 -4
+struct gpio_desc {
+     struct gpio_chip *chip;
+};
+
 
-/* gpio_lock protects modification to the table of chips and to
- * gpio_chip->requested.  If a gpio is requested, its gpio_chip
- * is not removable.
- */
Quoted 4 lines

But it still protects data. Don't remove documentation for what locks protect ... update it! Otherwise someonels going to come by and make a change which breaks the locking model. Usually in some subtle (hard-to-debug) way.

I'd prefer to name it "gpio_desc_lock" instead, which is self-explanatory and thus requires no comment at all

Quoted 16 lines

Review

Quoted 11 lines
(quoted diff) +1 -5
-     for (id = 0; id < ARRAY_SIZE(chips); id++) {
-             chip = chips[id];
-             if (!chip)
-                     continue;
-
+     list_for_each_entry(chip, &gpio_chip_list, node) {
              seq_printf(s, "%sGPIOs %d-%d, %s%s:\n",
                              started ? "\n" : "",
                              chip->base, chip->base + chip->ngpio - 1,

Note that this now produces the debug info in a relatively random order ... ordered by registration rather than anything useful, and hence awkward to read.

Quoted 6 lines

It'd be better if you just scanned your new gpio_desc[] table in numeric order, and start a new section whenever you find a new gpio_chip.

That'd get rid of that otherwise-useless list, too.

absolutely, you get the same feeling of mine and since this is for illustration purpose only, I don't want more patches to fix this...

Quoted 2 lines

- Dave

Signature · 2 lines
Cheers
- eric
#20 lore raw
In reply to eric miao
Quoted 5 lines · eric miao

so that requested will always be used, only *requested_str will be used for DEBUG_FS tracking assistance

Subject: [PATCH 2/5] define gpio_chip.requested_str as a debugfs tracking string

Doesn't seem unreasonable, since the extra cost is only one bit per GPIO (and that would be paid only when debugfs is available). What can I say ... I'd rather not pay the extra memory space. :)

#21 lore raw
In reply to eric miao
Quoted 5 lines · eric miao

Here comes a bunch of patches to illustrate my idea, some are not related to the point I mentioned, and they are not mature for now :-)

Subject: [PATCH 1/5] add gpio_is_onchip() for commonly used gpio range checking

I'll send substantive comments later, but meanwhile note that the *CURRENT* version was posted last Friday:

http://marc.info/?l=linux-kernel&m=119463810905330&w=2
http://marc.info/?l=linux-kernel&m=119463811005344&w=2
http://marc.info/?l=linux-kernel&m=119463811105352&w=2

Plus the appended tweak. It's more useful to send patches against current code, so applying them doesn't provide a small avalanche of rejects. :)

With respect to this patch adding gpio_is_onchip(), I don't see a point. The "gpio >= chip->base" check is basically "are the data structures corrupted?" and so it should only be done if "extra_checks" is defined. (And IMO, not then ...) And combining the other two tests that way doesn't make anything more clear to me. That's somewhat of a style issue, I guess, unless you're like me and don't much trust GCC to avoid extra instructions.

- Dave

#22 lore raw
Quoted 5 lines · David Brownell

  http://marc.info/?l=linux-kernel&m=119463810905330&w=2
  http://marc.info/?l=linux-kernel&m=119463811005344&w=2
  http://marc.info/?l=linux-kernel&m=119463811105352&w=2

Plus the appended tweak.

-ENOPATCH ... ;)

==========
Minor fixups to the gpiolib code.

---
 lib/gpiolib.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
lib/gpiolib.c +2 -2
@@ -28,7 +28,7 @@
 #define	extra_checks	0
 #endif
 
-/* gpio_lock protects the table of chips and to gpio_chip->requested.
+/* gpio_lock protects the table of chips and gpio_chip->requested.
  * While any gpio is requested, its gpio_chip is not removable.  It's
  * a raw spinlock to ensure safe access from hardirq contexts, and to
  * shrink bitbang overhead:  per-bit preemption would be very wrong.
@@ -533,6 +533,6 @@ static int __init gpiolib_debugfs_init(v
 				NULL, NULL, &gpiolib_operations);
 	return 0;
 }
-postcore_initcall(gpiolib_debugfs_init);
+subsys_initcall(gpiolib_debugfs_init);
 
 #endif	/* DEBUG_FS */
#23 lore raw
In reply to David Brownell
Quoted 10 lines · David Brownell
Quoted 5 lines · eric miao

Here comes a bunch of patches to illustrate my idea, some are not related to the point I mentioned, and they are not mature for now :-)

Subject: [PATCH 1/5] add gpio_is_onchip() for commonly used gpio range checking

I'll send substantive comments later, but meanwhile note that the *CURRENT* version was posted last Friday:

Quoted 8 lines

http://marc.info/?l=linux-kernel&m=119463810905330&w=2
http://marc.info/?l=linux-kernel&m=119463811005344&w=2
http://marc.info/?l=linux-kernel&m=119463811105352&w=2

Plus the appended tweak. It's more useful to send patches against current code, so applying them doesn't provide a small avalanche of rejects. :)

Ok, I'll update the patches later.

Quoted 10 lines

With respect to this patch adding gpio_is_onchip(), I don't see a point. The "gpio >= chip->base" check is basically "are the data structures corrupted?" and so it should only be done if "extra_checks" is defined. (And IMO, not then ...) And combining the other two tests that way doesn't make anything more clear to me. That's somewhat of a style issue, I guess, unless you're like me and don't much trust GCC to avoid extra instructions.

just a style issue, moving something commonly done into a routine, and extra_checks could be put there instead everywhere for a clean look :-)

Quoted 3 lines

- Dave

Signature · 2 lines
Cheers
- eric
#24 lore raw
In reply to eric miao
Quoted 4 lines · eric miao
Quoted 4 lines
Quoted 2 lines

Can we use "per gpio based" structure instead of "per gpio_chip" based one, just like what the generic IRQ layer is doing nowadays?

We "can" do most anything.  What would that improve though?

... What would that improve, though? Your followup posts still don't answer that question for me. I see the code, but don't have an answer to that question.

#25 lore raw
Quoted 10 lines · David Brownell
Quoted 4 lines · eric miao
Quoted 4 lines
Quoted 2 lines

Can we use "per gpio based" structure instead of "per gpio_chip" based one, just like what the generic IRQ layer is doing nowadays?

We "can" do most anything. What would that improve though?

... What would that improve, though? Your followup posts still don't answer that question for me. I see the code, but don't have an answer to that question.

to be honest, I don't feel like the holes. Put restrictions on the numbering of GPIOs might not be a good idea either.

Quoted 3 lines
Signature · 2 lines
Cheers
- eric
#26 lore raw
Quoted 9 lines · eric miao
Quoted 6 lines
Quoted 1 line
Quoted 1 line

We "can" do most anything. What would that improve though?

... What would that improve, though?  Your followup posts still don't answer that question for me.  I see the code, but don't have an answer to that question.

to be honest, I don't feel like the holes. Put restrictions on the numbering of GPIOs might not be a good idea either.

So the point of these is to make it easier for platforms (or even just boards) to make sure the GPIO number space is densely packed, rather than loosely so? Paying about 2KBytes for that privilege. (Assuming a 32 bit system with 256 GPIOs.)

I could see that being a reasonable tradeoff. I wouldn't have started there myself, but you know how that goes!

Does anyone else have any comments on that issue?

One point you haven't really brought up in this thread is your concern about the impact of this on IRQs. One issue being that for GPIOs used as IRQs, with linear mappings resembling

	static inline int gpio_to_irq(unsigned gpio)
	{
		if (gpio >= LAST_IRQ_CAPABLE_GPIO)
			return -EINVAL;
		return irq + FIRST_GPIO_IRQ_NUMBER;
	}

then tightly packed GPIOs mean less space wasted for IRQ descriptors that would never be used.

And since an irq_desc bigger than your gpio_desc, there's a tradeoff between wasting space on unused gpio_desc structs versus unused irq_desc structs. 2 KBytes would cost about only 35 irq_desc structs, vs 256 gpio_desc structs.

I'm guessing that's why you care about dense packing for the GPIO numbers...

- Dave

#27 lore raw
Quoted 10 lines · David Brownell

So the point of these is to make it easier for platforms (or even just boards) to make sure the GPIO number space is densely packed, rather than loosely so?  Paying about 2KBytes for that privilege.  (Assuming a 32 bit system with 256 GPIOs.)

I could see that being a reasonable tradeoff.  I wouldn't have started there myself, but you know how that goes!

Does anyone else have any comments on that issue?

Nobody else seems to have any comments on Eric's series of patches to add a gpio_desc layer ... whereas, I was looking at updating one platform, and got annoyed at some stuff that would have been non-issues with them in place!

Eric, would you feel like rolling an all-in-one patch against the gpiolib support from 2.6.24-rc3-mm? Including updated versions of your patches:

- [PATCH 2/5] define gpio_chip.requested_str (renaming it as "label" to match its usage)
- [PATCH 3/5] use a per GPIO "struct gpio_desc" (but without that needless list; for debug, just scan the gpio_desc list for the next non-null chip)
- [PATCH] move per GPIO "is_out" to "struct gpio_desc"

	(i.e. patch 4/5)

- [PATCH 5/5] move per GPIO "requested" to "struct gpio_desc" (and "label" too)

along with removing the ARCH_GPIOS_PER_CHIP symbol, and reducing ARCH_NR_GPIOS to a value which will waste less space by default? (Like maybe 256.)

I think an all-in-one patch will be easier to review and agree on including (or not).

- Dave

#28 lore raw

OK, here's the all-in-one patch based on David's most recent gpiolib fix,

Changes include:

1. use per gpio structure "gpio_desc", thus eliminating the restriction of ARCH_GPIOS_PER_CHIP, thus making it possible leaving no holes in GPIOs numbering

Note: the number of GPIOs on different GPIO expander chips may vary much, from 1-2 GPIOs on some audio codecs to dedicated 16-pin GPIO expander chips. The ARCH_GPIOS_PER_CHIP might not be fair enough.

this change introduces the following small changes:

a) removal of "gpio_chips[]" and use "gpio_desc[]" instead
b) move of "is_out" and "requested" from "struct gpio_chip" to "struct gpio_desc" and make them bit fields
c) removal of "gpiochip_is_requested()", and use "gpio_desc->requested" instead

Note: I do want a reference count field within gpio_chip to record the number of requested GPIOs within the chip, leave this to next patch

d) make gpio_ensure_requested() use gpio_desc, and simplify the code a bit

2. reduce ARCH_NR_GPIOS to 256, which is moderate for most sane platforms, and thus saving some memory of the per gpio structures

3. dedicated "gpio_desc.requested_label" field for use with debugfs

4. use a loop for "gpio_desc[]" instead of a loop for "gpio_chips[]" in gpiolib_show(), change is straight forward; since it is now per gpio based, the gpio_chip.dbg_show() is removed as well

------ >8 -------

include/asm-generic/gpio.h +11 -33
index 869b739..e3b2c8f 100644
@@ -14,14 +14,20 @@
  */
 
 #ifndef ARCH_NR_GPIOS
-#define ARCH_NR_GPIOS		512
-#endif
-
-#ifndef ARCH_GPIOS_PER_CHIP
-#define ARCH_GPIOS_PER_CHIP	BITS_PER_LONG
+#define ARCH_NR_GPIOS		256
 #endif
 
 struct seq_file;
+struct gpio_chip;
+
+struct gpio_desc {
+	struct gpio_chip	*chip;
+	unsigned		is_out:1;
+	unsigned		requested:1;
+#ifdef CONFIG_DEBUG_FS
+	const char		*requested_label;
+#endif
+};
 
 /**
  * struct gpio_chip - abstract a GPIO controller
@@ -41,8 +47,6 @@ struct seq_file;
  *	(base + ngpio - 1).
  * @can_sleep: flag must be set iff get()/set() methods sleep, as they
  *	must while accessing GPIO expander chips over I2C or SPI
- * @is_out: bit array where bit N is true iff GPIO with offset N has been
- *	 called successfully to configure this as an output
  *
  * A gpio_chip can help platforms abstract various sources of GPIOs so
  * they can all be accessed through a common programing interface.
@@ -65,37 +69,11 @@ struct gpio_chip {
 						unsigned offset, int value);
 	void			(*set)(struct gpio_chip *chip,
 						unsigned offset, int value);
-	void			(*dbg_show)(struct seq_file *s,
-						struct gpio_chip *chip);
 	int			base;
 	u16			ngpio;
 	unsigned		can_sleep:1;
-
-	/* other fields are modified by the gpio library only */
-	DECLARE_BITMAP(is_out, ARCH_GPIOS_PER_CHIP);
-
-#ifdef CONFIG_DEBUG_FS
-	/* fat bits */
-	const char		*requested[ARCH_GPIOS_PER_CHIP];
-#else
-	/* thin bits */
-	DECLARE_BITMAP(requested, ARCH_GPIOS_PER_CHIP);
-#endif
 };
 
-/* returns true iff a given gpio signal has been requested;
- * primarily for code dumping gpio_chip state.
- */
-static inline int
-gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
-{
-#ifdef CONFIG_DEBUG_FS
-	return chip->requested[offset] != NULL;
-#else
-	return test_bit(offset, chip->requested);
-#endif
-}
-
 /* add/remove chips */
 extern int gpiochip_add(struct gpio_chip *chip);
 extern int __must_check gpiochip_remove(struct gpio_chip *chip);
lib/gpiolib.c +84 -97

316 lines not on this page · raw message

struct gpio_chip *chip)

seq_printf(s, "\n");
}
-}
-
-static int gpiolib_show(struct seq_file *s, void *unused) -{
- struct gpio_chip *chip;
- unsigned id;
- int started = 0;
-
- /* REVISIT this isn't locked against gpio_chip removal ... */
-
- for (id = 0; id < ARRAY_SIZE(chips); id++) {
- chip = chips[id];
- if (!chip)
- continue;
-
- seq_printf(s, "%sGPIOs %d-%d, %s%s:\n",
- started ? "\n" : "",
- chip->base, chip->base + chip->ngpio - 1,
- chip->label ? : "generic",
- chip->can_sleep ? ", can sleep" : "");
- started = 1;
- if (chip->dbg_show)
- chip->dbg_show(s, chip);
- else
- gpiolib_dbg_show(s, chip);
- }
return 0;
}

------ >8 -------

Quoted 42 lines · David Brownell
Quoted 10 lines · David Brownell

So the point of these is to make it easier for platforms (or even just boards) to make sure the GPIO number space is densely packed, rather than loosely so? Paying about 2KBytes for that privilege. (Assuming a 32 bit system with 256 GPIOs.)

I could see that being a reasonable tradeoff. I wouldn't have started there myself, but you know how that goes!

Does anyone else have any comments on that issue?

Nobody else seems to have any comments on Eric's series of patches to add a gpio_desc layer ... whereas, I was looking at updating one platform, and got annoyed at some stuff that would have been non-issues with them in place!

Eric, would you feel like rolling an all-in-one patch against the gpiolib support from 2.6.24-rc3-mm? Including updated versions of your patches:

- [PATCH 2/5] define gpio_chip.requested_str (renaming it as "label" to match its usage)
- [PATCH 3/5] use a per GPIO "struct gpio_desc" (but without that needless list; for debug, just scan the gpio_desc list for the next non-null chip)
- [PATCH] move per GPIO "is_out" to "struct gpio_desc"

        (i.e. patch 4/5)

- [PATCH 5/5] move per GPIO "requested" to "struct gpio_desc" (and "label" too)

along with removing the ARCH_GPIOS_PER_CHIP symbol, and reducing ARCH_NR_GPIOS to a value which will waste less space by default? (Like maybe 256.)

I think an all-in-one patch will be easier to review and agree on including (or not).

- Dave

Signature · 2 lines
Cheers
- eric
#29 lore raw

Thanks, I'll be looking at it ... the one thing I strongly dislike is this change:

Quoted 3 lines · eric miao

4. use a loop for "gpio_desc[]" instead of a loop for "gpio_chips[]" in gpiolib_show(), change is straight forward; since it is now per gpio based, the gpio_chip.dbg_show() is removed as well

That removes the ability to display all kinds of significant chip-specific state ... like whether a given signal has active pullups or pulldowns, uses open-drain signaling, and so forth.

It also makes access a lot slower ... e.g. rather than one batch of I2C or SPI operations for all N signals on a chip, it's got to do N batches.

Plus it just needlessly breaks existing code.

I'll put together a version without that problem.

- Dave

#30 lore raw
In reply to eric miao
Quoted 2 lines · eric miao

c) removal of "gpiochip_is_requested()", and use "gpio_desc->requested" instead

That's problematic for GPIO code that needs to know if a given GPIO has been requested ... since you don't export gpio_desc[] (and shouldn't) that functionality has effectively been removed!

So that would break two patches now in MM (mcp23s08 support, and the avr32 platform conversion) plus others not yet merged...

Quoted 2 lines

d) make gpio_ensure_requested() use gpio_desc, and simplify the code a bit

That's problematic too ... since it no longer actually ensures that the chip gets marked as requested. :(

Since struct gpio_desc isn't exported, it might as well be private to lib/gpiolib.c instead of public in the header...

#31 lore raw
In reply to eric miao
Quoted 5 lines · eric miao

        status = chip->direction_input(chip, gpio); -       if (status == 0)
-               clear_bit(gpio, chip->is_out); +       if (status)
+               desc->is_out = 0;

You added that same bug in two places (direction_output too). Only zero status means success; otherwise it's negative errno.

Clearly this patch wasn't tested at all.

#32 lore raw

Sorry, I thought you want a preliminary one, here's the one tested and including your comments except for one:

if caller holds gpio_lock, gpio_ensure_requested() is actually safe.

please review:

---
 include/asm-generic/gpio.h |   35 +++-----
 lib/gpiolib.c              |  212 +++++++++++++++++++-------------------------
 2 files changed, 104 insertions(+), 143 deletions(-)
include/asm-generic/gpio.h +13 -22
index 869b739..34e60ba 100644
@@ -14,14 +14,22 @@
  */
 
 #ifndef ARCH_NR_GPIOS
-#define ARCH_NR_GPIOS		512
+#define ARCH_NR_GPIOS		256
 #endif
 
-#ifndef ARCH_GPIOS_PER_CHIP
-#define ARCH_GPIOS_PER_CHIP	BITS_PER_LONG
+struct seq_file;
+struct gpio_chip;
+
+struct gpio_desc {
+	struct gpio_chip	*chip;
+	unsigned		is_out:1;
+	unsigned		requested:1;
+#ifdef CONFIG_DEBUG_FS
+	const char		*requested_label;
 #endif
+};
 
-struct seq_file;
+extern struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
 
 /**
  * struct gpio_chip - abstract a GPIO controller
@@ -41,8 +49,6 @@ struct seq_file;
  *	(base + ngpio - 1).
  * @can_sleep: flag must be set iff get()/set() methods sleep, as they
  *	must while accessing GPIO expander chips over I2C or SPI
- * @is_out: bit array where bit N is true iff GPIO with offset N has been
- *	 called successfully to configure this as an output
  *
  * A gpio_chip can help platforms abstract various sources of GPIOs so
  * they can all be accessed through a common programing interface.
@@ -70,17 +76,6 @@ struct gpio_chip {
 	int			base;
 	u16			ngpio;
 	unsigned		can_sleep:1;
-
-	/* other fields are modified by the gpio library only */
-	DECLARE_BITMAP(is_out, ARCH_GPIOS_PER_CHIP);
-
-#ifdef CONFIG_DEBUG_FS
-	/* fat bits */
-	const char		*requested[ARCH_GPIOS_PER_CHIP];
-#else
-	/* thin bits */
-	DECLARE_BITMAP(requested, ARCH_GPIOS_PER_CHIP);
-#endif
 };
 
 /* returns true iff a given gpio signal has been requested;
@@ -89,11 +84,7 @@ struct gpio_chip {
 static inline int
 gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
 {
-#ifdef CONFIG_DEBUG_FS
-	return chip->requested[offset] != NULL;
-#else
-	return test_bit(offset, chip->requested);
-#endif
+	return gpio_desc[chip->base + offset].requested;
 }
 
 /* add/remove chips */
lib/gpiolib.c +90 -95

322 lines not on this page · raw message

struct gpio_chip *chip)
}

seq_printf(s, "\n");
- }
-}
-
-static int gpiolib_show(struct seq_file *s, void *unused) -{
- struct gpio_chip *chip;
- unsigned id;
- int started = 0;
-
- /* REVISIT this isn't locked against gpio_chip removal ... */
-
- for (id = 0; id < ARRAY_SIZE(chips); id++) {
- chip = chips[id];
- if (!chip)
- continue;
-
- seq_printf(s, "%sGPIOs %d-%d, %s%s:\n",
- started ? "\n" : "",
- chip->base, chip->base + chip->ngpio - 1,
- chip->label ? : "generic",
- chip->can_sleep ? ", can sleep" : "");
- started = 1;
- if (chip->dbg_show)
- chip->dbg_show(s, chip);
- else
- gpiolib_dbg_show(s, chip);
+ gpio++;
}
return 0;
}

Signature · 1 line
1.5.2.5.GIT
Quoted 12 lines · David Brownell

Review

Quoted 5 lines · eric miao

status = chip->direction_input(chip, gpio);

(quoted diff) +2 -2
-if (status == 0)
-clear_bit(gpio, chip->is_out);
+if (status)
+desc->is_out = 0;

You added that same bug in two places (direction_output too). Only zero status means success; otherwise it's negative errno.

Clearly this patch wasn't tested at all.

Signature · 2 lines
Cheers
- eric
#33 lore raw

[patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc

In reply to eric miao

Update gpiolib to use a table of per-GPIO "struct gpio_desc" instead of a table of "struct gpio_chip".

- Change "is_out" and "requested" from arrays in "struct gpio_chip" to bit fields in "struct gpio_desc", eliminating ARCH_GPIOS_PER_CHIP.

- Stop overloading "requested" flag with "label" tracked for debugfs.

- Change gpiochip_is_requested() into a regular function, since it now accesses data that's not exported from the gpiolib code. Also change its signature, for the same reason.

- Reduce default ARCH_NR_GPIOS to 256 to shrink gpio_desc table cost. On 32-bit platforms without debugfs, that table size is 2KB.

This makes it easier to work with chips with different numbers of GPIOs, and to avoid holes in GPIOs number sequences. Those holes can cost a lot of unusable irq_desc space for GPIOs that act as IRQs.

Based on a patch from Eric Miao.

# NOT signed-off yet ... purely for comment. It's been sanity tested.
---
The question I'm most interested in is whether it's worth paying the extra data memory. I'm currently leaning towards "yes", mostly since it'll let me be lazy about some development boards with four different kinds of GPIO controller, each with different numbers of GPIOs.

Note that the ARM AT91 updates are against a patch which has been circulated for comment but not yet submitted. The at32ap and mcp23s08 parts are currently in the MM tree. The PXA platform support didn't need changes; DaVinci won't either. The OMAP support (not recently updated) will need slightly more updates than those shown here.

 arch/arm/mach-at91/gpio.c            |    7 -
 arch/avr32/mach-at32ap/pio.c         |    7 -
 drivers/spi/mcp23s08.c               |    8 -
 include/asm-avr32/arch-at32ap/gpio.h |    2 
 include/asm-generic/gpio.h           |   45 +-------
 lib/gpiolib.c                        |  194 ++++++++++++++++++-----------------
 6 files changed, 129 insertions(+), 134 deletions(-)
arch/arm/mach-at91/gpio.c +5 -2
@@ -484,7 +484,10 @@ at91_bank_show(struct seq_file *s, struc
 	mdsr = __raw_readl(pio + PIO_MDSR);
 
 	for (i = 0, mask = 1; i < chip->ngpio; i++, mask <<= 1) {
-		if (!gpiochip_is_requested(chip, i)) {
+		const char	*label;
+
+		label = gpiochip_is_requested(chip, i);
+		if (!label) {
 			/* peripheral-A or peripheral B?
 			 * else unused/unrequested GPIO; ignore.
 			 */
@@ -501,7 +504,7 @@ at91_bank_show(struct seq_file *s, struc
 		 */
 		seq_printf(s, " gpio-%-3d P%c%-2d (%-12s) %s %s %s",
 			chip->base + i, 'A' + bank_num, i,
-			chip->requested[i],
+			label,
 			(osr & mask) ? "out" : "in ",
 			(mask & pdsr) ? "hi" : "lo",
 			(mask & pusr) ? "  " : "up");
arch/avr32/mach-at32ap/pio.c +5 -2
@@ -315,12 +315,15 @@ static void pio_bank_show(struct seq_fil
 	bank = 'A' + pio->pdev->id;
 
 	for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
-		if (!gpiochip_is_requested(chip, i))
+		const char *label;
+
+		label = gpiochip_is_requested(chip, i);
+		if (!label)
 			continue;
 
 		seq_printf(s, " gpio-%-3d P%c%-2d (%-12s) %s %s %s",
 			chip->base + i, bank, i,
-			chip->requested[i],
+			label,
 			(osr & mask) ? "out" : "in ",
 			(mask & pdsr) ? "hi" : "lo",
 			(mask & pusr) ? "  " : "up");
drivers/spi/mcp23s08.c +5 -3
@@ -184,12 +184,14 @@ static void mcp23s08_dbg_show(struct seq
 	}
 
 	for (t = 0, mask = 1; t < 8; t++, mask <<= 1) {
-		if (!gpiochip_is_requested(chip, t))
+		const char	*label;
+
+		label = gpiochip_is_requested(chip, t);
+		if (!label)
 			continue;
 
 		seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s",
-			chip->base + t, bank, t,
-			chip->requested[t],
+			chip->base + t, bank, t, label,
 			(mcp->cache[MCP_IODIR] & mask) ? "in " : "out",
 			(mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo",
 			(mcp->cache[MCP_GPPU] & mask) ? "  " : "up");
include/asm-avr32/arch-at32ap/gpio.h +1 -1
@@ -8,7 +8,7 @@
 /* Some GPIO chips can manage IRQs; some can't.  The exact numbers can
  * be changed if needed, but for the moment they're not configurable.
  */
-#define ARCH_NR_GPIOS	(NR_GPIO_IRQS + 2 * ARCH_GPIOS_PER_CHIP)
+#define ARCH_NR_GPIOS	(NR_GPIO_IRQS + 2 * 32)
 
 
 /* Arch-neutral GPIO API, supporting both "native" and external GPIOs. */
include/asm-generic/gpio.h +8 -37

73 lines not on this page · raw message

lib/gpiolib.c +105 -89

343 lines not on this page · raw message

#34 lore raw

Re: [patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc

Quoted 221 lines · David Brownell

Update gpiolib to use a table of per-GPIO "struct gpio_desc" instead of a table of "struct gpio_chip".

- Change "is_out" and "requested" from arrays in "struct gpio_chip" to bit fields in "struct gpio_desc", eliminating ARCH_GPIOS_PER_CHIP.

- Stop overloading "requested" flag with "label" tracked for debugfs.

- Change gpiochip_is_requested() into a regular function, since it now accesses data that's not exported from the gpiolib code. Also change its signature, for the same reason.

- Reduce default ARCH_NR_GPIOS to 256 to shrink gpio_desc table cost. On 32-bit platforms without debugfs, that table size is 2KB.

This makes it easier to work with chips with different numbers of GPIOs, and to avoid holes in GPIOs number sequences. Those holes can cost a lot of unusable irq_desc space for GPIOs that act as IRQs.

Based on a patch from Eric Miao.

# NOT signed-off yet ... purely for comment. It's been sanity tested.
---
The question I'm most interested in is whether it's worth paying the extra data memory. I'm currently leaning towards "yes", mostly since it'll let me be lazy about some development boards with four different kinds of GPIO controller, each with different numbers of GPIOs.

Note that the ARM AT91 updates are against a patch which has been circulated for comment but not yet submitted. The at32ap and mcp23s08 parts are currently in the MM tree. The PXA platform support didn't need changes; DaVinci won't either. The OMAP support (not recently updated) will need slightly more updates than those shown here.

 arch/arm/mach-at91/gpio.c            |    7 -
 arch/avr32/mach-at32ap/pio.c         |    7 -
 drivers/spi/mcp23s08.c               |    8 -
 include/asm-avr32/arch-at32ap/gpio.h |    2
 include/asm-generic/gpio.h           |   45 +-------
 lib/gpiolib.c                        |  194 ++++++++++++++++++-----------------
 6 files changed, 129 insertions(+), 134 deletions(-)
arch/arm/mach-at91/gpio.c 2007-11-27 14:32:01.000000000 -0800 renamed from arch/arm/mach-at91/gpio.c 2007-11-27 14:31:05.000000000 -0800 +5 -2
@@ -484,7 +484,10 @@ at91_bank_show(struct seq_file *s, struc
        mdsr = __raw_readl(pio + PIO_MDSR);
 
        for (i = 0, mask = 1; i < chip->ngpio; i++, mask <<= 1) {
-               if (!gpiochip_is_requested(chip, i)) {
+               const char      *label;
+
+               label = gpiochip_is_requested(chip, i);
+               if (!label) {
                        /* peripheral-A or peripheral B?
                         * else unused/unrequested GPIO; ignore.
                         */
@@ -501,7 +504,7 @@ at91_bank_show(struct seq_file *s, struc
                 */
                seq_printf(s, " gpio-%-3d P%c%-2d (%-12s) %s %s %s",
                        chip->base + i, 'A' + bank_num, i,
-                       chip->requested[i],
+                       label,
                        (osr & mask) ? "out" : "in ",
                        (mask & pdsr) ? "hi" : "lo",
                        (mask & pusr) ? "  " : "up");
arch/avr32/mach-at32ap/pio.c 2007-11-27 14:30:04.000000000 -0800 renamed from arch/avr32/mach-at32ap/pio.c 2007-11-27 14:30:03.000000000 -0800 +5 -2
@@ -315,12 +315,15 @@ static void pio_bank_show(struct seq_fil
        bank = 'A' + pio->pdev->id;
 
        for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
-               if (!gpiochip_is_requested(chip, i))
+               const char *label;
+
+               label = gpiochip_is_requested(chip, i);
+               if (!label)
                        continue;
 
                seq_printf(s, " gpio-%-3d P%c%-2d (%-12s) %s %s %s",
                        chip->base + i, bank, i,
-                       chip->requested[i],
+                       label,
                        (osr & mask) ? "out" : "in ",
                        (mask & pdsr) ? "hi" : "lo",
                        (mask & pusr) ? "  " : "up");
drivers/spi/mcp23s08.c 2007-11-27 14:30:04.000000000 -0800 renamed from drivers/spi/mcp23s08.c 2007-11-27 14:29:20.000000000 -0800 +5 -3
@@ -184,12 +184,14 @@ static void mcp23s08_dbg_show(struct seq
        }
 
        for (t = 0, mask = 1; t < 8; t++, mask <<= 1) {
-               if (!gpiochip_is_requested(chip, t))
+               const char      *label;
+
+               label = gpiochip_is_requested(chip, t);
+               if (!label)
                        continue;
 
                seq_printf(s, " gpio-%-3d P%c.%d (%-12s) %s %s %s",
-                       chip->base + t, bank, t,
-                       chip->requested[t],
+                       chip->base + t, bank, t, label,
                        (mcp->cache[MCP_IODIR] & mask) ? "in " : "out",
                        (mcp->cache[MCP_GPIO] & mask) ? "hi" : "lo",
                        (mcp->cache[MCP_GPPU] & mask) ? "  " : "up");
include/asm-avr32/arch-at32ap/gpio.h 2007-11-27 14:30:04.000000000 -0800 renamed from include/asm-avr32/arch-at32ap/gpio.h 2007-11-27 14:30:03.000000000 -0800 +1 -1
@@ -8,7 +8,7 @@
 /* Some GPIO chips can manage IRQs; some can't.  The exact numbers can
  * be changed if needed, but for the moment they're not configurable.
  */
-#define ARCH_NR_GPIOS  (NR_GPIO_IRQS + 2 * ARCH_GPIOS_PER_CHIP)
+#define ARCH_NR_GPIOS  (NR_GPIO_IRQS + 2 * 32)
 
 
 /* Arch-neutral GPIO API, supporting both "native" and external GPIOs. */
include/asm-generic/gpio.h 2007-11-27 14:30:04.000000000 -0800 renamed from include/asm-generic/gpio.h 2007-11-27 14:29:19.000000000 -0800 +8 -37
@@ -4,21 +4,16 @@
 #ifdef CONFIG_GPIO_LIB
 
 /* Platforms may implement their GPIO interface with library code,
- * at a small performance cost for non-inlined operations.
+ * at a small performance cost for non-inlined operations and some
+ * extra memory (for code and per-GPIO table entries).
  *
  * While the GPIO programming interface defines valid GPIO numbers
  * to be in the range 0..MAX_INT, this library restricts them to the
- * smaller range 0..ARCH_NR_GPIOS and allocates them in groups of
- * ARCH_GPIOS_PER_CHIP (which will usually be the word size used for
- * each bank of a SOC processor's integrated GPIO modules).
+ * smaller range 0..ARCH_NR_GPIOS.
  */
 
 #ifndef ARCH_NR_GPIOS
-#define ARCH_NR_GPIOS          512
-#endif
-
-#ifndef ARCH_GPIOS_PER_CHIP
-#define ARCH_GPIOS_PER_CHIP    BITS_PER_LONG
+#define ARCH_NR_GPIOS          256
 #endif
 
 struct seq_file;
@@ -36,13 +31,10 @@ struct seq_file;
  *     state (such as pullup/pulldown configuration).
  * @base: identifies the first GPIO number handled by this chip; or, if
  *     negative during registration, requests dynamic ID allocation.
- * @ngpio: the number of GPIOs handled by this controller; the value must
- *     be at most ARCH_GPIOS_PER_CHIP, so the last GPIO handled is
- *     (base + ngpio - 1).
+ * @ngpio: the number of GPIOs handled by this controller; the last GPIO
+ *     handled is (base + ngpio - 1).
  * @can_sleep: flag must be set iff get()/set() methods sleep, as they
  *     must while accessing GPIO expander chips over I2C or SPI
- * @is_out: bit array where bit N is true iff GPIO with offset N has been
- *      called successfully to configure this as an output
  *
  * A gpio_chip can help platforms abstract various sources of GPIOs so
  * they can all be accessed through a common programing interface.
@@ -70,31 +62,10 @@ struct gpio_chip {
        int                     base;
        u16                     ngpio;
        unsigned                can_sleep:1;
-
-       /* other fields are modified by the gpio library only */
-       DECLARE_BITMAP(is_out, ARCH_GPIOS_PER_CHIP);
-
-#ifdef CONFIG_DEBUG_FS
-       /* fat bits */
-       const char              *requested[ARCH_GPIOS_PER_CHIP];
-#else
-       /* thin bits */
-       DECLARE_BITMAP(requested, ARCH_GPIOS_PER_CHIP);
-#endif
 };
 
-/* returns true iff a given gpio signal has been requested;
- * primarily for code dumping gpio_chip state.
- */
-static inline int
-gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
-{
-#ifdef CONFIG_DEBUG_FS
-       return chip->requested[offset] != NULL;
-#else
-       return test_bit(offset, chip->requested);
-#endif
-}
+extern const char *gpiochip_is_requested(struct gpio_chip *chip,
+                       unsigned offset);
 
 /* add/remove chips */
 extern int gpiochip_add(struct gpio_chip *chip);
lib/gpiolib.c 2007-11-27 14:30:04.000000000 -0800 renamed from lib/gpiolib.c 2007-11-27 14:29:20.000000000 -0800 +11 -3
@@ -28,39 +28,41 @@
 #define        extra_checks    0
 #endif
 
-/* gpio_lock protects the table of chips and gpio_chip->requested.
+/* gpio_lock protects the gpio_desc[] table and desc->requested.
  * While any GPIO is requested, its gpio_chip is not removable;
  * each GPIO's "requested" flag serves as a lock and refcount.
  */
 static DEFINE_SPINLOCK(gpio_lock);
-static struct gpio_chip *chips[DIV_ROUND_UP(ARCH_NR_GPIOS,
-                                       ARCH_GPIOS_PER_CHIP)];
+
+struct gpio_desc {
+       struct gpio_chip        *chip;
+       unsigned                is_out:1;
+       unsigned                requested:1;
+#ifdef CONFIG_DEBUG_FS
+       const char              *label;
+#endif
+};
+static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
 
 
 /* Warn when drivers omit gpio_request() calls -- legal but

lib/gpiolib.c 2007-11-27 14:30:04.000000000 -0800 line 55

Quoted 10 lines
lib/gpiolib.c 2007-11-27 14:30:04.000000000 -0800 renamed from lib/gpiolib.c 2007-11-27 14:29:20.000000000 -0800 +4 -3
  * ill-advised when setting direction, and otherwise illegal.
  */
-static void gpio_ensure_requested(struct gpio_chip *chip, unsigned offset)
+static void gpio_ensure_requested(struct gpio_desc *desc)
 {
-       int             requested;
-
+       if (!desc->requested) {
+               desc->requested = 1;
+               pr_warning("GPIO-%ld autorequested\n", gpio_desc - desc);

produces a warning here about %ld, and maybe you mean desc - gpio_desc??

Quoted 310 lines
lib/gpiolib.c 2007-11-27 14:30:04.000000000 -0800 renamed from lib/gpiolib.c 2007-11-27 14:29:20.000000000 -0800 +86 -81
 #ifdef CONFIG_DEBUG_FS
-       requested = (int) chip->requested[offset];
-       if (!requested)
-               chip->requested[offset] = "[auto]";
-#else
-       requested = test_and_set_bit(offset, chip->requested);
+               desc->label = "[auto]";
 #endif
-
-       if (!requested)
-               printk(KERN_DEBUG "GPIO-%d autorequested\n",
-                               chip->base + offset);
+       }
 }
 
 /* caller holds gpio_lock *OR* gpio is marked as requested */
 static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
 {
-       return chips[gpio / ARCH_GPIOS_PER_CHIP];
+       return gpio_desc[gpio].chip;
 }
 
 /**
@@ -78,20 +80,26 @@ int gpiochip_add(struct gpio_chip *chip)
        int             status = 0;
        unsigned        id;
 
-       if (chip->base < 0 || (chip->base % ARCH_GPIOS_PER_CHIP) != 0)
-               return -EINVAL;
-       if ((chip->base + chip->ngpio) >= ARCH_NR_GPIOS)
-               return -EINVAL;
-       if (chip->ngpio > ARCH_GPIOS_PER_CHIP)
+       /* NOTE chip->base negative is reserved to mean a request for
+        * dynamic allocation.  We probably won't ever use that ...
+        */
+
+       if (chip->base < 0 || (chip->base  + chip->ngpio) >= ARCH_NR_GPIOS)
                return -EINVAL;
 
        spin_lock_irqsave(&gpio_lock, flags);
 
-       id = chip->base / ARCH_GPIOS_PER_CHIP;
-       if (chips[id] == NULL)
-               chips[id] = chip;
-       else
-               status = -EBUSY;
+       /* make sure the GPIOs are not claimed by any gpio_chip */
+       for (id = chip->base; id < chip->base + chip->ngpio; id++)
+               if (gpio_desc[id].chip != NULL) {
+                       status = -EBUSY;
+                       break;
+               }
+
+       if (status == 0) {
+               for (id = chip->base; id < chip->base + chip->ngpio; id++)
+                       gpio_desc[id].chip = chip;
+       }
 
        spin_unlock_irqrestore(&gpio_lock, flags);
        return status;
@@ -108,23 +116,19 @@ int gpiochip_remove(struct gpio_chip *ch
 {
        unsigned long   flags;
        int             status = 0;
-       int             offset;
-       unsigned        id = chip->base / ARCH_GPIOS_PER_CHIP;
+       unsigned        id;
 
        spin_lock_irqsave(&gpio_lock, flags);
 
-       for (offset = 0; offset < chip->ngpio; offset++) {
-               if (gpiochip_is_requested(chip, offset)) {
+       for (id = chip->base; id < chip->base + chip->ngpio; id++)
+               if (gpio_desc[id].requested) {
                        status = -EBUSY;
                        break;
                }
-       }
 
        if (status == 0) {
-               if (chips[id] == chip)
-                       chips[id] = NULL;
-               else
-                       status = -EINVAL;
+               for (id = chip->base; id < chip->base + chip->ngpio; id++)
+                       gpio_desc[id].chip = NULL;
        }
 
        spin_unlock_irqrestore(&gpio_lock, flags);
@@ -139,35 +143,28 @@ EXPORT_SYMBOL_GPL(gpiochip_remove);
  */
 int gpio_request(unsigned gpio, const char *label)
 {
-       struct gpio_chip        *chip;
+       struct gpio_desc        *desc;
        int                     status = -EINVAL;
        unsigned long           flags;
 
        spin_lock_irqsave(&gpio_lock, flags);
 
-       chip = gpio_to_chip(gpio);
-       if (!chip)
-               goto done;
-       gpio -= chip->base;
-       if (gpio >= chip->ngpio)
+       desc = &gpio_desc[gpio];
+       if (desc->chip == NULL)
                goto done;
 
        /* NOTE:  gpio_request() can be called in early boot,
         * before IRQs are enabled.
         */
 
-       status = 0;
+       if (!desc->requested) {
+               desc->requested = 1;
 #ifdef CONFIG_DEBUG_FS
-       if (!label)
-               label = "?";
-       if (chip->requested[gpio])
-               status = -EBUSY;
-       else
-               chip->requested[gpio] = label;
-#else
-       if (test_and_set_bit(gpio, chip->requested))
-               status = -EBUSY;
+               desc->label = (label == NULL) ? "?" : label;
 #endif
+               status = 0;
+       } else
+               status = -EBUSY;
 
 done:
        spin_unlock_irqrestore(&gpio_lock, flags);
@@ -178,33 +175,51 @@ EXPORT_SYMBOL_GPL(gpio_request);
 void gpio_free(unsigned gpio)
 {
        unsigned long           flags;
-       struct gpio_chip        *chip;
+       struct gpio_desc        *desc;
 
        spin_lock_irqsave(&gpio_lock, flags);
 
-       chip = gpio_to_chip(gpio);
-       if (!chip)
-               goto done;
-       gpio -= chip->base;
-       if (gpio >= chip->ngpio)
-               goto done;
-
+       desc = &gpio_desc[gpio];
+       if (desc->chip && desc->requested) {
+               desc->requested = 0;
 #ifdef CONFIG_DEBUG_FS
-       if (chip->requested[gpio])
-               chip->requested[gpio] = NULL;
-       else
-               chip = NULL;
-#else
-       if (!test_and_clear_bit(gpio, chip->requested))
-               chip = NULL;
+               desc->label = NULL;
 #endif
-       WARN_ON(extra_checks && chip == NULL);
-done:
+       } else
+               WARN_ON(extra_checks);
+
        spin_unlock_irqrestore(&gpio_lock, flags);
 }
 EXPORT_SYMBOL_GPL(gpio_free);
 
 
+/**
+ * gpiochip_is_requested - return string iff signal was requested
+ * @chip: controller managing the signal
+ * @offset: controller's identifer
+ *
+ * If debugfs support is enabled, the string returned is the label passed
+ * to gpio_request(); otherwise it is a meaningless constant.  When NULL
+ * is returned, the GPIO is not currently requested.
+ *
+ * This function is for use by GPIO controller drivers.  The label can
+ * help with diagnostics, and knowing that the signal is used as a GPIO
+ * can help ensure it's not accidentally given to another controller.
+ */
+const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset)
+{
+       unsigned gpio = chip->base + offset;
+
+       if (gpio >= ARCH_NR_GPIOS || gpio_desc[gpio].chip != chip)
+               return NULL;
+#ifdef CONFIG_DEBUG_FS
+       return gpio_desc[gpio].label;
+#else
+       return gpio_desc[gpio].requested ? "?" : NULL;
+#endif
+}
+EXPORT_SYMBOL_GPL(gpiochip_is_requested);
+
 
 /* Drivers MUST make configuration calls before get/set calls
  *
@@ -218,17 +233,18 @@ int gpio_direction_input(unsigned gpio)
 {
        unsigned long           flags;
        struct gpio_chip        *chip;
+       struct gpio_desc        *desc = &gpio_desc[gpio];
        int                     status = -EINVAL;
 
        spin_lock_irqsave(&gpio_lock, flags);
 
-       chip = gpio_to_chip(gpio);
+       chip = desc->chip;
        if (!chip || !chip->get || !chip->direction_input)
                goto fail;
        gpio -= chip->base;
        if (gpio >= chip->ngpio)
                goto fail;
-       gpio_ensure_requested(chip, gpio);
+       gpio_ensure_requested(desc);
 
        /* now we know the gpio is valid and chip won't vanish */
 
@@ -238,7 +254,7 @@ int gpio_direction_input(unsigned gpio)
 
        status = chip->direction_input(chip, gpio);
        if (status == 0)
-               clear_bit(gpio, chip->is_out);
+               desc->is_out = 0;
        return status;
 fail:
        spin_unlock_irqrestore(&gpio_lock, flags);
@@ -250,17 +266,18 @@ int gpio_direction_output(unsigned gpio,
 {
        unsigned long           flags;
        struct gpio_chip        *chip;
+       struct gpio_desc        *desc = &gpio_desc[gpio];
        int                     status = -EINVAL;
 
        spin_lock_irqsave(&gpio_lock, flags);
 
-       chip = gpio_to_chip(gpio);
+       chip = desc->chip;
        if (!chip || !chip->set || !chip->direction_output)
                goto fail;
        gpio -= chip->base;
        if (gpio >= chip->ngpio)
                goto fail;
-       gpio_ensure_requested(chip, gpio);
+       gpio_ensure_requested(desc);
 
        /* now we know the gpio is valid and chip won't vanish */
 
@@ -270,7 +287,7 @@ int gpio_direction_output(unsigned gpio,
 
        status = chip->direction_output(chip, gpio, value);
        if (status == 0)
-               set_bit(gpio, chip->is_out);
+               desc->is_out = 1;
        return status;
 fail:
        spin_unlock_irqrestore(&gpio_lock, flags);
@@ -366,26 +383,23 @@ EXPORT_SYMBOL_GPL(gpio_set_value_canslee
 
 static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
 {
-       unsigned        i;
+       unsigned                i;
+       unsigned                gpio = chip->base;
+       struct gpio_desc        *gdesc = &gpio_desc[gpio];
 
-       for (i = 0; i < chip->ngpio; i++) {
-               unsigned        gpio;
-               int             is_out;
 
-               if (!chip->requested[i])
+       for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) {
+               if (!gdesc->requested)
                        continue;
 
-               gpio = chip->base + i;
-               is_out = test_bit(i, chip->is_out);
-
                seq_printf(s, " gpio-%-3d (%-12s) %s %s",
-                       gpio, chip->requested[i],
-                       is_out ? "out" : "in ",
+                       gpio, gdesc->label,
+                       gdesc->is_out ? "out" : "in ",
                        chip->get
                                ? (chip->get(chip, i) ? "hi" : "lo")
                                : "?  ");
 
-               if (!is_out) {
+               if (!gdesc->is_out) {
                        int             irq = gpio_to_irq(gpio);
                        struct irq_desc *desc = irq_desc + irq;
 
@@ -435,14 +449,16 @@ static void gpiolib_dbg_show(struct seq_
 
 static int gpiolib_show(struct seq_file *s, void *unused)
 {
-       struct gpio_chip        *chip;
-       unsigned                id;
+       struct gpio_chip        *chip = NULL;
+       unsigned                gpio;
        int                     started = 0;
 
        /* REVISIT this isn't locked against gpio_chip removal ... */

lib/gpiolib.c 2007-11-27 14:30:04.000000000 -0800 static void gpiolib_dbg_show(struct seq_ · line 464

Quoted 10 lines
lib/gpiolib.c 2007-11-27 14:30:04.000000000 -0800 renamed from lib/gpiolib.c 2007-11-27 14:29:20.000000000 -0800 +4 -2
 
-       for (id = 0; id < ARRAY_SIZE(chips); id++) {
-               chip = chips[id];
+       for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) {
+               if (chip == gpio_desc[gpio].chip)
+                       continue;
+               chip = gpio_desc[gpio].chip;
                if (!chip)
                        continue;
 

Except for the above, I feel OK overall.

Signature · 2 lines
Cheers
- eric
#35 lore raw

Re: [patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc

Quoted 9 lines · eric miao
Quoted 7 lines

+static void gpio_ensure_requested(struct gpio_desc *desc)
 {
-       int             requested;
-
+       if (!desc->requested) {
+               desc->requested = 1;
+               pr_warning("GPIO-%ld autorequested\n", gpio_desc - desc);

produces a warning here about %ld,

It uses "%ld" since I got a warning with "%d". :(

Evidently a cast will be needed.

Quoted 1 line

and maybe you mean desc - gpio_desc??

Maybe. Either that or the "GPIO-" should be "GPIO"!

Quoted 1 line

Except for the above, I feel OK overall.

#36 lore raw

PATCH 2/4 pcf875x I2C GPIO expander driver

In reply to a message not in the archive

This is a new-style I2C driver for some common 8 and 16 bit I2C based GPIO expanders: pcf8574 and pcf8575. Since it's a new-style driver, it's configured as part of board-specific init ... eliminating the need for error-prone manual configuration of module parameters.

The driver exposes the GPIO signals using the platform-neutral GPIO programming interface, so they are easily accessed by other kernel code. The lack of such a flexible kernel API is what has ensured the proliferation of board-specific hacks for these chips... stuff that rarely makes it upstream since it's so ugly. This driver will let such board-specific code use standard GPIO calls.

---
Note that there's currently a drivers/i2c/chips/pcf8574.c driver.

Key differences include: this one talks to other kernel code so it can use the GPIOs "normally", but that one talks to userspace through sysfs. Also, this one is a "new style" I2C driver, so it's smaller and doesn't need all those error-prone module parameters. Plus, this one handles both 8 and 16 bit chip versions.

 drivers/i2c/chips/Kconfig   |   18 ++
 drivers/i2c/chips/Makefile  |    1 
 drivers/i2c/chips/pcf857x.c |  309 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pcf857x.h     |   43 ++++++
 4 files changed, 371 insertions(+)
drivers/i2c/chips/Kconfig +18 -0
@@ -51,6 +51,24 @@ config SENSORS_EEPROM
 	  This driver can also be built as a module.  If so, the module
 	  will be called eeprom.
 
+config GPIO_PCF857X
+	tristate "PCF875x GPIO expanders"
+	depends on GPIO_LIB
+	help
+	  Say yes here to provide access to some I2C GPIO expanders which
+	  may be used for additional digital outputs or inputs:
+
+	    - pcf8574, pcf8574a ... 8 bits, from NXP or TI
+	    - pcf8575, pcf8575c ... 16 bits, from NXP or TI
+
+	  Your board setup code will need to declare the expanders in
+	  use, and assign numbers to the GPIOs they expose.  Those GPIOs
+	  can then be used from drivers and other kernel code, just like
+	  other GPIOs, but only accessible from task contexts.
+
+	  This driver provides only an in-kernel interface to those GPIOs.
+	  Any sysfs interface to userspace would be provided separately.
+
 config SENSORS_PCF8574
 	tristate "Philips PCF8574 and PCF8574A"
 	depends on EXPERIMENTAL
include/linux/pcf857x.h new file +43 -0

43 lines not on this page · raw message

drivers/i2c/chips/Makefile +1 -0
@@ -11,6 +11,7 @@ obj-$(CONFIG_SENSORS_M41T00)	+= m41t00.o
 obj-$(CONFIG_SENSORS_PCA9539)	+= pca9539.o
 obj-$(CONFIG_SENSORS_PCF8574)	+= pcf8574.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
+obj-$(CONFIG_GPIO_PCF857X)	+= pcf857x.o
 obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
 obj-$(CONFIG_TPS65010)		+= tps65010.o
 obj-$(CONFIG_SENSORS_TLV320AIC23) += tlv320aic23.o
drivers/i2c/chips/pcf857x.c new file +309 -0

309 lines not on this page · raw message

#37 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Hi David,

Sorry for the late review.

Note that I can't test your code.

Quoted 12 lines · David Brownell

This is a new-style I2C driver for some common 8 and 16 bit I2C based GPIO expanders: pcf8574 and pcf8575. Since it's a new-style driver, it's configured as part of board-specific init ... eliminating the need for error-prone manual configuration of module parameters.

The driver exposes the GPIO signals using the platform-neutral GPIO programming interface, so they are easily accessed by other kernel code. The lack of such a flexible kernel API is what has ensured the proliferation of board-specific hacks for these chips... stuff that rarely makes it upstream since it's so ugly. This driver will let such board-specific code use standard GPIO calls.

Quoted 3 lines

---
Note that there's currently a drivers/i2c/chips/pcf8574.c driver.

By now there's also a drivers/i2c/chips/pcf8575.c driver.

Quoted 26 lines

Key differences include: this one talks to other kernel code so it can use the GPIOs "normally", but that one talks to userspace through sysfs. Also, this one is a "new style" I2C driver, so it's smaller and doesn't need all those error-prone module parameters. Plus, this one handles both 8 and 16 bit chip versions.

 drivers/i2c/chips/Kconfig   |   18 ++
 drivers/i2c/chips/Makefile  |    1 
 drivers/i2c/chips/pcf857x.c |  309 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pcf857x.h     |   43 ++++++
 4 files changed, 371 insertions(+)
drivers/i2c/chips/Kconfig +7 -0
@@ -51,6 +51,24 @@ config SENSORS_EEPROM
 	  This driver can also be built as a module.  If so, the module
 	  will be called eeprom.
 
+config GPIO_PCF857X
+	tristate "PCF875x GPIO expanders"
+	depends on GPIO_LIB
+	help
+	  Say yes here to provide access to some I2C GPIO expanders which
+	  may be used for additional digital outputs or inputs:
+

drivers/i2c/chips/Kconfig config SENSORS_EEPROM · line 70

Quoted 10 lines
drivers/i2c/chips/Kconfig +10 -0
+	    - pcf8574, pcf8574a ... 8 bits, from NXP or TI
+	    - pcf8575, pcf8575c ... 16 bits, from NXP or TI
+
+	  Your board setup code will need to declare the expanders in
+	  use, and assign numbers to the GPIOs they expose.  Those GPIOs
+	  can then be used from drivers and other kernel code, just like
+	  other GPIOs, but only accessible from task contexts.
+
+	  This driver provides only an in-kernel interface to those GPIOs.
+	  Any sysfs interface to userspace would be provided separately.

How?

Quoted 7 lines
drivers/i2c/chips/Kconfig +1 -0
+
 config SENSORS_PCF8574
 	tristate "Philips PCF8574 and PCF8574A"
 	depends on EXPERIMENTAL

include/linux/pcf857x.h line 7

Quoted 7 lines
include/linux/pcf857x.h new file +7 -0
@@ -0,0 +1,43 @@
+#ifndef __LINUX_PCF857X_H
+#define __LINUX_PCF857X_H
+
+/**
+ * struct pcf857x_platform_data - data to set up pcf857x driver
+ * @gpio_base: number of the chip's first GPIO
+ * @n_latch: optional bit-inverse of initial output state

Strange name, and I can't make much sense of the description either.

include/linux/pcf857x.h line 10

Quoted 3 lines
include/linux/pcf857x.h new file +3 -0
+ * @context: optional parameter passed to setup() and teardown()
+ * @setup: optional callback issued once the GPIOs are valid
+ * @teardown: optional callback issued before the GPIOs are invvalidated

Would make more sense to list setup and teardown before context?

Typo: invalidated.

Quoted 5 lines
include/linux/pcf857x.h new file +5 -0
+ *
+ * In addition to the I2C_BOARD_INFO() state appropriate to each chip,
+ * the i2c_board_info used with the pcf875x driver must provide the
+ * chip "type" ("pcf8574", "pcf8574a", "pcf8575", "pcf8575c") and its
+ * platform_data (pointer to one of these structures) with at least

include/linux/pcf857x.h line 25

Quoted 10 lines
include/linux/pcf857x.h new file +10 -0
+ * the gpio_base value initialized.
+ *
+ * The @setup callback may be used with the kind of board-specific glue
+ * which hands the (now-valid) GPIOs to other drivers, or which puts
+ * devices in their initial states using these GPIOs.
+ *
+ * Since these GPIO chips don't have conventional registers recording
+ * whether a pin is used for input or output, or an output latch to
+ * record the values being driven, the n_latch value may be used to
+ * avoid intialization glitches.  Its inverted value initializes the

Typo: initialization.

include/linux/pcf857x.h line 28

Quoted 3 lines
include/linux/pcf857x.h new file +3 -0
+ * value into which bits are masked before they're written to the PCF
+ * chip.  That means that if it's left at zero, the chip is treated as
+ * if it came from power-up reset.

After reading this paragraph I still have no idea what n_latch does. But maybe that's just me.

Quoted 18 lines
include/linux/pcf857x.h new file +15 -0
+ */
+struct pcf857x_platform_data {
+	unsigned	gpio_base;
+	unsigned	n_latch;
+
+	void		*context;
+	int		(*setup)(struct i2c_client *client,
+					int gpio, unsigned ngpio,
+					void *context);
+	int		(*teardown)(struct i2c_client *client,
+					int gpio, unsigned ngpio,
+					void *context);
+};
+
+#endif /* __LINUX_PCF857X_H */

drivers/i2c/chips/Makefile obj-$(CONFIG_SENSORS_M41T00) += m41t00.o · line 14

Quoted 4 lines
drivers/i2c/chips/Makefile +1 -0
@@ -11,6 +11,7 @@ obj-$(CONFIG_SENSORS_M41T00)	+= m41t00.o
 obj-$(CONFIG_SENSORS_PCA9539)	+= pca9539.o
 obj-$(CONFIG_SENSORS_PCF8574)	+= pcf8574.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
+obj-$(CONFIG_GPIO_PCF857X)	+= pcf857x.o

For alphabetical order, it would go one line above.

drivers/i2c/chips/pcf857x.c line 2

Quoted 8 lines
drivers/i2c/chips/Makefile +0 -0
 obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
 obj-$(CONFIG_TPS65010)		+= tps65010.o
 obj-$(CONFIG_SENSORS_TLV320AIC23) += tlv320aic23.o
drivers/i2c/chips/pcf857x.c new file +2 -0
@@ -0,0 +1,309 @@
+/*
+ * pcf857x - driver for pcf857{4,4a,5,5c} I2C GPIO expanders

I recommend spelling out chip names completely, as it lets people grep the kernel tree for chip names when they look for support.

Quoted 11 lines
drivers/i2c/chips/pcf857x.c new file +11 -0
+ *
+ * Copyright (C) 2007 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

drivers/i2c/chips/pcf857x.c line 23

Quoted 10 lines
drivers/i2c/chips/pcf857x.c new file +10 -0
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/pcf857x.h>

I suspect that there will be many more such header files in the future. Would it make sense to move them to include/linux/gpio?

drivers/i2c/chips/pcf857x.c line 29

Quoted 6 lines
drivers/i2c/chips/pcf857x.c new file +6 -0
+
+#include <asm/gpio.h>
+
+
+/*
+ * The pcf857x chips only expose a one read register and one write register.

Typo: extra "a".

Quoted 13 lines
drivers/i2c/chips/pcf857x.c new file +13 -0
+ * Writing a "one" bit (to match the reset state) lets that pin be used as
+ * an input; it's not an open-drain model, but it acts a bit like that.
+ *
+ * Some other I2C GPIO expander chips (like the pca9534, pca9535, pca9555,
+ * pca9539, mcp23008, and mc23017) have a more complex register model with
+ * more conventional input circuitry, often using 0x20..0x27 addresses.
+ */
+struct pcf857x {
+	struct gpio_chip	chip;
+	struct i2c_client	*client;
+	unsigned		out;		/* software latch */
+};
+

drivers/i2c/chips/pcf857x.c line 52

Quoted 10 lines
drivers/i2c/chips/pcf857x.c new file +10 -0
+/*-------------------------------------------------------------------------*/
+
+/* Talk to 8-bit I/O expander */
+
+static int pcf857x_input8(struct gpio_chip *chip, unsigned offset)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+
+	gpio->out |= (1 << offset);
+	return i2c_smbus_write_byte(gpio->client, (u8) gpio->out);

Useless cast.

drivers/i2c/chips/pcf857x.c line 61

Quoted 9 lines
drivers/i2c/chips/pcf857x.c new file +9 -0
+}
+
+static int pcf857x_get8(struct gpio_chip *chip, unsigned offset)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	s32		value;
+
+	value = i2c_smbus_read_byte(gpio->client);
+	return (value < 0) ? 0 : !!(value & (1 << offset));

!!(value & (1 << offset))
is more efficiently written
(value >> offset) & 1

drivers/i2c/chips/pcf857x.c line 73

Quoted 12 lines
drivers/i2c/chips/pcf857x.c new file +12 -0
+}
+
+static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	unsigned	bit = 1 << offset;
+
+	if (value)
+		gpio->out |= bit;
+	else
+		gpio->out &= ~bit;
+	return i2c_smbus_write_byte(gpio->client, (u8) gpio->out);

Useless cast.

drivers/i2c/chips/pcf857x.c line 79

Quoted 6 lines
drivers/i2c/chips/pcf857x.c new file +6 -0
+}
+
+static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
+{
+	pcf857x_output8(chip, offset, value);
+}

It would be more efficient to drop pcf857x_set8 altogether and do gpio->chip.set = pcf857x_output8.

Many of the comments above apply to the 16-bit functions below as well.

drivers/i2c/chips/pcf857x.c line 87

Quoted 8 lines
drivers/i2c/chips/pcf857x.c new file +8 -0
+
+/*-------------------------------------------------------------------------*/
+
+/* Talk to 16-bit I/O expander */
+
+static int i2c_write_le16(struct i2c_client *client, u16 word)
+{
+	u8 buf[2] = { word & 0xff, word >> 8, };

Stray comma.

Quoted 48 lines
drivers/i2c/chips/pcf857x.c new file +48 -0
+	int status;
+
+	status = i2c_master_send(client, buf, 2);
+	return (status < 0) ? status : 0;
+}
+
+static int i2c_read_le16(struct i2c_client *client)
+{
+	u8 buf[2];
+	int status;
+
+	status = i2c_master_recv(client, buf, 2);
+	if (status < 0)
+		return status;
+	return (buf[1] << 8) | buf[0];
+}
+
+static int pcf857x_input16(struct gpio_chip *chip, unsigned offset)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+
+	gpio->out |= (1 << offset);
+	return i2c_write_le16(gpio->client, (u16) gpio->out);
+}
+
+static int pcf857x_get16(struct gpio_chip *chip, unsigned offset)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		value;
+
+	value = i2c_read_le16(gpio->client);
+	return (value < 0) ? 0 : !!(value & (1 << offset));
+}
+
+static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	unsigned	bit = 1 << offset;
+
+	if (value)
+		gpio->out |= bit;
+	else
+		gpio->out &= ~bit;
+	return i2c_write_le16(gpio->client, (u16) gpio->out);
+}
+
+static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value)
+{

drivers/i2c/chips/pcf857x.c line 145

Quoted 10 lines
drivers/i2c/chips/pcf857x.c new file +10 -0
+	pcf857x_output16(chip, offset, value);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pcf857x_probe(struct i2c_client *client)
+{
+	struct pcf857x_platform_data	*pdata;
+	struct pcf857x			*gpio;
+	int				status = 0;

Useless initialization.

drivers/i2c/chips/pcf857x.c line 152

Quoted 7 lines
drivers/i2c/chips/pcf857x.c new file +7 -0
+
+	pdata = client->dev.platform_data;
+	if (!pdata)
+		return -ENODEV;
+
+	/* Allocate, initialize, and register this gpio_chip. */
+	gpio = kzalloc(sizeof *gpio, GFP_KERNEL);

You need to include <linux/slab.h> to have access to this function.

Quoted 14 lines
drivers/i2c/chips/pcf857x.c new file +14 -0
+	if (!gpio)
+		return -ENOMEM;
+
+	gpio->chip.base = pdata->gpio_base;
+	gpio->chip.can_sleep = 1;
+
+	/* NOTE:  the OnSemi jlc1562b is also largely compatible with
+	 * these parts, notably for output.  It has a low-resolution
+	 * DAC instead of pin change IRQs; and its inputs can be the
+	 * result of comparators.
+	 */
+
+	/* '74a addresses are 0x38..0x3f; '74 uses 0x20..0x27 */
+	if (strcmp(client->name, "pcf8574a") == 0

drivers/i2c/chips/pcf857x.c line 176

Quoted 10 lines
drivers/i2c/chips/pcf857x.c new file +10 -0
+			|| strcmp(client->name, "pcf8574") == 0) {
+		gpio->chip.ngpio = 8;
+		gpio->chip.direction_input = pcf857x_input8;
+		gpio->chip.get = pcf857x_get8;
+		gpio->chip.direction_output = pcf857x_output8;
+		gpio->chip.set = pcf857x_set8;
+
+		if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE))
+			status = -EIO;

You set status here...

drivers/i2c/chips/pcf857x.c line 179

Quoted 3 lines
drivers/i2c/chips/pcf857x.c new file +3 -0
+
+		/* fail if there's no chip present */
+		status = i2c_smbus_read_byte(client);

... but overwrite it immediately.

Quoted 7 lines
drivers/i2c/chips/pcf857x.c new file +7 -0
+
+	/* '75/'75c addresses are 0x20..0x27, just like the '74;
+	 * the '75c doesn't have a current source pulling high.
+	 */
+	} else if (strcmp(client->name, "pcf8575c") == 0
+			|| strcmp(client->name, "pcf8575") == 0) {
+		gpio->chip.ngpio = 16;

drivers/i2c/chips/pcf857x.c line 196

Quoted 10 lines
drivers/i2c/chips/pcf857x.c new file +10 -0
+		gpio->chip.direction_input = pcf857x_input16;
+		gpio->chip.get = pcf857x_get16;
+		gpio->chip.direction_output = pcf857x_output16;
+		gpio->chip.set = pcf857x_set16;
+
+		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+			status = -EIO;
+
+		/* fail if there's no chip present */
+		status = i2c_read_le16(client);

Same problem here.

drivers/i2c/chips/pcf857x.c line 205

Quoted 9 lines
drivers/i2c/chips/pcf857x.c new file +9 -0
+
+	} else
+		status = -ENODEV;
+
+	if (status < 0) {
+		dev_dbg(&client->dev, "probe error %d for '%s'\n",
+				status, client->name);
+		kfree(gpio);
+		return status;

Might make sense to move this to a common error path, as you do it more than once.

Quoted 59 lines
drivers/i2c/chips/pcf857x.c new file +59 -0
+	}
+
+	gpio->chip.label = client->name;
+
+	gpio->client = client;
+	i2c_set_clientdata(client, gpio);
+
+	/* NOTE:  these chips have strange "pseudo-bidirectional" I/O pins.
+	 * We can't actually know whether a pin is configured (a) as output
+	 * and driving the signal low, or (b) as input and reporting a low
+	 * value ... without knowing the last value written since the chip
+	 * came out of reset (if any).  We can't read the latched output.
+	 *
+	 * In short, the only reliable solution for setting up pin direction
+	 * is to do it explicitly.  The setup() method can do that.
+	 *
+	 * We use pdata->n_latch to avoid trouble.  In the typical case it's
+	 * left initialized to zero; our software copy of the "latch" then
+	 * matches the chip's reset state.  But there may be cases where a
+	 * system must drive some pins low, without transient glitching.
+	 * Handle those cases by assigning n_latch to a nonzero value.
+	 */
+	gpio->out = ~pdata->n_latch;
+
+	status = gpiochip_add(&gpio->chip);
+	if (status < 0) {
+		kfree(gpio);
+		return status;
+	}
+
+	/* NOTE: these chips can issue "some pin-changed" IRQs, which we
+	 * don't yet even try to use.  Among other issues, the relevant
+	 * genirq state isn't available to modular drivers; and most irq
+	 * methods can't be called from sleeping contexts.
+	 */
+
+	dev_info(&client->dev, "gpios %d..%d on a %s%s\n",
+			gpio->chip.base,
+			gpio->chip.base + gpio->chip.ngpio - 1,
+			client->name,
+			client->irq ? " (irq ignored)" : "");
+
+	/* Let platform code set up the GPIOs and their users.
+	 * Now is the first time anyone can use them.
+	 */
+	if (pdata->setup) {
+		status = pdata->setup(client,
+				gpio->chip.base, gpio->chip.ngpio,
+				pdata->context);
+		if (status < 0)
+			dev_dbg(&client->dev, "setup --> %d\n", status);
+	}
+
+	return 0;
+}
+
+static int pcf857x_remove(struct i2c_client *client)
+{
+	struct pcf857x_platform_data	*pdata = client->dev.platform_data;

drivers/i2c/chips/pcf857x.c line 274

Quoted 10 lines
drivers/i2c/chips/pcf857x.c new file +10 -0
+	struct pcf857x			*gpio = i2c_get_clientdata(client);
+	int				status = 0;
+
+	if (pdata->teardown) {
+		status = pdata->teardown(client,
+				gpio->chip.base, gpio->chip.ngpio,
+				pdata->context);
+		if (status < 0) {
+			dev_err(&client->dev, "%s --> %d\n",
+					"teardown", status);

Why %s instead of hard-coding "teardown"?

Why is this an error message while a failing setup() at initialization time only deserves a debug message?

Quoted 25 lines
drivers/i2c/chips/pcf857x.c new file +25 -0
+			return status;
+		}
+	}
+
+	status = gpiochip_remove(&gpio->chip);
+	if (status == 0)
+		kfree(gpio);
+	else
+		dev_err(&client->dev, "%s --> %d\n", "remove", status);
+	return status;
+}
+
+static struct i2c_driver pcf857x_driver = {
+	.driver = {
+		.name	= "pcf857x",
+		.owner	= THIS_MODULE,
+	},
+	.probe	= pcf857x_probe,
+	.remove	= pcf857x_remove,
+};
+
+static int __init pcf857x_init(void)
+{
+	return i2c_add_driver(&pcf857x_driver);
+}

drivers/i2c/chips/pcf857x.c line 309

Quoted 10 lines
drivers/i2c/chips/pcf857x.c new file +10 -0
+/* we want GPIOs to be ready at device_initcall() time */
+subsys_initcall(pcf857x_init);
+
+static void __exit pcf857x_exit(void)
+{
+	i2c_del_driver(&pcf857x_driver);
+}
+module_exit(pcf857x_exit);
+
+MODULE_LICENSE("GPL");

No MODULE_AUTHOR? No entry in MAINTAINERS?

Signature · 1 line
Jean Delvare
#38 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Quoted 4 lines · Jean Delvare

!!(value & (1 << offset))
is more efficiently written
(value >> offset) & 1

... but not more efficiently implemented.

Your version requires code to do the shift on live data at runtime. David's version lets the compiler create the mask once, at compile-time.

b.g.

Signature · 2 lines
Bill Gatliff
[email protected]
#39 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Hi Bill,

Quoted 9 lines · Bill Gatliff
Quoted 3 lines · Jean Delvare

!!(value & (1 << offset))
is more efficiently written
(value >> offset) & 1

... but not more efficiently implemented.

Your version requires code to do the shift on live data at runtime. David's version lets the compiler create the mask once, at compile-time.

I don't understand. How could the compiler create the mask at compile-time when "offset" is only known at runtime?

Anyway, I just checked and gcc (4.1.2 on x86_86) generates the same code for both expressions, so there's not much to argue about. David, feel free to ignore my comment if you prefer your implementation :)

Signature · 1 line
Jean Delvare
#40 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Quoted 10 lines · Jean Delvare

Hi Bill,

Quoted 6 lines · Bill Gatliff
Quoted 4 lines · Jean Delvare

!!(value & (1 << offset))
is more efficiently written
(value >> offset) & 1

Quoted 8 lines
Quoted 4 lines

... but not more efficiently implemented.

Your version requires code to do the shift on live data at runtime. David's version lets the compiler create the mask once, at compile-time.

I don't understand. How could the compiler create the mask at compile-time when "offset" is only known at runtime?

Oops. :) I was thinking of some different code. Disregard.

/me got up too early this morning, after working too late last night!

b.g.

Signature · 2 lines
Bill Gatliff
[email protected]
#41 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

In reply to Jean Delvare
Quoted 3 lines · Jean Delvare

Hi David,

Sorry for the late review.

I've got patches in my queue too, sigh ...

Thanks for the review. I'll snip out typos and similar trivial comments (and fix them!), using responses here for more the substantive feedback.

- Dave

Quoted 6 lines
Quoted 6 lines
drivers/i2c/chips/Kconfig +0 -0
@@ -51,6 +51,24 @@ config SENSORS_EEPROM
 	  This driver can also be built as a module.  If so, the module
 	  will be called eeprom.
 
Quoted 10 lines

drivers/i2c/chips/Kconfig config SENSORS_EEPROM · line 70

Quoted 8 lines
drivers/i2c/chips/Kconfig +8 -0
+config GPIO_PCF857X
+	tristate "PCF875x GPIO expanders"
+	depends on GPIO_LIB
+	help
+	  ...
+
+	  This driver provides only an in-kernel interface to those GPIOs.
+	  Any sysfs interface to userspace would be provided separately.

How?

I'll take that out, to avoid the question. The answer is still mostly TBD, but the gpiolib infrastructure provides a number of the hooks that such a userspace interface would need.

Quoted 6 lines

drivers/i2c/chips/Kconfig config SENSORS_EEPROM · line 65

Quoted 4 lines
drivers/i2c/chips/Kconfig +4 -0
+/**
+ * struct pcf857x_platform_data - data to set up pcf857x driver
+ * @gpio_base: number of the chip's first GPIO
+ * @n_latch: optional bit-inverse of initial output state

Strange name, and I can't make much sense of the description either.

Updated description:

* @n_latch: optional bit-inverse of initial register value; if
* you leave this initialized to zero, the driver will treat
* all bits as inputs as if the chip was just reset

This chip is documented as being "pseudo-bidirectional", which is a sign that there are some confusing mechanisms lurking...

Conventions for naming negative-true signals include a "#" suffix (illegal for C), a overbar (not expressible in ASCII), and prefixes including "/" (illegal for C) and "n" (aha!). I morphed the latter into "n_" since it's often paired with all-caps signal names, as in "nRESET", which are bad kernel coding style.

Latches hold values; http://en.wikipedia.org/wiki/Latch_%28electronics%29
talks about bit-level latching, but GPIO controllers use register-wide latches to record the value that should be driven on output pins. (As opposed to input pins, whose values are read without latching.)

Quoted 2 lines

After reading this paragraph I still have no idea what n_latch does. But maybe that's just me.

It's a wierd little arrangement, maybe you have a better explanation. I tried hitting the confusing points more directly:

* These GPIO chips are only "pseudo-bidirectional"; read the chip specs
* to understand the behavior. They don't have separate registers to
* record which pins are used for input or output, record which output
* values are driven, or provide access to input values. That must all
* be inferred by reading the chip's value and knowing the last value
* written to it. If you don't initialize n_latch, that last written
* value is presumed to be all ones (as if the chip were just reset).

Quoted 9 lines

drivers/i2c/chips/Makefile obj-$(CONFIG_SENSORS_M41T00) += m41t00.o · line 14

Quoted 7 lines
drivers/i2c/chips/Makefile +1 -0
@@ -11,6 +11,7 @@ obj-$(CONFIG_SENSORS_M41T00)	+= m41t00.o
 obj-$(CONFIG_SENSORS_PCA9539)	+= pca9539.o
 obj-$(CONFIG_SENSORS_PCF8574)	+= pcf8574.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
+obj-$(CONFIG_GPIO_PCF857X)	+= pcf857x.o

For alphabetical order, it would go one line above.

For alphabetical order it would go much sooner.
GPIO precedes SENSOR. ;)

Quoted 11 lines

drivers/i2c/chips/pcf857x.c line 2

Quoted 8 lines
drivers/i2c/chips/Makefile +0 -0
 obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
 obj-$(CONFIG_TPS65010)		+= tps65010.o
 obj-$(CONFIG_SENSORS_TLV320AIC23) += tlv320aic23.o
drivers/i2c/chips/pcf857x.c new file +2 -0
@@ -0,0 +1,309 @@
+/*
+ * pcf857x - driver for pcf857{4,4a,5,5c} I2C GPIO expanders

I recommend spelling out chip names completely, as it lets people grep the kernel tree for chip names when they look for support.

I'll do that -- but note that the names *are* spelled out later.

Quoted 4 lines

drivers/i2c/chips/pcf857x.c line 23

Quoted 1 line
drivers/i2c/chips/pcf857x.c new file +1 -0
+#include <linux/pcf857x.h>

I suspect that there will be many more such header files in the future. Would it make sense to move them to include/linux/gpio?

I was thinking more like <linux/i2c/...> myself. There are many more I2C chips than GPIO expanders.

Quoted 10 lines

drivers/i2c/chips/pcf857x.c line 79

Quoted 7 lines
drivers/i2c/chips/pcf857x.c new file +7 -0
+static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
+	...
+
+static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
+{
+	pcf857x_output8(chip, offset, value);
+}

It would be more efficient to drop pcf857x_set8 altogether and do gpio->chip.set = pcf857x_output8.

No can do; return types differ, which means that on some platforms the calling conventions have significant differences.

Quoted 4 lines

drivers/i2c/chips/pcf857x.c line 274

Quoted 2 lines
drivers/i2c/chips/pcf857x.c new file +2 -0
+                     dev_err(&client->dev, "%s --> %d\n",
+                                     "teardown", status);

Why %s instead of hard-coding "teardown"?

To share (current code) three copies of the "<3>%s %s: %s --> %d\n" string. Every little bit of kernel bloat prevention helps. ;)

#42 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Hi David,

Quoted 18 lines · David Brownell
Quoted 6 lines · Jean Delvare
Quoted 6 lines
drivers/i2c/chips/Kconfig +0 -0
@@ -51,6 +51,24 @@ config SENSORS_EEPROM
 	  This driver can also be built as a module.  If so, the module
 	  will be called eeprom.
 
Quoted 10 lines

drivers/i2c/chips/Kconfig config SENSORS_EEPROM · line 70

Quoted 8 lines
drivers/i2c/chips/Kconfig +8 -0
+config GPIO_PCF857X
+	tristate "PCF875x GPIO expanders"
+	depends on GPIO_LIB
+	help
+	  ...
+
+	  This driver provides only an in-kernel interface to those GPIOs.
+	  Any sysfs interface to userspace would be provided separately.

How?

Quoted 3 lines

I'll take that out, to avoid the question. The answer is still mostly TBD, but the gpiolib infrastructure provides a number of the hooks that such a userspace interface would need.

So the user-space interface would be part of the generic GPIO infrastructure? I like the idea.

Quoted 35 lines
Quoted 6 lines

drivers/i2c/chips/Kconfig config SENSORS_EEPROM · line 65

Quoted 4 lines
drivers/i2c/chips/Kconfig +4 -0
+/**
+ * struct pcf857x_platform_data - data to set up pcf857x driver
+ * @gpio_base: number of the chip's first GPIO
+ * @n_latch: optional bit-inverse of initial output state

Strange name, and I can't make much sense of the description either.

Updated description:

* @n_latch: optional bit-inverse of initial register value; if
* you leave this initialized to zero, the driver will treat
* all bits as inputs as if the chip was just reset

This chip is documented as being "pseudo-bidirectional", which is a sign that there are some confusing mechanisms lurking...

Conventions for naming negative-true signals include a "#" suffix (illegal for C), a overbar (not expressible in ASCII), and prefixes including "/" (illegal for C) and "n" (aha!). I morphed the latter into "n_" since it's often paired with all-caps signal names, as in "nRESET", which are bad kernel coding style.

Latches hold values; http://en.wikipedia.org/wiki/Latch_%28electronics%29
talks about bit-level latching, but GPIO controllers use register-wide latches to record the value that should be driven on output pins. (As opposed to input pins, whose values are read without latching.)

Quoted 2 lines

After reading this paragraph I still have no idea what n_latch does. But maybe that's just me.

It's a wierd little arrangement, maybe you have a better explanation. I tried hitting the confusing points more directly:

Quoted 7 lines

* These GPIO chips are only "pseudo-bidirectional"; read the chip specs
* to understand the behavior. They don't have separate registers to
* record which pins are used for input or output, record which output
* values are driven, or provide access to input values. That must all
* be inferred by reading the chip's value and knowing the last value
* written to it. If you don't initialize n_latch, that last written
* value is presumed to be all ones (as if the chip were just reset).

Much clearer now, thanks. I know what a latch is, I just couldn't get how latching (or lack thereof) was related with an initial register value. With the explanation above, I get it.

Quoted 10 lines
Quoted 9 lines

drivers/i2c/chips/Makefile obj-$(CONFIG_SENSORS_M41T00) += m41t00.o · line 14

Quoted 7 lines
drivers/i2c/chips/Makefile +1 -0
@@ -11,6 +11,7 @@ obj-$(CONFIG_SENSORS_M41T00)	+= m41t00.o
 obj-$(CONFIG_SENSORS_PCA9539)	+= pca9539.o
 obj-$(CONFIG_SENSORS_PCF8574)	+= pcf8574.o
 obj-$(CONFIG_SENSORS_PCF8591)	+= pcf8591.o
+obj-$(CONFIG_GPIO_PCF857X)	+= pcf857x.o

For alphabetical order, it would go one line above.

Quoted 2 lines

For alphabetical order it would go much sooner.
GPIO precedes SENSOR. ;)

We apply the alphabetical order to driver names, not configuration symbols, as far as I know. Though for most directories it probably doesn't make a difference; drivers/i2c/chips is admittedly a bit messy in this respect.

Note that at some point I will attempt to get rid of the "SENSORS" part of configuration options that have nothing to do with sensors, that should help a bit.

Quoted 7 lines
Quoted 4 lines

drivers/i2c/chips/Makefile obj-$(CONFIG_SENSORS_M41T00) += m41t00.o · line 15

Quoted 1 line
drivers/i2c/chips/Makefile +1 -0
+#include <linux/pcf857x.h>

I suspect that there will be many more such header files in the future. Would it make sense to move them to include/linux/gpio?

I was thinking more like <linux/i2c/...> myself. There are many more I2C chips than GPIO expanders.

But most i2c chip drivers don't need a header file. Or is this going to change with the new-style i2c drivers?

Along the same line, I am wondering if it would make sense to put the various GPIO drivers in drivers/gpio. It's a much better practice to group the drivers according to the functionality they provide than the way they are connected to the system. drivers/i2c/chips is an exception in this respect, it's meant for i2c drivers that have no obvious place to live in. That's why there aren't many drivers there, and I hope it will stay this way. In an ideal world we could even get rid of this directory and move the remaining drivers to drivers/misc.

Quoted 11 lines
Quoted 10 lines

drivers/i2c/chips/Makefile obj-$(CONFIG_SENSORS_M41T00) += m41t00.o

Quoted 7 lines
drivers/i2c/chips/Makefile +7 -0
+static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
+	...
+
+static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
+{
+	pcf857x_output8(chip, offset, value);
+}

It would be more efficient to drop pcf857x_set8 altogether and do gpio->chip.set = pcf857x_output8.

Quoted 2 lines

No can do; return types differ, which means that on some platforms the calling conventions have significant differences.

Ah, right, sorry for missing that. I had only looked at the parameters and forgot the return type.

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

+                     dev_err(&client->dev, "%s --> %d\n", +                                     "teardown", status);

Why %s instead of hard-coding "teardown"?

To share (current code) three copies of the "<3>%s %s: %s --> %d\n" string. Every little bit of kernel bloat prevention helps. ;)

Only two copies in the version you posted, but indeed there would be three if the trick was applied consistently.

Signature · 1 line
Jean Delvare
#43 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Quoted 3 lines · Jean Delvare

So the user-space interface would be part of the generic GPIO infrastructure? I like the idea.

I thought that would make sense too! :) Someone would need to write the code though. Having such a mechanism would provide another "carrot" to migrate folk towards the gpiolib core.

I think adding a gpiochip primitive to mark a (potential) GPIO as invalid would support the converse of /sys/kernel/debug/gpio. Invalid GPIOs include pins set up for non-GPIO usage (like being used for MMC or MII), or not wired up on a given board. Pins that were valid as GPIOs and not requested by a kernel driver might reasonably be managed by userspace code.

Quoted 10 lines
Quoted 7 lines
Quoted 4 lines
Quoted 1 line

+#include <linux/pcf857x.h>

I suspect that there will be many more such header files in the future. Would it make sense to move them to include/linux/gpio?

I was thinking more like <linux/i2c/...> myself. There are many more I2C chips than GPIO expanders.

But most i2c chip drivers don't need a header file. Or is this going to change with the new-style i2c drivers?

I expect it will become a lot more common. Remember that legacy I2C drivers *couldn't* get any board-specific config data; that's been problematic, since it meant the drivers themselves ended up
with lots of board-specific cruft. That prevented many drivers
from going upstream at all. (As I mentioned about pcf8574 code, although in that case the problem was worsened by lack of any reusable kernel interface for such GPIO signals.)

Quoted 2 lines

Along the same line, I am wondering if it would make sense to put the various GPIO drivers in drivers/gpio.

Could be. Right now we have three "GPIO expander" drivers using the new "gpiolib" framework: pcf875x and pca9539 for I2C, and mcp23s08 for SPI. There are many more that *could* be used with Linux boxes. And there are other drivers/XYZ directories that are (currently) that small. Maybe gpiolib should go upstream like that, and lib/gpiolib should be in drivers/gpio too...

However, keep in mind that lots of chips export a few GPIOs but don't have that as their core functionality ... one example is the drivers/i2c/chips/tps65010 driver. So it'd never be the case that GPIO drivers only live in that directory.

- Dave

#44 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Quoted 8 lines · David Brownell
Quoted 3 lines · Jean Delvare

So the user-space interface would be part of the generic GPIO infrastructure? I like the idea.

I thought that would make sense too! :) Someone would need to write the code though. Having such a mechanism would provide another "carrot" to migrate folk towards the gpiolib core.

Here's some code to do this. It's not entirely perfect yet, but it is
usable. I create a directory in sysfs (class/gpio/<name>:<num>) for each
gpio line. The are files in this directory to allow reading/setting the
gpio value and direction. One can also see the label associated with the
gpio if gpiolib is keeping track of labels (i.e. debugfs is on). sysfs
also creates a gpio directory under the device associated with the gpio
lines (this is a new thing one needs to add) with that device's gpios. Actually, think these are "real" sysfs directories, and the ones in /sys/class are copies made by sysfs.

----------------------------------------------------------------------------

Quoted 1 line

From c65d0fb239b79de7f595e47edb2fb641217e7309 Mon Sep 17 00:00:00 2001

From: Trent Piepho <[email protected]>
Date: Thu, 3 Apr 2008 18:37:23 -0700
Subject: GPIO: Create a sysfs gpio class for messing with GPIOs from userspace

struct gpio_chip gets a new field, dev, which points to the struct device of whatever the gpio lines are part of. If this is non-NULL, gpio class entries are created for this device when gpiochip_add() is called, by a new function gpiochip_classdev_register().

It creates a sysfs directory for each gpio the chip defines. Each device has three attributes so far, direction, value, and label. label is read-only, and will only be present if DEBUG_FS is on, as without DEBUG_FS the gpio layer doesn't keep track of any labels.

Maybe the value file should be changed to RO or WO depending on direction?

Setting the direction auto-allocates the gpio, which is not really what I wanted. Maybe I should call the chip set method directly?

There are almost certainly a bunch of races with gpio_desc access.

No code has been written yet to remove the devices from the class when the class is removed.

The GPIO_CLASS define/ifdef code should either be removed, or turned into a Kconfig variable that can be used turn this feature on and off.

---
 drivers/gpio/gpiolib.c     |  188 ++++++++++++++++++++++++++++++++++++++++++++
 include/asm-generic/gpio.h |    2 +
 2 files changed, 190 insertions(+), 0 deletions(-)
drivers/gpio/gpiolib.c +188 -0
index d8db2f8..db0677d 100644
@@ -18,6 +18,7 @@
  * only an instruction or two per bit.
  */
 
+#define GPIO_CLASS	1
 
 /* When debugging, extend minimal trust to callers and platform code.
  * Also emit diagnostic messages that may help initial bringup, when
@@ -47,6 +48,9 @@ struct gpio_desc {
 #ifdef CONFIG_DEBUG_FS
 	const char		*label;
 #endif
+#if GPIO_CLASS
+	struct device		*dev;
+#endif
 };
 static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
 
@@ -57,6 +61,174 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
 #endif
 }
 
+#if GPIO_CLASS
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+
+static struct class *gpio_class;
+
+static ssize_t gpio_direction_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+
+	strcpy(buf, test_bit(FLAG_IS_OUT, &gdesc->flags) ? "out\n" : "in\n\0");
+
+	return 5;
+}
+
+static ssize_t gpio_direction_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+	int d, n = gdesc - gpio_desc;
+
+	if (size >= 3 && !strncmp(buf, "out", 3)) {
+		d = 1;
+	} else if (size >= 2 && !strncmp(buf, "in", 2)) {
+		d = 0;
+	} else {
+		d = simple_strtoul(buf, NULL, 0);
+	}
+
+	if (d)
+		gpio_direction_output(n, 0);
+	else
+		gpio_direction_input(n);
+
+	return size;
+}
+
+static DEVICE_ATTR(direction, 0644, gpio_direction_show, gpio_direction_store);
+
+static ssize_t gpio_value_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+	int n = gdesc - gpio_desc;
+
+	if (test_bit(FLAG_IS_OUT, &gdesc->flags)) {
+		return -EINVAL;
+		/* strcpy(buf, "-1\n"); return 4; */ /* Or this? */
+	}
+	return sprintf(buf, "%d\n", gpio_get_value(n)) + 1;
+}
+
+static ssize_t gpio_value_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+	int v, n = gdesc - gpio_desc;
+
+	if (!test_bit(FLAG_IS_OUT, &gdesc->flags))
+		return -EINVAL;
+
+	v = simple_strtoul(buf, NULL, 0);
+	gpio_set_value(n, v);
+
+	return size;
+}
+
+static DEVICE_ATTR(value, 0644, gpio_value_show, gpio_value_store);
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t gpio_label_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+
+	if (!gdesc->label) {
+		strcpy(buf, "free\n");
+		return 6;
+	} else
+		return sprintf(buf, "%s\n", gdesc->label) + 1;
+}
+
+static DEVICE_ATTR(label, 0444, gpio_label_show, NULL);
+#endif
+
+static int gpiochip_classdev_register(const struct gpio_chip *chip)
+{
+	int ret, i;
+	struct gpio_desc *gdesc;
+
+	BUG_ON(!chip->dev);
+
+	for (i = chip->base; i < chip->base + chip->ngpio; i++) {
+		gdesc = gpio_desc + i;
+		gdesc->dev = device_create(gpio_class, chip->dev, 0, "%s:%d",
+					   chip->label, i);
+		if (IS_ERR(gdesc->dev)) {
+			ret = PTR_ERR(gdesc->dev);
+			i--;
+			goto fail;
+		}
+
+		dev_set_drvdata(gdesc->dev, gdesc);
+
+		ret = device_create_file(gdesc->dev, &dev_attr_direction);
+		if (ret)
+			goto fail_dev;
+		ret = device_create_file(gdesc->dev, &dev_attr_value);
+		if (ret)
+			goto fail_dir;
+#ifdef CONFIG_DEBUG_FS
+		ret = device_create_file(gdesc->dev, &dev_attr_label);
+		if (ret)
+			goto fail_value;
+#endif
+	}
+	return 0;
+
+fail:
+	for (; i >= chip->base; i--) {
+		gdesc = gpio_desc + i;
+
+#ifdef CONFIG_DEBUG_FS
+		device_remove_file(gdesc->dev, &dev_attr_label);
+
+fail_value:
+#endif
+		device_remove_file(gdesc->dev, &dev_attr_value);
+
+fail_dir:
+		device_remove_file(gdesc->dev, &dev_attr_direction);
+
+fail_dev:
+		device_unregister(gdesc->dev);
+		gdesc->dev = NULL;
+	}
+	return ret;
+}
+
+static int __init gpio_class_init(void)
+{
+	gpio_class = class_create(THIS_MODULE, "gpio");
+	if (IS_ERR(gpio_class))
+		return PTR_ERR(gpio_class);
+	return 0;
+}
+
+static void __exit gpio_class_exit(void)
+{
+	/* FIXME:  Code to remove all the sysfs devices and files created
+	 * should go here */
+	class_destroy(gpio_class);
+}
+subsys_initcall(gpio_class_init);
+module_exit(gpio_class_exit);
+
+#else /* no class */
+
+/* I coulda been a contender, I coulda had class... */
+static inline int gpiochip_classdev_register(const struct gpio_chip *chip)
+{ return 0; }
+
+#endif
+
+
 /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
  * when setting direction, and otherwise illegal.  Until board setup code
  * and drivers use explicit requests everywhere (which won't happen when
@@ -118,6 +290,22 @@ int gpiochip_add(struct gpio_chip *chip)
 	}
 
 	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	if (status)
+		goto fail;
+
+	if (chip->dev) {
+		/* can sleep, so can't call with the spinlock held */
+		status = gpiochip_classdev_register(chip);
+		if (status) {
+			/* De-allocate GPIOs */
+			spin_lock_irqsave(&gpio_lock, flags);
+			for (id = chip->base; id < chip->base + chip->ngpio; id++)
+				gpio_desc[id].chip = NULL;
+			spin_unlock_irqrestore(&gpio_lock, flags);
+		}
+	}
+
 fail:
 	/* failures here can mean systems won't boot... */
 	if (status)
include/asm-generic/gpio.h +2 -0
index f29a502..b2a5262 100644
@@ -29,6 +29,7 @@ struct seq_file;
  * @dbg_show: optional routine to show contents in debugfs; default code
  *	will be used when this is omitted, but custom code can show extra
  *	state (such as pullup/pulldown configuration).
+ * @dev: optional device for the GPIO chip.  Used to create sysfs files.
  * @base: identifies the first GPIO number handled by this chip; or, if
  *	negative during registration, requests dynamic ID allocation.
  * @ngpio: the number of GPIOs handled by this controller; the last GPIO
@@ -59,6 +60,7 @@ struct gpio_chip {
 						unsigned offset, int value);
 	void			(*dbg_show)(struct seq_file *s,
 						struct gpio_chip *chip);
+	struct device		*dev;
 	int			base;
 	u16			ngpio;
 	unsigned		can_sleep:1;
Signature · 1 line
1.5.4.1
#45 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Quoted 6 lines · Trent Piepho
Quoted 5 lines · David Brownell
Quoted 3 lines · Jean Delvare

So the user-space interface would be part of the generic GPIO infrastructure? I like the idea.

Quoted 6 lines
Quoted 3 lines

I thought that would make sense too! :) Someone would need to write the code though. Having such a mechanism would provide another "carrot" to migrate folk towards the gpiolib core.

Here's some code to do this. It's not entirely perfect yet, but it is usable.

I quite like the fact that this easily tracks labels but I like the interface of simple_gpio posted a few days back:
http://lkml.org/lkml/2008/3/26/87

Either way, anything unified is good.

	--Ben.
#46 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Quoted 11 lines · Ben Nizette
Quoted 10 lines · Trent Piepho
Quoted 8 lines · David Brownell
Quoted 3 lines · Jean Delvare

So the user-space interface would be part of the generic GPIO infrastructure? I like the idea.

I thought that would make sense too! :) Someone would need to write the code though. Having such a mechanism would provide another "carrot" to migrate folk towards the gpiolib core.

Quoted 8 lines
Quoted 2 lines

Here's some code to do this. It's not entirely perfect yet, but it is usable.

I quite like the fact that this easily tracks labels but I like the interface of simple_gpio posted a few days back:
http://lkml.org/lkml/2008/3/26/87

Either way, anything unified is good.

Always too slow posting my patches. I wrote this two months ago when there wasn't anything else.

A char device allows better permissions and could be more efficient, if one really wants to do extensive control of gpio lines from userspace. I can see how it might be preferrable in some instances.

The nice thing about sysfs is that you don't need any extra software to interact with it. It's very convienent when you're just trying to debug the gpio driver you're writing or verify that the gpio lines you just connected are doing things. It's also nice to be able to say something like:

# run these commands to un-write protect flash
echo out > /sys/class/gpio/MPC85XX:5/direction
echo 1 > /sys/class/gpio/MPC85XX:5/value

Instead of a complicated process that includes directions for creating the correct device file, compiling a program that will set gpio lines, downloading said program's source, and so on.

#47 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Quoted 2 lines · Trent Piepho

Always too slow posting my patches. I wrote this two months ago when there wasn't anything else.

I've got something similar myself; mine's more complex, handles and reports IRQs but isn't near releasable. Happens :-)

Quoted 5 lines

A char device allows better permissions and could be more efficient, if one really wants to do extensive control of gpio lines from userspace. I can see how it might be preferrable in some instances.

Quoted 8 lines

The nice thing about sysfs is that you don't need any extra software to interact with it. It's very convienent when you're just trying to debug the gpio driver you're writing or verify that the gpio lines you just connected are doing things. It's also nice to be able to say something like:

# run these commands to un-write protect flash
echo out > /sys/class/gpio/MPC85XX:5/direction
echo 1 > /sys/class/gpio/MPC85XX:5/value

simple_gpio allows
echo "O1" > /dev/gpioN
to do what you've got above, no extra points there ;-)

Quoted 3 lines

Instead of a complicated process that includes directions for creating the correct device file, compiling a program that will set gpio lines, downloading said program's source, and so on.

Creating the device file is largely mdev/udev's job, most people don't have to worry about it.

As I mentioned before (and in my review of simple_gpio) I really like the idea of having labels attached to the gpio numbers in some way; as it stands I see that as simple_gpio's only major weakness.

David, you're kinda the gatekeeper here; any input from you on which approach is to be preferred, essential features etc?

--Ben.

#48 lore raw

Re: userspace GPIO access (WAS: [patch/rfc 2/4] pcf875x ...)

Quoted 2 lines · Ben Nizette

David, you're kinda the gatekeeper here; any input from you on which approach is to be preferred, essential features etc?

I won't much care about /dev/... vs /sys/... though I'd probably have used sysfs myself (just because it's much simpler and doesn't need to imply mdev/udev and classes).

The configuration part of each driver bothers me:

- Mike's simple_gpio requires manual kernel config to set up the platform_device nodes ... and thus rules out the first usage scenarios I ever heard of for such a userspace mechanism.

- Trent's gpio_class exposes all GPIOs, even ones that are claimed by kernel drivers ... and thus makes it easy to clobber kernel driver state. (Plus it won't work on most built-in GPIOs, since they by and large don't have parent devices.)

What I'd like to see is userspace config commands to cause the gpio_request() ... *maybe* something like

    echo 42 foo 0 > .../gpio_config

	... causing error-checked versions of:

    gpio_request(42, "foo")
    gpio_direction_output(42, 0)

	... then some .../gpio42 file, read/write, appears

and

echo 84 bar in > .../gpio_config

... causing error-checked versions of:

gpio_request(84, "bar")
gpio_direction_input(84)

... then some .../gpio84 file, read-only, appears

Though arguably the label could just always be "userspace" (it's mostly for /sys/kernel/debug/gpio), and the default could be to configure as an input (unless an output value was given). Plus, there should be some way to cause gpio_free() too.

A potential advantage of the /dev/... node approach would be that it's easier to support an IRQ-backed poll() mechanism for inputs.

- Dave

#49 lore raw

Re: userspace GPIO access (WAS: [patch/rfc 2/4] pcf875x ...)

Quoted 4 lines · David Brownell

- Trent's gpio_class exposes all GPIOs, even ones
that are claimed by kernel drivers ... and thus
makes it easy to clobber kernel driver state.

This was intentional. When you're developing said kernel drivers, or connecting hardware they're supposed to drive, it's very handy to be able to set and read the GPIOs from userspace. At least, when writing a gpiolib driver and code that used gpiolib, I found this ability very useful, so I thought other developers might as well.

I suppose one could make the sys files read-only once a kernel driver allocates a gpio. But it would be nice to have the ability to make them writable, if one really wants that.

Quoted 2 lines

(Plus it won't work on most built-in GPIOs, since they by and large don't have parent devices.)

Couldn't they always add one? My GPIO driver is part of the CPU/SoC, and it has a device node. It's pretty easy to add a platform device, and probably cleaner than not associating a device with the gpio driver.

Quoted 1 line

From my understanding of sysfs, it seems any sysfs based approach has to

be based on a device.

Quoted 2 lines

What I'd like to see is userspace config commands to cause the gpio_request() ... *maybe* something like

Suppose I took the code I had, and make the label file writable? Writing to it allocates the gpio with the written label? That would be relatively simple to add. Is there any reason why the GPIOs should appear in sysfs by default? They are devices, and most other devices appear in sysfs.

Quoted 1 line

Plus, there should be some way to cause gpio_free() too.

Write a blank label? Too bad one can't "rm" sysfs files, that would be a neat way to trigger stuff. I can see it used to hot-unplug a pci device, just delete the slot.

#50 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

In reply to Trent Piepho

Hi Trent,

Quoted 41 lines · Trent Piepho
Quoted 8 lines · David Brownell
Quoted 3 lines · Jean Delvare

So the user-space interface would be part of the generic GPIO infrastructure? I like the idea.

I thought that would make sense too! :) Someone would need to write the code though. Having such a mechanism would provide another "carrot" to migrate folk towards the gpiolib core.

Here's some code to do this. It's not entirely perfect yet, but it is usable. I create a directory in sysfs (class/gpio/<name>:<num>) for each gpio line. The are files in this directory to allow reading/setting the gpio value and direction. One can also see the label associated with the gpio if gpiolib is keeping track of labels (i.e. debugfs is on). sysfs also creates a gpio directory under the device associated with the gpio lines (this is a new thing one needs to add) with that device's gpios. Actually, think these are "real" sysfs directories, and the ones in /sys/class are copies made by sysfs.

----------------------------------------------------------------------------
From c65d0fb239b79de7f595e47edb2fb641217e7309 Mon Sep 17 00:00:00 2001
From: Trent Piepho <[email protected]>
Date: Thu, 3 Apr 2008 18:37:23 -0700
Subject: GPIO: Create a sysfs gpio class for messing with GPIOs from userspace

struct gpio_chip gets a new field, dev, which points to the struct device of whatever the gpio lines are part of. If this is non-NULL, gpio class entries are created for this device when gpiochip_add() is called, by a new function gpiochip_classdev_register().

It creates a sysfs directory for each gpio the chip defines. Each device has three attributes so far, direction, value, and label. label is read-only, and will only be present if DEBUG_FS is on, as without DEBUG_FS the gpio layer doesn't keep track of any labels.

Maybe the value file should be changed to RO or WO depending on direction?

Setting the direction auto-allocates the gpio, which is not really what I wanted. Maybe I should call the chip set method directly?

Quoted 7 lines

There are almost certainly a bunch of races with gpio_desc access.

No code has been written yet to remove the devices from the class when the class is removed.

The GPIO_CLASS define/ifdef code should either be removed, or turned into a Kconfig variable that can be used turn this feature on and off.

Purely technical review (I can't say whether this interface is better than what has been proposed elsewhere or not):

Quoted 39 lines
---
  drivers/gpio/gpiolib.c     |  188 ++++++++++++++++++++++++++++++++++++++++++++
  include/asm-generic/gpio.h |    2 +
  2 files changed, 190 insertions(+), 0 deletions(-)
drivers/gpio/gpiolib.c +10 -0
index d8db2f8..db0677d 100644
@@ -18,6 +18,7 @@
   * only an instruction or two per bit.
   */
 
+#define GPIO_CLASS	1
 
  /* When debugging, extend minimal trust to callers and platform code.
   * Also emit diagnostic messages that may help initial bringup, when
@@ -47,6 +48,9 @@ struct gpio_desc {
  #ifdef CONFIG_DEBUG_FS
  	const char		*label;
  #endif
+#if GPIO_CLASS
+	struct device		*dev;
+#endif
  };
  static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
 
@@ -57,6 +61,174 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label)
  #endif
  }
 
+#if GPIO_CLASS
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/err.h>
+

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 79

Quoted 10 lines
drivers/gpio/gpiolib.c +10 -0
+static struct class *gpio_class;
+
+static ssize_t gpio_direction_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+
+	strcpy(buf, test_bit(FLAG_IS_OUT, &gdesc->flags) ? "out\n" : "in\n\0");
+
+	return 5;

Confusing construct... I suggest using sprintf instead, which will automatically return the correct number of bytes for you.

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 91

Quoted 12 lines
drivers/gpio/gpiolib.c +12 -0
+}
+
+static ssize_t gpio_direction_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+	int d, n = gdesc - gpio_desc;
+
+	if (size >= 3 && !strncmp(buf, "out", 3)) {
+		d = 1;
+	} else if (size >= 2 && !strncmp(buf, "in", 2)) {
+		d = 0;

As far as I know, the string you receive from sysfs is guaranteed to be NUL-terminated, so the size checks are not needed.

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 93

Quoted 2 lines
drivers/gpio/gpiolib.c +2 -0
+	} else {
+		d = simple_strtoul(buf, NULL, 0);

This exposes to user-space the so far internal-only decision to encode output as 1 and input as 0. I don't see much benefit in doing this, in particular when you don't check for errors so for example an input of "Out" would result in value 0 i.e. input mode. I'd rather support only "in" and "out" as valid values and return -EINVAL otherwise.

Quoted 11 lines
drivers/gpio/gpiolib.c +11 -0
+	}
+
+	if (d)
+		gpio_direction_output(n, 0);
+	else
+		gpio_direction_input(n);
+
+	return size;
+}
+
+static DEVICE_ATTR(direction, 0644, gpio_direction_show, gpio_direction_store);

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 114

Quoted 10 lines
drivers/gpio/gpiolib.c +10 -0
+
+static ssize_t gpio_value_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+	int n = gdesc - gpio_desc;
+
+	if (test_bit(FLAG_IS_OUT, &gdesc->flags)) {
+		return -EINVAL;
+		/* strcpy(buf, "-1\n"); return 4; */ /* Or this? */

Why not just return gpio_get_value(n) in all cases? User might want to know which value is currently being output.

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 116

Quoted 2 lines
drivers/gpio/gpiolib.c +2 -0
+	}
+	return sprintf(buf, "%d\n", gpio_get_value(n)) + 1;

Why "+ 1"? As far as I know you are not supposed to return a trailing NUL to user-space.

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 128

Quoted 12 lines
drivers/gpio/gpiolib.c +12 -0
+}
+
+static ssize_t gpio_value_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+	int v, n = gdesc - gpio_desc;
+
+	if (!test_bit(FLAG_IS_OUT, &gdesc->flags))
+		return -EINVAL;
+
+	v = simple_strtoul(buf, NULL, 0);

We have strict_strtoul() now.

Quoted 6 lines
drivers/gpio/gpiolib.c +6 -0
+	gpio_set_value(n, v);
+
+	return size;
+}
+
+static DEVICE_ATTR(value, 0644, gpio_value_show, gpio_value_store);

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 144

Quoted 10 lines
drivers/gpio/gpiolib.c +10 -0
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t gpio_label_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+
+	if (!gdesc->label) {
+		strcpy(buf, "free\n");
+		return 6;

Again sprintf would seem safer than a hard-coded length.

Quoted 63 lines
drivers/gpio/gpiolib.c +63 -0
+	} else
+		return sprintf(buf, "%s\n", gdesc->label) + 1;
+}
+
+static DEVICE_ATTR(label, 0444, gpio_label_show, NULL);
+#endif
+
+static int gpiochip_classdev_register(const struct gpio_chip *chip)
+{
+	int ret, i;
+	struct gpio_desc *gdesc;
+
+	BUG_ON(!chip->dev);
+
+	for (i = chip->base; i < chip->base + chip->ngpio; i++) {
+		gdesc = gpio_desc + i;
+		gdesc->dev = device_create(gpio_class, chip->dev, 0, "%s:%d",
+					   chip->label, i);
+		if (IS_ERR(gdesc->dev)) {
+			ret = PTR_ERR(gdesc->dev);
+			i--;
+			goto fail;
+		}
+
+		dev_set_drvdata(gdesc->dev, gdesc);
+
+		ret = device_create_file(gdesc->dev, &dev_attr_direction);
+		if (ret)
+			goto fail_dev;
+		ret = device_create_file(gdesc->dev, &dev_attr_value);
+		if (ret)
+			goto fail_dir;
+#ifdef CONFIG_DEBUG_FS
+		ret = device_create_file(gdesc->dev, &dev_attr_label);
+		if (ret)
+			goto fail_value;
+#endif
+	}
+	return 0;
+
+fail:
+	for (; i >= chip->base; i--) {
+		gdesc = gpio_desc + i;
+
+#ifdef CONFIG_DEBUG_FS
+		device_remove_file(gdesc->dev, &dev_attr_label);
+
+fail_value:
+#endif
+		device_remove_file(gdesc->dev, &dev_attr_value);
+
+fail_dir:
+		device_remove_file(gdesc->dev, &dev_attr_direction);
+
+fail_dev:
+		device_unregister(gdesc->dev);
+		gdesc->dev = NULL;
+	}
+	return ret;
+}
+
+static int __init gpio_class_init(void)
+{

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 217

Quoted 10 lines
drivers/gpio/gpiolib.c +10 -0
+	gpio_class = class_create(THIS_MODULE, "gpio");
+	if (IS_ERR(gpio_class))
+		return PTR_ERR(gpio_class);
+	return 0;
+}
+
+static void __exit gpio_class_exit(void)
+{
+	/* FIXME:  Code to remove all the sysfs devices and files created
+	 * should go here */

Oh yes it really should ;)

Quoted 18 lines
drivers/gpio/gpiolib.c +14 -0
+	class_destroy(gpio_class);
+}
+subsys_initcall(gpio_class_init);
+module_exit(gpio_class_exit);
+
+#else /* no class */
+
+/* I coulda been a contender, I coulda had class... */
+static inline int gpiochip_classdev_register(const struct gpio_chip *chip)
+{ return 0; }
+
+#endif
+
+
  /* Warn when drivers omit gpio_request() calls -- legal but ill-advised
   * when setting direction, and otherwise illegal.  Until board setup code
   * and drivers use explicit requests everywhere (which won't happen when

drivers/gpio/gpiolib.c int gpiochip_add(struct gpio_chip *chip) · line 298

Quoted 9 lines
drivers/gpio/gpiolib.c +6 -0
@@ -118,6 +290,22 @@ int gpiochip_add(struct gpio_chip *chip)
  	}
 
  	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	if (status)
+		goto fail;
+
+	if (chip->dev) {
+		/* can sleep, so can't call with the spinlock held */

You don't actually hold the spinlock at this point.

Quoted 18 lines
drivers/gpio/gpiolib.c +10 -0
+		status = gpiochip_classdev_register(chip);
+		if (status) {
+			/* De-allocate GPIOs */
+			spin_lock_irqsave(&gpio_lock, flags);
+			for (id = chip->base; id < chip->base + chip->ngpio; id++)
+				gpio_desc[id].chip = NULL;
+			spin_unlock_irqrestore(&gpio_lock, flags);
+		}
+	}
+
  fail:
  	/* failures here can mean systems won't boot... */
  	if (status)
include/asm-generic/gpio.h +0 -0
index f29a502..b2a5262 100644

include/asm-generic/gpio.h struct seq_file; · line 32

Quoted 4 lines
include/asm-generic/gpio.h +1 -0
@@ -29,6 +29,7 @@ struct seq_file;
   * @dbg_show: optional routine to show contents in debugfs; default code
   *	will be used when this is omitted, but custom code can show extra
   *	state (such as pullup/pulldown configuration).
+ * @dev: optional device for the GPIO chip.  Used to create sysfs files.

Broken indentation.

Quoted 11 lines
include/asm-generic/gpio.h +1 -0
   * @base: identifies the first GPIO number handled by this chip; or, if
   *	negative during registration, requests dynamic ID allocation.
   * @ngpio: the number of GPIOs handled by this controller; the last GPIO
@@ -59,6 +60,7 @@ struct gpio_chip {
  						unsigned offset, int value);
  	void			(*dbg_show)(struct seq_file *s,
  						struct gpio_chip *chip);
+	struct device		*dev;
  	int			base;
  	u16			ngpio;
  	unsigned		can_sleep:1;
Signature · 1 line
Jean Delvare
#51 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Quoted 20 lines · Jean Delvare
Quoted 10 lines · Trent Piepho

From c65d0fb239b79de7f595e47edb2fb641217e7309 Mon Sep 17 00:00:00 2001
From: Trent Piepho <[email protected]>
Date: Thu, 3 Apr 2008 18:37:23 -0700
Subject: GPIO: Create a sysfs gpio class for messing with GPIOs from userspace

struct gpio_chip gets a new field, dev, which points to the struct device of whatever the gpio lines are part of. If this is non-NULL, gpio class entries are created for this device when gpiochip_add() is called, by a new function gpiochip_classdev_register().

Quoted 5 lines

It creates a sysfs directory for each gpio the chip defines. Each device has three attributes so far, direction, value, and label. label is read-only, and will only be present if DEBUG_FS is on, as without DEBUG_FS the gpio layer doesn't keep track of any labels.

Purely technical review (I can't say whether this interface is better than what has been proposed elsewhere or not):

Quoted 6 lines

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 79

Quoted 3 lines
drivers/gpio/gpiolib.c +3 -0
+	strcpy(buf, test_bit(FLAG_IS_OUT, &gdesc->flags) ? "out\n" : "in\n\0");
+
+	return 5;

Confusing construct... I suggest using sprintf instead, which will automatically return the correct number of bytes for you.

But it's less efficient! Will nobody think of the wasted cycles?

Quoted 8 lines

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 93

Quoted 2 lines
drivers/gpio/gpiolib.c +2 -0
+	} else {
+		d = simple_strtoul(buf, NULL, 0);

This exposes to user-space the so far internal-only decision to encode output as 1 and input as 0. I don't see much benefit in doing this, in particular when you don't check for errors so for example an input of "Out" would result in value 0 i.e. input mode. I'd rather support only "in" and "out" as valid values and return -EINVAL otherwise.

I guess I wanted to support the interface of using out or 1 vs in or 0, since both seemed like the two most obvious ways of setting it. But direction uses in/out for output, so that's probably the most obvious single interface.

Quoted 9 lines

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 114

Quoted 6 lines
drivers/gpio/gpiolib.c +6 -0
+	const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+	int n = gdesc - gpio_desc;
+
+	if (test_bit(FLAG_IS_OUT, &gdesc->flags)) {
+		return -EINVAL;
+		/* strcpy(buf, "-1\n"); return 4; */ /* Or this? */

Why not just return gpio_get_value(n) in all cases? User might want to know which value is currently being output.

I thought the gpiolib layer didn't let you read an output, but I see that's not the case now. Maybe it's changed since the first revision? I've changed this to call gpio_get_value(n) for outputs too.

Though for the MPC8572 GPIO driver I wrote, it doesn't support reading outputs. The hardware doesn't allow it (IMHO, a design flaw), and working around this significantly slows down GPIO functions. What I'm trying to do with the GPIO lines is going to be slower than desired no matter how fast I make the gpio code (which is almost entirely responsible for the final speed), so slowing down reads by a factor of four or more just to read back outputs isn't desirable.

Quoted 6 lines

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 217

Quoted 4 lines
drivers/gpio/gpiolib.c +4 -0
+static void __exit gpio_class_exit(void)
+{
+	/* FIXME:  Code to remove all the sysfs devices and files created
+	 * should go here */

Oh yes it really should ;)

I know, but I'm not using modules for the system this is in, so it will never get called. What's the point of writing code I'll never use if this isn't useful for the kernel?

Quoted 12 lines

drivers/gpio/gpiolib.c int gpiochip_add(struct gpio_chip *chip) · line 298

Quoted 10 lines
drivers/gpio/gpiolib.c +6 -0
@@ -118,6 +290,22 @@ int gpiochip_add(struct gpio_chip *chip)
  	}
 
  	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	if (status)
+		goto fail;
+
+	if (chip->dev) {
+		/* can sleep, so can't call with the spinlock held */

You don't actually hold the spinlock at this point.

It would be easier to call gpiochip_classdev_register() earlier when the spin lock is held, but one can't because it could sleep. The comment was a warning to anyone who thought they could simplify the logic.

Quoted 4 lines

include/asm-generic/gpio.h struct seq_file; · line 32

Quoted 2 lines
include/asm-generic/gpio.h +1 -0
   *	state (such as pullup/pulldown configuration).
+ * @dev: optional device for the GPIO chip.  Used to create sysfs files.

Broken indentation.

Looks like mailer damage from something, it's correct in my patch file.

#52 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Quoted 10 lines · Trent Piepho
Quoted 7 lines · Jean Delvare

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 79

Quoted 3 lines · Trent Piepho
drivers/gpio/gpiolib.c +3 -0
+	strcpy(buf, test_bit(FLAG_IS_OUT, &gdesc->flags) ? "out\n" : "in\n\0");
+
+	return 5;

Confusing construct... I suggest using sprintf instead, which will automatically return the correct number of bytes for you.

But it's less efficient! Will nobody think of the wasted cycles?

Can you prove that it is actually less efficient, and if so, by how much? The time spent in this single function if probably insignificant in comparison to the whole chain from the user-space process to the GPIO chip.

Not that it really matters anyway, this is in no way a hot path so clarity and correctness definitely take over efficiency. And the code above is actually incorrect: as I mentioned elsewhere in this thread, you aren't support to include trailing \0s in the buffer you pass back to sysfs. Not all programming languages use \0 for string termination.

Quoted 11 lines

(...)

Quoted 6 lines

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 217

Quoted 4 lines
drivers/gpio/gpiolib.c +4 -0
+static void __exit gpio_class_exit(void)
+{
+	/* FIXME:  Code to remove all the sysfs devices and files created
+	 * should go here */

Oh yes it really should ;)

I know, but I'm not using modules for the system this is in, so it will never get called. What's the point of writing code I'll never use if this isn't useful for the kernel?

Because most certainly your code won't be accepted upstream until this is fixed, and presumably you posted this patch in the hope that it would go upstream ;) Just because it isn't useful to you doesn't mean it won't be useful to others. Otherwise this particular piece of code couldn't be built as a module at all.

Quoted 14 lines

(...)

Quoted 12 lines

drivers/gpio/gpiolib.c int gpiochip_add(struct gpio_chip *chip) · line 298

Quoted 10 lines
drivers/gpio/gpiolib.c +6 -0
@@ -118,6 +290,22 @@ int gpiochip_add(struct gpio_chip *chip)
  	}
 
  	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	if (status)
+		goto fail;
+
+	if (chip->dev) {
+		/* can sleep, so can't call with the spinlock held */

You don't actually hold the spinlock at this point.

Quoted 3 lines

It would be easier to call gpiochip_classdev_register() earlier when the spin lock is held, but one can't because it could sleep. The comment was a warning to anyone who thought they could simplify the logic.

Oops, sorry. I totally missed the "can't" in the comment.

Signature · 1 line
Jean Delvare
#53 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

Quoted 10 lines · Jean Delvare
Quoted 9 lines · Trent Piepho
Quoted 7 lines · Jean Delvare

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 79

Quoted 3 lines · Trent Piepho
drivers/gpio/gpiolib.c +3 -0
+	strcpy(buf, test_bit(FLAG_IS_OUT, &gdesc->flags) ? "out\n" : "in\n\0");
+
+	return 5;

Confusing construct... I suggest using sprintf instead, which will automatically return the correct number of bytes for you.

Quoted 4 lines
Quoted 1 line

But it's less efficient! Will nobody think of the wasted cycles?

Can you prove that it is actually less efficient, and if so, by how much? The time spent in this single function if probably insignificant

I think sprintf will parse the format string, and then end up calling the same strcpy() call to handle a %s. Since sprintf() contains the strcpy(), it has to be slower than strcpy alone.

But I should have put a :) in there, as I changed this code.

Quoted 3 lines

thread, you aren't support to include trailing \0s in the buffer you pass back to sysfs. Not all programming languages use \0 for string termination.

I fixed all those. I wasn't clear on that when I wrote this code and forgot I had done it incorrectly.

Quoted 9 lines
Quoted 8 lines
Quoted 4 lines

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 217

Quoted 2 lines
drivers/gpio/gpiolib.c +2 -0
+	/* FIXME:  Code to remove all the sysfs devices and files created
+	 * should go here */

Oh yes it really should ;)

I know, but I'm not using modules for the system this is in, so it will never get called. What's the point of writing code I'll never use if this isn't useful for the kernel?

Quoted 5 lines

Because most certainly your code won't be accepted upstream until this is fixed, and presumably you posted this patch in the hope that it would go upstream ;) Just because it isn't useful to you doesn't mean it won't be useful to others. Otherwise this particular piece of code couldn't be built as a module at all.

I guess I was waiting for a "this could go upstream if you fix this" or "this won't go upstream even if you fix it" so I don't waste time writing code no one is interested in.

#54 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

In reply to Trent Piepho
Quoted 5 lines · Trent Piepho
Quoted 5 lines
Quoted 3 lines

+    if (test_bit(FLAG_IS_OUT, &gdesc->flags)) {
+            return -EINVAL;
+            /* strcpy(buf, "-1\n"); return 4; */ /* Or this? */

Why not just return gpio_get_value(n) in all cases?

That's the right answer...

Quoted 6 lines
Quoted 2 lines

User might want to
know which value is currently being output.

I thought the gpiolib layer didn't let you read an output, but I see that's not the case now.  Maybe it's changed since the first revision?  I've changed this to call gpio_get_value(n) for outputs too.

I don't recall ever disallowing reading output values ... the exact behavior is platform-specific, though it "should" be the value actually sensed at the pin, which might not be the same as what's being driven.

Quoted 3 lines

Though for the MPC8572 GPIO driver I wrote, it doesn't support reading outputs.  The hardware doesn't allow it (IMHO, a design flaw), and working around this significantly slows down GPIO functions.

Then don't worry about it. Any portable code can't rely on being able to do that.

- Dave

Quoted 6 lines

What I'm trying to do
with the GPIO lines is going to be slower than desired no matter how fast I make the gpio code (which is almost entirely responsible for the final speed), so slowing down reads by a factor of four or more just to read back outputs isn't desirable.

#55 lore raw

Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver

In reply to Jean Delvare
Quoted 14 lines · Jean Delvare

drivers/gpio/gpiolib.c static inline void desc_set_label(struct gpio_desc *d, const char *label) · line 91

Quoted 10 lines
drivers/gpio/gpiolib.c +10 -0
+static ssize_t gpio_direction_store(struct device *dev,
+             struct device_attribute *attr, const char *buf, size_t size)
+{
+     const struct gpio_desc *gdesc = dev_get_drvdata(dev);
+     int d, n = gdesc - gpio_desc;
+
+     if (size >= 3 && !strncmp(buf, "out", 3)) {
+             d = 1;
+     } else if (size >= 2 && !strncmp(buf, "in", 2)) {
+             d = 0;

As far as I know, the string you receive from sysfs is guaranteed to be NUL-terminated, so the size checks are not needed.

Quoted 8 lines
Quoted 2 lines

+     } else {
+             d = simple_strtoul(buf, NULL, 0);

This exposes to user-space the so far internal-only decision to encode output as 1 and input as 0. I don't see much benefit in doing this, in particular when you don't check for errors so for example an input of "Out" would result in value 0 i.e. input mode. I'd rather support only "in" and "out" as valid values and return -EINVAL otherwise.

So, after trimming trailing whitespace:

	if (strcmp(buf, "out") == 0)
		gpio_direction_output(...)
	else if (strcmp(buf, "in") == 0)
		gpio_direction_input(...)

Yes, that'd be a lot better.

Quoted 10 lines
Quoted 10 lines

+     }

drivers/gpio/gpiolib.c +8 -0
+
+     if (d)
+             gpio_direction_output(n, 0);
+     else
+             gpio_direction_input(n);
+
+     return size;
+}
#56 lore raw

Re: [patch/rfc 2/4] pcf857x I2C GPIO expander driver

In reply to David Brownell
Quoted 3 lines · David Brownell

Thanks for the review.  I'll snip out typos and similar trivial comments (and fix them!), using responses here for more the substantive feedback.

Here's the current version of this patch ... updated to put the driver into drivers/gpio (separate patch setting that up) and the header into <linux/i2c/pcf857x.h>

Note that after looking at the GPIO expanders listed at the NXP website, I updated this to accept a few more of these chips. Other than reset pins and addressing options, the key difference between these seems to be the top I2C clock speed supported:

pcf857x ... 100 KHz
pca857x ... 400 KHz
pca967x ... 1000 KHz

Otherwise they're equivalent at the level of just swapping parts.

- Dave

============= SNIP!
This is a new-style I2C driver for most common 8 and 16 bit I2C based "quasi-bidirectional" GPIO expanders: pcf8574 or pcf8575, and several compatible models (mostly faster, supporting I2C at up to 1 MHz).

Since it's a new-style driver, these devices must be configured as part of board-specific init. That eliminates the need for error-prone manual configuration of module parameters, and makes compatibility with legacy drivers (pcf8574.c, pc8575.c)for these chips easier.

The driver exposes the GPIO signals using the platform-neutral GPIO programming interface, so they are easily accessed by other kernel code. The lack of such a flexible kernel API is what has ensured the proliferation of board-specific drivers for these chips... stuff that rarely makes it upstream since it's so ugly. This driver will let them use standard calls.

---
 drivers/gpio/Kconfig        |   23 +++
 drivers/gpio/Makefile       |    2 
 drivers/gpio/pcf857x.c      |  331 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c/pcf857x.h |   45 +++++
 4 files changed, 401 insertions(+)
drivers/gpio/Kconfig +23 -0
@@ -5,4 +5,27 @@
 menu "GPIO Support"
 	depends on GPIO_LIB
 
+config GPIO_PCF857X
+	tristate "PCF857x, PCA857x, and PCA967x I2C GPIO expanders"
+	depends on I2C
+	help
+	  Say yes here to provide access to most "quasi-bidirectional" I2C
+	  GPIO expanders used for additional digital outputs or inputs.
+	  Most of these parts are from NXP, though TI is a second source for
+	  some of them.  Compatible models include:
+
+	  8 bits:   pcf8574, pcf8574a, pca8574, pca8574a,
+	            pca9670, pca9672, pca9674, pca9674a
+
+	  16 bits:  pcf8575, pcf8575c, pca8575,
+	            pca9671, pca9673, pca9675
+
+	  Your board setup code will need to declare the expanders in
+	  use, and assign numbers to the GPIOs they expose.  Those GPIOs
+	  can then be used from drivers and other kernel code, just like
+	  other GPIOs, but only accessible from task contexts.
+
+	  This driver provides an in-kernel interface to those GPIOs using
+	  platform-neutral GPIO calls.
+
 endmenu
drivers/gpio/Makefile +2 -0
@@ -1 +1,3 @@
 # gpio support: dedicated expander chips, etc
+
+obj-$(CONFIG_GPIO_PCF857X)	+= pcf857x.o
drivers/gpio/pcf857x.c new file +331 -0

331 lines not on this page · raw message

include/linux/i2c/pcf857x.h new file +45 -0

45 lines not on this page · raw message

#57 lore raw

Re: [patch/rfc 2/4] pcf857x I2C GPIO expander driver

Hi David,

Quoted 27 lines · David Brownell
Quoted 3 lines · David Brownell

Thanks for the review.  I'll snip out typos and similar trivial comments (and fix them!), using responses here for more the substantive feedback.

Here's the current version of this patch ... updated to put the driver into drivers/gpio (separate patch setting that up) and the header into <linux/i2c/pcf857x.h>

Note that after looking at the GPIO expanders listed at the NXP website, I updated this to accept a few more of these chips. Other than reset pins and addressing options, the key difference between these seems to be the top I2C clock speed supported:

pcf857x ... 100 KHz
pca857x ... 400 KHz
pca967x ... 1000 KHz

Otherwise they're equivalent at the level of just swapping parts.

- Dave

============= SNIP!
This is a new-style I2C driver for most common 8 and 16 bit I2C based "quasi-bidirectional" GPIO expanders: pcf8574 or pcf8575, and several compatible models (mostly faster, supporting I2C at up to 1 MHz).

Quoted 4 lines

Since it's a new-style driver, these devices must be configured as part of board-specific init. That eliminates the need for error-prone manual configuration of module parameters, and makes compatibility with legacy drivers (pcf8574.c, pc8575.c)for these chips easier.

Missing space after closing parenthesis. Also, I don't quite see what is supposed to make compatibility with the legacy drivers easier, nor how, not why it matters in the first place.

Quoted 82 lines

The driver exposes the GPIO signals using the platform-neutral GPIO programming interface, so they are easily accessed by other kernel code. The lack of such a flexible kernel API is what has ensured the proliferation of board-specific drivers for these chips... stuff that rarely makes it upstream since it's so ugly. This driver will let them use standard calls.

---
 drivers/gpio/Kconfig        |   23 +++
 drivers/gpio/Makefile       |    2 
 drivers/gpio/pcf857x.c      |  331 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c/pcf857x.h |   45 +++++
 4 files changed, 401 insertions(+)
drivers/gpio/Kconfig +23 -0
@@ -5,4 +5,27 @@
 menu "GPIO Support"
 	depends on GPIO_LIB
 
+config GPIO_PCF857X
+	tristate "PCF857x, PCA857x, and PCA967x I2C GPIO expanders"
+	depends on I2C
+	help
+	  Say yes here to provide access to most "quasi-bidirectional" I2C
+	  GPIO expanders used for additional digital outputs or inputs.
+	  Most of these parts are from NXP, though TI is a second source for
+	  some of them.  Compatible models include:
+
+	  8 bits:   pcf8574, pcf8574a, pca8574, pca8574a,
+	            pca9670, pca9672, pca9674, pca9674a
+
+	  16 bits:  pcf8575, pcf8575c, pca8575,
+	            pca9671, pca9673, pca9675
+
+	  Your board setup code will need to declare the expanders in
+	  use, and assign numbers to the GPIOs they expose.  Those GPIOs
+	  can then be used from drivers and other kernel code, just like
+	  other GPIOs, but only accessible from task contexts.
+
+	  This driver provides an in-kernel interface to those GPIOs using
+	  platform-neutral GPIO calls.
+
 endmenu
drivers/gpio/Makefile +2 -0
@@ -1 +1,3 @@
 # gpio support: dedicated expander chips, etc
+
+obj-$(CONFIG_GPIO_PCF857X)	+= pcf857x.o
drivers/gpio/pcf857x.c new file +27 -0
@@ -0,0 +1,331 @@
+/*
+ * pcf857x - driver for pcf857x, pca857x, and pca967x I2C GPIO expanders
+ *
+ * Copyright (C) 2007 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c/pcf857x.h>
+
+#include <asm/gpio.h>
+

drivers/gpio/pcf857x.c line 37

Quoted 10 lines
drivers/gpio/pcf857x.c new file +10 -0
+
+/*
+ * The pcf857x, pca857x, and pca967x chips only expose one read and one
+ * write register.  Writing a "one" bit (to match the reset state) lets
+ * that pin be used as an input; it's not an open-drain model, but acts
+ * a bit like one.  This is described as "quasi-bidirectional"; read the
+ * chip documentation for details.
+ *
+ * Some other I2C GPIO expander chips (like the pca953{4,5,6,7,9}, pca9555,
+ * pca9698, mcp23008, and mc23017) have more complex register models with

mc_p_23017?

Quoted 17 lines
drivers/gpio/pcf857x.c new file +17 -0
+ * more conventional input circuitry, often using 0x20..0x27 addresses.
+ */
+struct pcf857x {
+	struct gpio_chip	chip;
+	struct i2c_client	*client;
+	unsigned		out;		/* software latch */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* Talk to 8-bit I/O expander */
+
+static int pcf857x_input8(struct gpio_chip *chip, unsigned offset)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+
+	gpio->out |= (1 << offset);

drivers/gpio/pcf857x.c line 64

Quoted 10 lines
drivers/gpio/pcf857x.c new file +10 -0
+	return i2c_smbus_write_byte(gpio->client, gpio->out);
+}
+
+static int pcf857x_get8(struct gpio_chip *chip, unsigned offset)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	s32		value;
+
+	value = i2c_smbus_read_byte(gpio->client);
+	return (value < 0) ? 0 : (value & (1 << offset));

This is no longer a boolean value, is that OK? I guess that it doesn't matter but maybe it should be documented (what GPIO drivers are allowed to return in these callback functions.)

Quoted 16 lines
drivers/gpio/pcf857x.c new file +16 -0
+}
+
+static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	unsigned	bit = 1 << offset;
+
+	if (value)
+		gpio->out |= bit;
+	else
+		gpio->out &= ~bit;
+	return i2c_smbus_write_byte(gpio->client, gpio->out);
+}
+
+static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
+{

drivers/gpio/pcf857x.c line 90

Quoted 10 lines
drivers/gpio/pcf857x.c new file +10 -0
+	pcf857x_output8(chip, offset, value);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* Talk to 16-bit I/O expander */
+
+static int i2c_write_le16(struct i2c_client *client, u16 word)
+{
+	u8 buf[2] = { word & 0xff, word >> 8, };

Stray comma.

Quoted 71 lines
drivers/gpio/pcf857x.c new file +71 -0
+	int status;
+
+	status = i2c_master_send(client, buf, 2);
+	return (status < 0) ? status : 0;
+}
+
+static int i2c_read_le16(struct i2c_client *client)
+{
+	u8 buf[2];
+	int status;
+
+	status = i2c_master_recv(client, buf, 2);
+	if (status < 0)
+		return status;
+	return (buf[1] << 8) | buf[0];
+}
+
+static int pcf857x_input16(struct gpio_chip *chip, unsigned offset)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+
+	gpio->out |= (1 << offset);
+	return i2c_write_le16(gpio->client, gpio->out);
+}
+
+static int pcf857x_get16(struct gpio_chip *chip, unsigned offset)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		value;
+
+	value = i2c_read_le16(gpio->client);
+	return (value < 0) ? 0 : (value & (1 << offset));
+}
+
+static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	unsigned	bit = 1 << offset;
+
+	if (value)
+		gpio->out |= bit;
+	else
+		gpio->out &= ~bit;
+	return i2c_write_le16(gpio->client, gpio->out);
+}
+
+static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value)
+{
+	pcf857x_output16(chip, offset, value);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pcf857x_probe(struct i2c_client *client)
+{
+	struct pcf857x_platform_data	*pdata;
+	struct pcf857x			*gpio;
+	int				status;
+
+	pdata = client->dev.platform_data;
+	if (!pdata)
+		return -ENODEV;
+
+	/* Allocate, initialize, and register this gpio_chip. */
+	gpio = kzalloc(sizeof *gpio, GFP_KERNEL);
+	if (!gpio)
+		return -ENOMEM;
+
+	gpio->chip.base = pdata->gpio_base;
+	gpio->chip.can_sleep = 1;
+

drivers/gpio/pcf857x.c line 171

Quoted 10 lines
drivers/gpio/pcf857x.c new file +10 -0
+	/* NOTE:  the OnSemi jlc1562b is also largely compatible with
+	 * these parts, notably for output.  It has a low-resolution
+	 * DAC instead of pin change IRQs; and its inputs can be the
+	 * result of comparators.
+	 */
+
+	/* 8574 addresses are 0x20..0x27; 8574a uses 0x38..0x3f;
+	 * 9670, 9672, 9764, and 9764a use quite a variety.
+	 *
+	 * NOTE: we dont distinguish here between *4 and *4a parts.

Typo: don't.

Quoted 90 lines
drivers/gpio/pcf857x.c new file +90 -0
+	 */
+	if (strcmp(client->name, "pcf8574") == 0
+			|| strcmp(client->name, "pca8574") == 0
+			|| strcmp(client->name, "pca9670") == 0
+			|| strcmp(client->name, "pca9672") == 0
+			|| strcmp(client->name, "pca9674") == 0
+			) {
+		gpio->chip.ngpio = 8;
+		gpio->chip.direction_input = pcf857x_input8;
+		gpio->chip.get = pcf857x_get8;
+		gpio->chip.direction_output = pcf857x_output8;
+		gpio->chip.set = pcf857x_set8;
+
+		if (!i2c_check_functionality(client->adapter,
+				I2C_FUNC_SMBUS_BYTE))
+			status = -EIO;
+
+		/* fail if there's no chip present */
+		else
+			status = i2c_smbus_read_byte(client);
+
+	/* '75/'75c addresses are 0x20..0x27, just like the '74;
+	 * the '75c doesn't have a current source pulling high.
+	 * 9671, 9673, and 9765 use quite a variety of addresses.
+	 *
+	 * NOTE: we dont distinguish here between 8575/8575a parts.
+	 */
+	} else if (strcmp(client->name, "pcf8575") == 0
+			|| strcmp(client->name, "pca8575") == 0
+			|| strcmp(client->name, "pca9671") == 0
+			|| strcmp(client->name, "pca9673") == 0
+			|| strcmp(client->name, "pca9675") == 0
+			) {
+		gpio->chip.ngpio = 16;
+		gpio->chip.direction_input = pcf857x_input16;
+		gpio->chip.get = pcf857x_get16;
+		gpio->chip.direction_output = pcf857x_output16;
+		gpio->chip.set = pcf857x_set16;
+
+		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+			status = -EIO;
+
+		/* fail if there's no chip present */
+		else
+			status = i2c_read_le16(client);
+
+	} else
+		status = -ENODEV;
+
+	if (status < 0)
+		goto fail;
+
+	gpio->chip.label = client->name;
+
+	gpio->client = client;
+	i2c_set_clientdata(client, gpio);
+
+	/* NOTE:  these chips have strange "quasi-bidirectional" I/O pins.
+	 * We can't actually know whether a pin is configured (a) as output
+	 * and driving the signal low, or (b) as input and reporting a low
+	 * value ... without knowing the last value written since the chip
+	 * came out of reset (if any).  We can't read the latched output.
+	 *
+	 * In short, the only reliable solution for setting up pin direction
+	 * is to do it explicitly.  The setup() method can do that.
+	 *
+	 * We use pdata->n_latch to avoid trouble.  In the typical case it's
+	 * left initialized to zero; our software copy of the "latch" then
+	 * matches the chip's all-ones reset state.  But some systems will
+	 * need to drive some pins low, while avoiding transient glitches.
+	 * Handle those cases by assigning n_latch to a nonzero value.
+	 */
+	gpio->out = ~pdata->n_latch;
+
+	status = gpiochip_add(&gpio->chip);
+	if (status < 0)
+		goto fail;
+
+	/* NOTE: these chips can issue "some pin-changed" IRQs, which we
+	 * don't yet even try to use.  Among other issues, the relevant
+	 * genirq state isn't available to modular drivers; and most irq
+	 * methods can't be called from sleeping contexts.
+	 */
+
+	dev_info(&client->dev, "gpios %d..%d on a %s%s\n",
+			gpio->chip.base,
+			gpio->chip.base + gpio->chip.ngpio - 1,
+			client->name,
+			client->irq ? " (irq ignored)" : "");
+

drivers/gpio/pcf857x.c line 271

Quoted 10 lines
drivers/gpio/pcf857x.c new file +10 -0
+	/* Let platform code set up the GPIOs and their users.
+	 * Now is the first time anyone can use them.
+	 */
+	if (pdata->setup) {
+		status = pdata->setup(client,
+				gpio->chip.base, gpio->chip.ngpio,
+				pdata->context);
+		if (status < 0)
+			dev_err(&client->dev, "%s --> %d\n",
+					"setup", status);

Shouldn't this be degraded to dev_warn? The probe still succeeds. Or keep dev_err but make the probe fail (in which case you'll probably want to swap this block of code with the dev_info above.)

Quoted 98 lines
drivers/gpio/pcf857x.c new file +60 -0
+	}
+
+	return 0;
+
+fail:
+	dev_dbg(&client->dev, "probe error %d for '%s'\n",
+			status, client->name);
+	kfree(gpio);
+	return status;
+}
+
+static int pcf857x_remove(struct i2c_client *client)
+{
+	struct pcf857x_platform_data	*pdata = client->dev.platform_data;
+	struct pcf857x			*gpio = i2c_get_clientdata(client);
+	int				status = 0;
+
+	if (pdata->teardown) {
+		status = pdata->teardown(client,
+				gpio->chip.base, gpio->chip.ngpio,
+				pdata->context);
+		if (status < 0) {
+			dev_err(&client->dev, "%s --> %d\n",
+					"teardown", status);
+			return status;
+		}
+	}
+
+	status = gpiochip_remove(&gpio->chip);
+	if (status == 0)
+		kfree(gpio);
+	else
+		dev_err(&client->dev, "%s --> %d\n", "remove", status);
+	return status;
+}
+
+static struct i2c_driver pcf857x_driver = {
+	.driver = {
+		.name	= "pcf857x",
+		.owner	= THIS_MODULE,
+	},
+	.probe	= pcf857x_probe,
+	.remove	= pcf857x_remove,
+};
+
+static int __init pcf857x_init(void)
+{
+	return i2c_add_driver(&pcf857x_driver);
+}
+/* we want GPIOs to be ready at device_initcall() time */
+subsys_initcall(pcf857x_init);
+
+static void __exit pcf857x_exit(void)
+{
+	i2c_del_driver(&pcf857x_driver);
+}
+module_exit(pcf857x_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
include/linux/i2c/pcf857x.h new file +35 -0
@@ -0,0 +1,45 @@
+#ifndef __LINUX_PCF857X_H
+#define __LINUX_PCF857X_H
+
+/**
+ * struct pcf857x_platform_data - data to set up pcf857x driver
+ * @gpio_base: number of the chip's first GPIO
+ * @n_latch: optional bit-inverse of initial register value; if
+ *	you leave this initialized to zero the driver will act
+ *	like the chip was just reset
+ * @setup: optional callback issued once the GPIOs are valid
+ * @teardown: optional callback issued before the GPIOs are invalidated
+ * @context: optional parameter passed to setup() and teardown()
+ *
+ * In addition to the I2C_BOARD_INFO() state appropriate to each chip,
+ * the i2c_board_info used with the pcf875x driver must provide the
+ * chip "type" ("pcf8574", "pcf8574a", "pcf8575", "pcf8575c") and its
+ * platform_data (pointer to one of these structures) with at least
+ * the gpio_base value initialized.
+ *
+ * The @setup callback may be used with the kind of board-specific glue
+ * which hands the (now-valid) GPIOs to other drivers, or which puts
+ * devices in their initial states using these GPIOs.
+ *
+ * These GPIO chips are only "quasi-bidirectional"; read the chip specs
+ * to understand the behavior.  They don't have separate registers to
+ * record which pins are used for input or output, record which output
+ * values are driven, or provide access to input values.  That must be
+ * inferred by reading the chip's value and knowing the last value written
+ * to it.  If you leave n_latch initialized to zero, that last written
+ * value is presumed to be all ones (as if the chip were just reset).
+ */
+struct pcf857x_platform_data {
+	unsigned	gpio_base;
+	unsigned	n_latch;
+

include/linux/i2c/pcf857x.h line 45

Quoted 10 lines
include/linux/i2c/pcf857x.h new file +10 -0
+	int		(*setup)(struct i2c_client *client,
+					int gpio, unsigned ngpio,
+					void *context);
+	int		(*teardown)(struct i2c_client *client,
+					int gpio, unsigned ngpio,
+					void *context);
+	void		*context;
+};
+
+#endif /* __LINUX_PCF857X_H */

The rest looks fine to me.

Signature · 1 line
Jean Delvare
#58 lore raw

Re: [patch/rfc 2/4] pcf857x I2C GPIO expander driver

Quoted 3 lines · Jean Delvare

Also, I don't quite see what
is supposed to make compatibility with the legacy drivers easier, nor how, not why it matters in the first place.

There's a clear either/or disjunction. No fuzzy/confusing middle ground.

Quoted 11 lines

drivers/gpio/pcf857x.c line 64

Quoted 7 lines
drivers/gpio/pcf857x.c new file +7 -0
+static int pcf857x_get8(struct gpio_chip *chip, unsigned offset)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	s32		value;
+
+	value = i2c_smbus_read_byte(gpio->client);
+	return (value < 0) ? 0 : (value & (1 << offset));

This is no longer a boolean value, is that OK? I guess that it doesn't matter but maybe it should be documented (what GPIO drivers are allowed to return in these callback functions.)

Already documented -- as zero/nonzero, the original boolean model for C. Anything else would be at least tristate, not boolean. :)

Quoted 6 lines
Quoted 6 lines
drivers/gpio/pcf857x.c new file +6 -0
+	/* Let platform code set up the GPIOs and their users.
+	 * Now is the first time anyone can use them.
+	 */
+	if (pdata->setup) {
+		status = pdata->setup(client,
+				gpio->chip.base, gpio->chip.ngpio,
Quoted 8 lines

drivers/gpio/pcf857x.c line 271

Quoted 4 lines
drivers/gpio/pcf857x.c new file +4 -0
+				pdata->context);
+		if (status < 0)
+			dev_err(&client->dev, "%s --> %d\n",
+					"setup", status);

Shouldn't this be degraded to dev_warn? The probe still succeeds. Or keep dev_err but make the probe fail (in which case you'll probably want to swap this block of code with the dev_info above.)

Good point.

Quoted 1 line

The rest looks fine to me.

Thanks for the comments. I'll send this in with the next batch of gpiolib patches.

- Dave

#59 lore raw

PATCH 3/4 DaVinci platform uses new GPIOLIB

In reply to a message not in the archive

Switch DaVinci over to using the new GPIO library, so it can access GPIO expanders and other non-SOC GPIOs using the same calls.
---
This is an example conversion, against the current DaVinci tree (it probably won't apply against mainline). The header changes mostly by what it wraps, and allowing a broader range of GPIOs. The implementation code is a it simpler, since it no longer needs to map GPIO numbers to register banks by itself. It's fairly representative of what most platform conversions would look like, except that many platforms won't actually inline access to any of the SOC-native GPIO signals.

 arch/arm/Kconfig                    |    2 
 arch/arm/mach-davinci/gpio.c        |  137 +++++++++++++++++++-----------------
 include/asm-arm/arch-davinci/gpio.h |   63 +++++++---------
 3 files changed, 105 insertions(+), 97 deletions(-)
arch/arm/Kconfig +2 -0
@@ -396,9 +396,11 @@ config ARCH_DAVINCI
 	select GENERIC_TIME
 	select GENERIC_CLOCKEVENTS
 	select GENERIC_GPIO
+	select GPIO_LIB
 	help
 	  Support for TI's DaVinci platform.
 
+
 config ARCH_OMAP
 	bool "TI OMAP"
 	select GENERIC_GPIO
arch/arm/mach-davinci/gpio.c +74 -63

189 lines not on this page · raw message

include/asm-arm/arch-davinci/gpio.h +29 -34

121 lines not on this page · raw message

#60 lore raw

PATCH 4/4 DaVinci EVM uses pcf857x GPIO driver

In reply to a message not in the archive

Declare two of the I2C GPIO expanders to the EVM board init logic, and use them. One hooks up to the LEDs using the leds-gpio driver; the other exposes a switch to sysfs, and initializes the audio clocks.

Oh, and get rid of bogus warning about IDE conflicting with NOR unless both are actually configured.

---
This is against the DaVinci tree, but this patch will apply to mainline.

 arch/arm/mach-davinci/board-evm.c |  192 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 192 insertions(+)
arch/arm/mach-davinci/board-evm.c +192 -0
@@ -13,6 +13,10 @@
 #include <linux/init.h>
 #include <linux/dma-mapping.h>
 #include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/pcf857x.h>
+#include <linux/leds.h>
+
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/physmap.h>
@@ -21,6 +25,7 @@
 #include <asm/io.h>
 #include <asm/mach-types.h>
 #include <asm/hardware.h>
+#include <asm/gpio.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -167,6 +172,189 @@ static struct platform_device rtc_dev = 
 	.id             = -1,
 };
 
+/*----------------------------------------------------------------------*/
+
+/*
+ * I2C GPIO expanders
+ */
+
+#define PCF_Uxx_BASE(x)	((3 + (x)) * ARCH_GPIOS_PER_CHIP)
+
+
+/* U2 -- LEDs */
+
+static struct gpio_led evm_leds[] = {
+	{ .name = "DS8", .active_low = 1,
+		.default_trigger = "heartbeat", },
+	{ .name = "DS7", .active_low = 1, },
+	{ .name = "DS6", .active_low = 1, },
+	{ .name = "DS5", .active_low = 1, },
+	{ .name = "DS4", .active_low = 1, },
+	{ .name = "DS3", .active_low = 1, },
+	{ .name = "DS2", .active_low = 1,
+		.default_trigger = "mmc0", },
+	{ .name = "DS1", .active_low = 1,
+		.default_trigger = "ide-disk", },
+};
+
+static const struct gpio_led_platform_data evm_led_data = {
+	.num_leds	= ARRAY_SIZE(evm_leds),
+	.leds		= evm_leds,
+};
+
+static struct platform_device *evm_led_dev;
+
+static int
+evm_led_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *c)
+{
+	struct gpio_led *leds = evm_leds;
+	int status;
+
+	while (ngpio--) {
+		leds->gpio = gpio++;
+		leds++;
+	}
+
+	/* what an extremely annoying way to be forced to handle
+	 * device unregistration ...
+	 */
+	evm_led_dev = platform_device_alloc("leds-gpio", 0);
+	platform_device_add_data(evm_led_dev,
+			&evm_led_data, sizeof evm_led_data);
+
+	evm_led_dev->dev.parent = &client->dev;
+	status = platform_device_add(evm_led_dev);
+	if (status < 0) {
+		platform_device_put(evm_led_dev);
+		evm_led_dev = NULL;
+	}
+	return status;
+}
+
+static int
+evm_led_teardown(struct i2c_client *client, int gpio, unsigned ngpio, void *c)
+{
+	if (evm_led_dev) {
+		platform_device_unregister(evm_led_dev);
+		evm_led_dev = NULL;
+	}
+	return 0;
+}
+
+static struct pcf857x_platform_data pcf_data_u2 = {
+	.gpio_base	= PCF_Uxx_BASE(0),
+	.setup		= evm_led_setup,
+	.teardown	= evm_led_teardown,
+};
+
+
+/* U18 - A/V clock generator and user switch */
+
+static int sw_gpio;
+
+static ssize_t
+sw_show(struct device *d, struct device_attribute *a, char *buf)
+{
+	char *s = gpio_get_value_cansleep(sw_gpio) ? "on\n" : "off\n";
+
+	strcpy(buf, s);
+	return strlen(s);
+}
+
+static DEVICE_ATTR(user_sw, S_IRUGO, sw_show, NULL);
+
+static int
+evm_u18_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *c)
+{
+	int	status;
+
+	/* export dip switch option */
+	sw_gpio = gpio + 7;
+	status = gpio_request(sw_gpio, "user_sw");
+	if (status == 0)
+		status = gpio_direction_input(sw_gpio);
+	if (status == 0)
+		status = device_create_file(&client->dev, &dev_attr_user_sw);
+	else
+		gpio_free(sw_gpio);
+	if (status != 0)
+		sw_gpio = -EINVAL;
+
+	/* audio PLL:  48 kHz (vs 44.1 or 32), single rate (vs double) */
+	gpio_request(gpio + 3, "pll_fs2");
+	gpio_direction_output(gpio + 3, 0);
+
+	gpio_request(gpio + 2, "pll_fs1");
+	gpio_direction_output(gpio + 2, 0);
+
+	gpio_request(gpio + 1, "pll_sr");
+	gpio_direction_output(gpio + 1, 0);
+
+	return 0;
+}
+
+static int
+evm_u18_teardown(struct i2c_client *client, int gpio, unsigned ngpio, void *c)
+{
+	gpio_free(gpio + 1);
+	gpio_free(gpio + 2);
+	gpio_free(gpio + 3);
+
+	if (sw_gpio > 0) {
+		device_remove_file(&client->dev, &dev_attr_user_sw);
+		gpio_free(sw_gpio);
+	}
+	return 0;
+}
+
+static struct pcf857x_platform_data pcf_data_u18 = {
+	.gpio_base	= PCF_Uxx_BASE(1),
+	.n_latch	= (1 << 3) | (1 << 2) | (1 << 1),
+	.setup		= evm_u18_setup,
+	.teardown	= evm_u18_teardown,
+};
+
+
+/* U35 - various I/O signals used to manage USB, CF, ATA, etc */
+
+#if 0
+static struct pcf857x_platform_data pcf_data_u35 = {
+	.gpio_base = PCF_Uxx_BASE(2),
+};
+#endif
+
+/*----------------------------------------------------------------------*/
+
+static struct i2c_board_info __initdata i2c_info[] =  {
+	{
+		I2C_BOARD_INFO("pcf857x", 0x38),
+		.type		= "pcf8574a",
+		.platform_data	= &pcf_data_u2,
+	},
+	{
+		I2C_BOARD_INFO("pcf857x", 0x39),
+		.type		= "pcf8574a",
+		.platform_data	= &pcf_data_u18,
+	},
+#if 0
+/* don't clash with mach-davinci/i2c-client.c
+ * or drivers/i2c/chips/gpio_expander_davinci.c
+ * ... eventually both should vanish
+ */
+	{
+		I2C_BOARD_INFO("pcf857x", 0x3a),
+		.type		= "pcf8574a",
+		.platform_data	= &pcf_data_u35,
+	},
+#endif
+	/* ALSO:
+	 * - tvl320aic33 audio codec (0x1b)
+	 * - msp430 microcontroller (0x23)
+	 * - 24wc256 eeprom (0x50)
+	 * - tvp5146 video decoder (0x5d)
+	 */
+};
+
 static struct platform_device *davinci_evm_devices[] __initdata = {
 	&davinci_evm_flash_device,
 #if defined(CONFIG_FB_DAVINCI) || defined(CONFIG_FB_DAVINCI_MODULE)
@@ -189,9 +377,13 @@ static __init void davinci_evm_init(void
 	davinci_psc_init();
 
 #if defined(CONFIG_BLK_DEV_DAVINCI) || defined(CONFIG_BLK_DEV_DAVINCI_MODULE)
+#if defined(CONFIG_MTD_CFI) || defined(CONFIG_MTD_CFI_MODULE)
 	printk(KERN_WARNING "WARNING: both IDE and NOR flash are enabled, "
 	       "but share pins.\n\t Disable IDE for NOR support.\n");
 #endif
+#endif
+
+	i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info));
 
 	platform_add_devices(davinci_evm_devices,
 			     ARRAY_SIZE(davinci_evm_devices));