linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch/rfc 1/4] GPIO implementation framework
       [not found] <200710291809.29936.david-b@pacbell.net>
@ 2007-10-30  1:51 ` David Brownell
  2007-11-05 21:05   ` David Brownell
  2007-10-30  1:51 ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver David Brownell
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-10-30  1:51 UTC (permalink / raw)
  To: Linux Kernel list
  Cc: Felipe Balbi, Bill Gatliff, Haavard Skinnemoen, Andrew Victor,
	Tony Lindgren, Jean Delvare, eric miao, Kevin Hilman, Paul Mundt,
	Ben Dooks

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).

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
 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(-)

--- a/Documentation/gpio.txt	2007-10-29 01:27:23.000000000 -0700
+++ b/Documentation/gpio.txt	2007-10-29 01:30:02.000000000 -0700
@@ -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.
--- a/lib/Kconfig	2007-10-29 01:27:23.000000000 -0700
+++ b/lib/Kconfig	2007-10-29 01:30:02.000000000 -0700
@@ -141,4 +141,10 @@ config HAS_DMA
 config CHECK_SIGNATURE
 	bool
 
+#
+# gpiolib is selected if needed
+#
+config GPIO_LIB
+	boolean
+
 endmenu
--- a/include/asm-generic/gpio.h	2007-10-29 01:27:23.000000000 -0700
+++ b/include/asm-generic/gpio.h	2007-10-29 01:30:02.000000000 -0700
@@ -1,6 +1,131 @@
 #ifndef _ASM_GENERIC_GPIO_H
 #define _ASM_GENERIC_GPIO_H
 
+#ifdef CONFIG_GPIO_LIB
+
+/* Platforms may implement their GPIO interface with library code,
+ * at the cost of performance for non-inlined operations.
+ *
+ * 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).
+ */
+
+#ifndef ARCH_NR_GPIOS
+#define ARCH_NR_GPIOS		512
+#endif
+
+#ifndef ARCH_GPIOS_PER_CHIP
+#define ARCH_GPIOS_PER_CHIP	BITS_PER_LONG
+#endif
+
+struct seq_file;
+
+/**
+ * struct gpio_chip - abstract a GPIO controller
+ * @label: for diagnostics
+ * @direction_input: configures signal "offset" as input, or returns error
+ * @get: returns value for signal "offset"; for output signals this
+ *	returns either the value actually sensed, or zero
+ * @direction_output: configures signal "offset" as output, or returns error
+ * @set: assigns output value for signal "offset"
+ * @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).
+ * @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).
+ * @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.
+ * Example sources would be SOC controllers, FPGAs, multifunction
+ * chips, dedicated GPIO expanders, and so on.
+ *
+ * Each chip controls a number of signals, numbered 0..@ngpio, which are
+ * identified in method calls by an "offset" value.  When those signals
+ * are referenced through calls like gpio_get_value(gpio), the offset
+ * is calculated by subtracting @base from the gpio number.
+ */
+struct gpio_chip {
+	char			*label;
+
+	int			(*direction_input)(struct gpio_chip *chip,
+						unsigned offset);
+	int			(*get)(struct gpio_chip *chip,
+						unsigned offset);
+	int			(*direction_output)(struct gpio_chip *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);
+
+
+/* Always use the library code for GPIO management calls,
+ * or when sleeping may be involved.
+ */
+extern int gpio_request(unsigned gpio, const char *label);
+extern void gpio_free(unsigned gpio);
+
+extern int gpio_direction_input(unsigned gpio);
+extern int gpio_direction_output(unsigned gpio, int value);
+
+extern int gpio_get_value_cansleep(unsigned gpio);
+extern void gpio_set_value_cansleep(unsigned gpio, int value);
+
+
+/* A platform's <asm/gpio.h> code may want to inline the I/O calls when
+ * the GPIO is constant and refers to some always-present controller,
+ * giving direct access to chip registers and tight bitbanging loops.
+ */
+extern int __gpio_get_value(unsigned gpio);
+extern void __gpio_set_value(unsigned gpio, int value);
+
+extern int __gpio_cansleep(unsigned gpio);
+
+
+#else
+
 /* platforms that don't directly support access to GPIOs through I2C, SPI,
  * or other blocking infrastructure can use these wrappers.
  */
@@ -22,4 +147,6 @@ static inline void gpio_set_value_cansle
 	gpio_set_value(gpio, value);
 }
 
+#endif
+
 #endif /* _ASM_GENERIC_GPIO_H */
--- a/lib/Makefile	2007-10-29 01:27:23.000000000 -0700
+++ b/lib/Makefile	2007-10-29 01:30:02.000000000 -0700
@@ -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
 
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ b/lib/gpiolib.c	2007-10-29 07:30:04.000000000 -0700
@@ -0,0 +1,500 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+
+#include <asm/gpio.h>
+
+
+/* Optional implementation infrastructure for GPIO interfaces.
+ *
+ * Platforms may want to use this if they tend to use very many GPIOs
+ * that aren't part of a System-On-Chip core; or across I2C/SPI/etc.
+ *
+ * When kernel footprint or instruction count is an issue, simpler
+ * implementations may be preferred.
+ */
+
+
+/* When debugging, extend minimal trust to callers and platform code;
+ * otherwise, minimize overhead in what may be bitbanging codepaths.
+ */
+#ifdef	CONFIG_DEBUG_GPIO
+#define	extra_checks	1
+#else
+#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)];
+
+/* 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)
+{
+	int		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);
+#endif
+
+	if (!requested)
+		printk(KERN_DEBUG "GPIO-%d autorequested\n",
+				chip->base + offset);
+}
+
+/* caller holds gpio_lock */
+static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
+{
+	return chips[gpio / ARCH_GPIOS_PER_CHIP];
+}
+
+/**
+ * gpiochip_add() - register a gpio_chip
+ * @chip: the chip to register, with chip->base initialized
+ * Context: potentially before irqs or kmalloc will work
+ *
+ * Returns a negative errno if the chip can't be registered, such as
+ * because the chip->base is invalid or already associated with a
+ * different chip.  Otherwise it returns zero as a success code.
+ */
+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)
+		return -EINVAL;
+	if ((chip->base + chip->ngpio) >= ARCH_NR_GPIOS)
+		return -EINVAL;
+	if (chip->ngpio > ARCH_GPIOS_PER_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;
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpiochip_add);
+
+/**
+ * gpiochip_remove() - unregister a gpio_chip
+ * @chip: the chip to unregister
+ *
+ * A gpio_chip with any GPIOs still requested may not be removed.
+ */
+int gpiochip_remove(struct gpio_chip *chip)
+{
+	unsigned long	flags;
+	int		status = 0;
+	int		offset;
+	unsigned	id = chip->base / ARCH_GPIOS_PER_CHIP;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	for (offset = 0; offset < chip->ngpio; offset++) {
+		if (gpiochip_is_requested(chip, offset)) {
+			status = -EBUSY;
+			break;
+		}
+	}
+
+	if (status == 0) {
+		if (chips[id] == chip)
+			chips[id] = NULL;
+		else
+			status = -EINVAL;
+	}
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpiochip_remove);
+
+
+/* These "optional" allocation calls help prevent drivers from stomping
+ * on each other, and help provide better diagnostics in debugfs.
+ * They're called even less than the "set direction" calls.
+ */
+int gpio_request(unsigned gpio, const char *label)
+{
+	struct gpio_chip	*chip;
+	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)
+		goto done;
+
+	/* NOTE:  gpio_request() can be called in early boot,
+	 * before IRQs are enabled.
+	 */
+
+	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;
+#endif
+
+done:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_request);
+
+void gpio_free(unsigned gpio)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip = gpio_to_chip(gpio);
+	if (!chip)
+		goto done;
+	gpio -= chip->base;
+	if (gpio >= chip->ngpio)
+		goto done;
+
+#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;
+#endif
+	WARN_ON(extra_checks && chip == NULL);
+done:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gpio_free);
+
+
+
+/* Drivers MUST make configuration calls before get/set calls
+ *
+ * As a rule these aren't called more than once (except for
+ * drivers using the open-drain emulation idiom) so these are
+ * natural places to accumulate extra debugging checks.  Note
+ * that we can't rely on gpio_request() having been called.
+ */
+
+int gpio_direction_input(unsigned gpio)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+	int			status = -EINVAL;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip = gpio_to_chip(gpio);
+	if (!chip || !chip->get || !chip->direction_input)
+		goto fail;
+	gpio -= chip->base;
+	if (gpio >= chip->ngpio)
+		goto fail;
+	gpio_ensure_requested(chip, gpio);
+
+	/* now we know the gpio is valid and chip won't vanish */
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	might_sleep_if(extra_checks && chip->can_sleep);
+
+	status = chip->direction_input(chip, gpio);
+	if (status == 0)
+		clear_bit(gpio, chip->is_out);
+	return status;
+fail:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	return status;
+}
+EXPORT_SYMBOL(gpio_direction_input);
+
+int gpio_direction_output(unsigned gpio, int value)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+	int			status = -EINVAL;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+
+	chip = gpio_to_chip(gpio);
+	if (!chip || !chip->get || !chip->direction_output)
+		goto fail;
+	gpio -= chip->base;
+	if (gpio >= chip->ngpio)
+		goto fail;
+	gpio_ensure_requested(chip, gpio);
+
+	/* now we know the gpio is valid and chip won't vanish */
+
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	might_sleep_if(extra_checks && chip->can_sleep);
+
+	status = chip->direction_output(chip, gpio, value);
+	if (status == 0)
+		set_bit(gpio, chip->is_out);
+	return status;
+fail:
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_direction_output);
+
+
+/* I/O calls are only valid after configuration completed; the relevant
+ * "is this a valid GPIO" error checks should already have been done.
+ *
+ * "Get" operations are often inlinable as reading a pin value register,
+ * and masking the relevant bit in that register.
+ *
+ * When "set" operations are inlinable, they involve writing that mask to
+ * one register to set a low value, or a different register to set it high.
+ * Otherwise a spinlock is needed, and there's little value to inlining.
+ */
+int __gpio_get_value(unsigned gpio)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	chip = gpio_to_chip(gpio);
+	if (extra_checks)
+		gpio_ensure_requested(chip, gpio - chip->base);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	if (unlikely(chip->can_sleep)) {
+		WARN_ON(extra_checks);
+		return 0;
+	} else
+		return chip->get(chip, gpio - chip->base);
+}
+EXPORT_SYMBOL_GPL(__gpio_get_value);
+
+void __gpio_set_value(unsigned gpio, int value)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	chip = gpio_to_chip(gpio);
+	if (extra_checks)
+		gpio_ensure_requested(chip, gpio - chip->base);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	if (unlikely(chip->can_sleep))
+		WARN_ON(extra_checks);
+	else
+		chip->set(chip, gpio - chip->base, value);
+}
+EXPORT_SYMBOL_GPL(__gpio_set_value);
+
+int __gpio_cansleep(unsigned gpio)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	chip = gpio_to_chip(gpio);
+	if (extra_checks)
+		gpio_ensure_requested(chip, gpio - chip->base);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+	return chip->can_sleep;
+}
+EXPORT_SYMBOL_GPL(__gpio_cansleep);
+
+
+
+/* There's no value in inlining GPIO calls that may sleep.
+ * Common examples include ones connected to I2C or SPI chips.
+ */
+
+int gpio_get_value_cansleep(unsigned gpio)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+
+	might_sleep_if(extra_checks);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	chip = gpio_to_chip(gpio);
+	if (extra_checks)
+		gpio_ensure_requested(chip, gpio - chip->base);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	return chip->get(chip, gpio - chip->base);
+}
+EXPORT_SYMBOL_GPL(gpio_get_value_cansleep);
+
+void gpio_set_value_cansleep(unsigned gpio, int value)
+{
+	unsigned long		flags;
+	struct gpio_chip	*chip;
+
+	might_sleep_if(extra_checks);
+
+	spin_lock_irqsave(&gpio_lock, flags);
+	chip = gpio_to_chip(gpio);
+	if (extra_checks)
+		gpio_ensure_requested(chip, gpio - chip->base);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	chip->set(chip, gpio - chip->base, value);
+}
+EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
+
+
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+
+static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	unsigned	i;
+
+	for (i = 0; i < chip->ngpio; i++) {
+		unsigned	gpio;
+		int		is_out;
+
+		if (!chip->requested[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],
+			is_out ? "out" : "in ",
+			chip->get
+				? (chip->get(chip, i) ? "hi" : "lo")
+				: "?  ");
+
+		if (!is_out) {
+			int		irq = gpio_to_irq(gpio);
+			struct irq_desc	*desc = irq_desc + irq;
+
+			/* This races with request_irq(), set_irq_type(),
+			 * and set_irq_wake() ... but those are "rare".
+			 *
+			 * More significantly, trigger type flags aren't
+			 * currently maintained by genirq.
+			 */
+			if (irq >= 0 && desc->action) {
+				char *trigger;
+
+				switch (desc->status & IRQ_TYPE_SENSE_MASK) {
+				case IRQ_TYPE_NONE:
+					trigger = "(default)";
+					break;
+				case IRQ_TYPE_EDGE_FALLING:
+					trigger = "edge-falling";
+					break;
+				case IRQ_TYPE_EDGE_RISING:
+					trigger = "edge-rising";
+					break;
+				case IRQ_TYPE_EDGE_BOTH:
+					trigger = "edge-both";
+					break;
+				case IRQ_TYPE_LEVEL_HIGH:
+					trigger = "level-high";
+					break;
+				case IRQ_TYPE_LEVEL_LOW:
+					trigger = "level-low";
+					break;
+				default:
+					trigger = "?trigger?";
+					break;
+				}
+
+				seq_printf(s, " irq-%d %s%s",
+					irq, trigger,
+					(desc->status & IRQ_WAKEUP)
+						? " wakeup" : "");
+			}
+		}
+
+		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;
+}
+
+static int gpiolib_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, gpiolib_show, NULL);
+}
+
+static struct file_operations gpiolib_operations = {
+	.open		= gpiolib_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init gpiolib_debugfs_init(void)
+{
+	/* /sys/kernel/debug/gpio */
+	(void) debugfs_create_file("gpio", S_IFREG | S_IRUGO,
+				NULL, NULL, &gpiolib_operations);
+	return 0;
+}
+postcore_initcall(gpiolib_debugfs_init);
+
+#endif	/* DEBUG_FS */

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [patch/rfc 2/4] pcf875x I2C GPIO expander driver
       [not found] <200710291809.29936.david-b@pacbell.net>
  2007-10-30  1:51 ` [patch/rfc 1/4] GPIO implementation framework David Brownell
@ 2007-10-30  1:51 ` David Brownell
  2007-11-30 12:32   ` Jean Delvare
  2007-10-30  1:53 ` [patch/rfc 3/4] DaVinci platform uses new GPIOLIB David Brownell
  2007-10-30  1:54 ` [patch/rfc 4/4] DaVinci EVM uses pcf857x GPIO driver David Brownell
  3 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-10-30  1:51 UTC (permalink / raw)
  To: Linux Kernel list
  Cc: Felipe Balbi, Bill Gatliff, Haavard Skinnemoen, Andrew Victor,
	Tony Lindgren, Jean Delvare, eric miao, Kevin Hilman, Paul Mundt,
	Ben Dooks

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.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
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(+)

--- a/drivers/i2c/chips/Kconfig	2007-10-28 21:04:06.000000000 -0700
+++ b/drivers/i2c/chips/Kconfig	2007-10-29 14:16:01.000000000 -0700
@@ -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
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ b/include/linux/pcf857x.h	2007-10-28 21:09:49.000000000 -0700
@@ -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
+ * @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
+ *
+ * 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.
+ *
+ * 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
+ * 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.
+ */
+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 */
--- a/drivers/i2c/chips/Makefile	2007-10-28 21:04:06.000000000 -0700
+++ b/drivers/i2c/chips/Makefile	2007-10-28 21:09:49.000000000 -0700
@@ -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
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/i2c/chips/pcf857x.c	2007-10-29 14:12:21.000000000 -0700
@@ -0,0 +1,309 @@
+/*
+ * pcf857x - driver for pcf857{4,4a,5,5c} 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/i2c.h>
+#include <linux/pcf857x.h>
+
+#include <asm/gpio.h>
+
+
+/*
+ * The pcf857x chips only expose a one read register 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 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 */
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* 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);
+}
+
+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));
+}
+
+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);
+}
+
+static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value)
+{
+	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, };
+	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)
+{
+	pcf857x_output16(chip, offset, value);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pcf857x_probe(struct i2c_client *client)
+{
+	struct pcf857x_platform_data	*pdata;
+	struct pcf857x			*gpio;
+	int				status = 0;
+
+	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;
+
+	/* 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
+			|| 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;
+
+		/* fail if there's no chip present */
+		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.
+	 */
+	} else if (strcmp(client->name, "pcf8575c") == 0
+			|| strcmp(client->name, "pcf8575") == 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 */
+		status = i2c_read_le16(client);
+
+	} else
+		status = -ENODEV;
+
+	if (status < 0) {
+		dev_dbg(&client->dev, "probe error %d for '%s'\n",
+				status, client->name);
+		kfree(gpio);
+		return status;
+	}
+
+	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;
+	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");

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [patch/rfc 3/4] DaVinci platform uses new GPIOLIB
       [not found] <200710291809.29936.david-b@pacbell.net>
  2007-10-30  1:51 ` [patch/rfc 1/4] GPIO implementation framework David Brownell
  2007-10-30  1:51 ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver David Brownell
@ 2007-10-30  1:53 ` David Brownell
  2007-10-30  1:54 ` [patch/rfc 4/4] DaVinci EVM uses pcf857x GPIO driver David Brownell
  3 siblings, 0 replies; 60+ messages in thread
From: David Brownell @ 2007-10-30  1:53 UTC (permalink / raw)
  To: Linux Kernel list
  Cc: Felipe Balbi, Bill Gatliff, Haavard Skinnemoen, Andrew Victor,
	Tony Lindgren, Jean Delvare, eric miao, Kevin Hilman, Paul Mundt,
	Ben Dooks

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(-)

--- a/arch/arm/Kconfig	2007-10-27 08:50:31.000000000 -0700
+++ b/arch/arm/Kconfig	2007-10-27 08:50:31.000000000 -0700
@@ -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
--- a/arch/arm/mach-davinci/gpio.c	2007-10-27 08:50:22.000000000 -0700
+++ b/arch/arm/mach-davinci/gpio.c	2007-10-27 13:57:01.000000000 -0700
@@ -1,7 +1,7 @@
 /*
  * TI DaVinci GPIO Support
  *
- * Copyright (c) 2006 David Brownell
+ * Copyright (c) 2006-2007 David Brownell
  * Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -26,47 +26,45 @@
 
 #include <asm/mach/irq.h>
 
-static DEFINE_SPINLOCK(gpio_lock);
-static DECLARE_BITMAP(gpio_in_use, DAVINCI_N_GPIO);
-
-int gpio_request(unsigned gpio, const char *tag)
-{
-	if (gpio >= DAVINCI_N_GPIO)
-		return -EINVAL;
 
-	if (test_and_set_bit(gpio, gpio_in_use))
-		return -EBUSY;
+static DEFINE_SPINLOCK(gpio_lock);
 
-	return 0;
-}
-EXPORT_SYMBOL(gpio_request);
+struct davinci_gpio {
+	struct gpio_chip	chip;
+	struct gpio_controller	*__iomem regs;
+};
 
-void gpio_free(unsigned gpio)
-{
-	if (gpio >= DAVINCI_N_GPIO)
-		return;
+static struct davinci_gpio chips[DIV_ROUND_UP(DAVINCI_N_GPIO, 32)];
 
-	clear_bit(gpio, gpio_in_use);
-}
-EXPORT_SYMBOL(gpio_free);
 
 /* create a non-inlined version */
-static struct gpio_controller *__iomem gpio2controller(unsigned gpio)
+static struct gpio_controller *__iomem __init gpio2controller(unsigned gpio)
 {
 	return __gpio_to_controller(gpio);
 }
 
+
+/*--------------------------------------------------------------------------*/
+
 /*
- * Assuming the pin is muxed as a gpio output, set its output value.
+ * board setup code *MUST* set PINMUX0 and PINMUX1 as
+ * needed, and enable the GPIO clock.
  */
-void __gpio_set(unsigned gpio, int value)
+
+static int davinci_direction_in(struct gpio_chip *chip, unsigned offset)
 {
-	struct gpio_controller *__iomem g = gpio2controller(gpio);
+	struct davinci_gpio *d = container_of(chip, struct davinci_gpio, chip);
+	struct gpio_controller *__iomem g = d->regs;
+	u32 temp;
 
-	__raw_writel(__gpio_mask(gpio), value ? &g->set_data : &g->clr_data);
-}
-EXPORT_SYMBOL(__gpio_set);
+	spin_lock(&gpio_lock);
+	temp = __raw_readl(&g->dir);
+	temp |= (1 << offset);
+	__raw_writel(temp, &g->dir);
+	spin_unlock(&gpio_lock);
 
+	return 0;
+}
 
 /*
  * Read the pin's value (works even if it's set up as output);
@@ -75,61 +73,74 @@ EXPORT_SYMBOL(__gpio_set);
  * Note that changes are synched to the GPIO clock, so reading values back
  * right after you've set them may give old values.
  */
-int __gpio_get(unsigned gpio)
+static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
-	struct gpio_controller	*__iomem g = gpio2controller(gpio);
+	struct davinci_gpio *d = container_of(chip, struct davinci_gpio, chip);
+	struct gpio_controller *__iomem g = d->regs;
 
-	return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));
+	return !!((1 << offset) & __raw_readl(&g->in_data));
 }
-EXPORT_SYMBOL(__gpio_get);
-
 
-/*--------------------------------------------------------------------------*/
-
-/*
- * board setup code *MUST* set PINMUX0 and PINMUX1 as
- * needed, and enable the GPIO clock.
- */
-
-int gpio_direction_input(unsigned gpio)
+static int
+davinci_direction_out(struct gpio_chip *chip, unsigned offset, int value)
 {
-	struct gpio_controller	*__iomem g = gpio2controller(gpio);
-	u32			temp;
-	u32			mask;
-
-	if (!g)
-		return -EINVAL;
+	struct davinci_gpio *d = container_of(chip, struct davinci_gpio, chip);
+	struct gpio_controller *__iomem g = d->regs;
+	u32 temp;
+	u32 mask = 1 << offset;
 
 	spin_lock(&gpio_lock);
-	mask = __gpio_mask(gpio);
 	temp = __raw_readl(&g->dir);
-	temp |= mask;
+	temp &= ~mask;
+	__raw_writel(mask, value ? &g->set_data : &g->clr_data);
 	__raw_writel(temp, &g->dir);
 	spin_unlock(&gpio_lock);
 	return 0;
 }
-EXPORT_SYMBOL(gpio_direction_input);
 
-int gpio_direction_output(unsigned gpio, int value)
+/*
+ * Assuming the pin is muxed as a gpio output, set its output value.
+ */
+static void
+davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 {
-	struct gpio_controller *__iomem g = gpio2controller(gpio);
-	u32 temp;
-	u32 mask;
+	struct davinci_gpio *d = container_of(chip, struct davinci_gpio, chip);
+	struct gpio_controller *__iomem g = d->regs;
 
-	if (!g)
-		return -EINVAL;
+	__raw_writel((1 << offset), value ? &g->set_data : &g->clr_data);
+}
+
+static int __init davinci_gpio_setup(void)
+{
+	int i, base;
+
+	for (i = 0, base = 0;
+			i < ARRAY_SIZE(chips);
+			i++, base += 32) {
+		chips[i].chip.label = "DaVinci";
+
+		chips[i].chip.direction_input = davinci_direction_in;
+		chips[i].chip.get = davinci_gpio_get;
+		chips[i].chip.direction_output = davinci_direction_out;
+		chips[i].chip.set = davinci_gpio_set;
+
+		chips[i].chip.base = base;
+		chips[i].chip.ngpio = DAVINCI_N_GPIO - base;
+		if (chips[i].chip.ngpio > 32)
+			chips[i].chip.ngpio = 32;
+
+		chips[i].regs = gpio2controller(base);
+
+		gpiochip_add(&chips[i].chip);
+	}
 
-	spin_lock(&gpio_lock);
-	mask = __gpio_mask(gpio);
-	temp = __raw_readl(&g->dir);
-	temp &= ~mask;
-	__raw_writel(mask, value ? &g->set_data : &g->clr_data);
-	__raw_writel(temp, &g->dir);
-	spin_unlock(&gpio_lock);
 	return 0;
 }
-EXPORT_SYMBOL(gpio_direction_output);
 
+/* REVISIT we may want to set up GPIOs even earlier ... */
+pure_initcall(davinci_gpio_setup);
+
+/*--------------------------------------------------------------------------*/
 /*
  * We expect irqs will normally be set up as input pins, but they can also be
  * used as output pins ... which is convenient for testing.
--- a/include/asm-arm/arch-davinci/gpio.h	2007-10-27 08:50:31.000000000 -0700
+++ b/include/asm-arm/arch-davinci/gpio.h	2007-10-27 08:50:31.000000000 -0700
@@ -15,6 +15,7 @@
 
 #include <linux/io.h>
 #include <asm/hardware.h>
+#include <asm-generic/gpio.h>
 
 /*
  * basic gpio routines
@@ -27,13 +28,16 @@
  * need to pay attention to PINMUX0 and PINMUX1 to be sure those pins are
  * used as gpios, not with other peripherals.
  *
- * GPIOs are numbered 0..(DAVINCI_N_GPIO-1).  For documentation, and maybe
- * for later updates, code should write GPIO(N) or:
+ * On-chip GPIOs are numbered 0..(DAVINCI_N_GPIO-1).  For documentation,
+ * and maybe for later updates, code should write GPIO(N) or:
  *  - GPIOV18(N) for 1.8V pins, N in 0..53; same as GPIO(0)..GPIO(53)
  *  - GPIOV33(N) for 3.3V pins, N in 0..17; same as GPIO(54)..GPIO(70)
  *
  * For GPIO IRQs use gpio_to_irq(GPIO(N)) or gpio_to_irq(GPIOV33(N)) etc
  * for now, that's != GPIO(N)
+ *
+ * GPIOs can also be on external chips, numbered after the ones built-in
+ * to the DaVinci chip.  For now, they won't be usable as IRQ sources.
  */
 #define	GPIO(X)		(X)		/* 0 <= X <= 70 */
 #define	GPIOV18(X)	(X)		/* 1.8V i/o; 0 <= X <= 53 */
@@ -83,25 +87,17 @@ static inline u32 __gpio_mask(unsigned g
 }
 
 /* The get/set/clear functions will inline when called with constant
- * parameters, for low-overhead bitbanging.  Illegal constant parameters
- * cause link-time errors.
+ * parameters referencing built-in GPIOs, for low-overhead bitbanging.
  *
- * Otherwise, calls with variable parameters use outlined functions.
+ * Otherwise, calls with variable parameters or referencing external
+ * GPIOs (e.g. on GPIO expander chips) use outlined functions.
  */
-extern int __error_inval_gpio(void);
-
-extern void __gpio_set(unsigned gpio, int value);
-extern int __gpio_get(unsigned gpio);
-
 static inline void gpio_set_value(unsigned gpio, int value)
 {
-	if (__builtin_constant_p(value)) {
+	if (__builtin_constant_p(value) && gpio < DAVINCI_N_GPIO) {
 		struct gpio_controller	*__iomem g;
 		u32			mask;
 
-		if (gpio >= DAVINCI_N_GPIO)
-			__error_inval_gpio();
-
 		g = __gpio_to_controller(gpio);
 		mask = __gpio_mask(gpio);
 		if (value)
@@ -111,48 +107,47 @@ static inline void gpio_set_value(unsign
 		return;
 	}
 
-	__gpio_set(gpio, value);
+	__gpio_set_value(gpio, value);
 }
 
 /* Returns zero or nonzero; works for gpios configured as inputs OR
- * as outputs.
+ * as outputs, at least for built-in GPIOs.
  *
- * NOTE: changes in reported values are synchronized to the GPIO clock.
- * This is most easily seen after calling gpio_set_value() and then immediatly
- * gpio_get_value(), where the gpio_get_value() would return the old value
- * until the GPIO clock ticks and the new value gets latched.
+ * NOTE: for built-in GPIOs, changes in reported values are synchronized
+ * to the GPIO clock.  This is easily seen after calling gpio_set_value()
+ * and then immediately gpio_get_value(), where the gpio_get_value() will
+ * return the old value until the GPIO clock ticks and the new value gets
+ * latched.
  */
-
 static inline int gpio_get_value(unsigned gpio)
 {
 	struct gpio_controller	*__iomem g;
 
-	if (!__builtin_constant_p(gpio))
-		return __gpio_get(gpio);
-
-	if (gpio >= DAVINCI_N_GPIO)
-		return __error_inval_gpio();
+	if (!__builtin_constant_p(gpio) || gpio >= DAVINCI_N_GPIO)
+		return __gpio_get_value(gpio);
 
 	g = __gpio_to_controller(gpio);
 	return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));
 }
 
-/* powerup default direction is IN */
-extern int gpio_direction_input(unsigned gpio);
-extern int gpio_direction_output(unsigned gpio, int value);
-
-#include <asm-generic/gpio.h>	/* cansleep wrappers */
-
-extern int gpio_request(unsigned gpio, const char *tag);
-extern void gpio_free(unsigned gpio);
+static inline int gpio_cansleep(unsigned gpio)
+{
+	if (__builtin_constant_p(gpio) && gpio < DAVINCI_N_GPIO)
+		return 0;
+	else
+		return __gpio_cansleep(gpio);
+}
 
 static inline int gpio_to_irq(unsigned gpio)
 {
+	if (gpio >= DAVINCI_N_GPIO)
+		return -EINVAL;
 	return DAVINCI_N_AINTC_IRQ + gpio;
 }
 
 static inline int irq_to_gpio(unsigned irq)
 {
+	/* caller guarantees gpio_to_irq() succeeded */
 	return irq - DAVINCI_N_AINTC_IRQ;
 }
 

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [patch/rfc 4/4] DaVinci EVM uses pcf857x GPIO driver
       [not found] <200710291809.29936.david-b@pacbell.net>
                   ` (2 preceding siblings ...)
  2007-10-30  1:53 ` [patch/rfc 3/4] DaVinci platform uses new GPIOLIB David Brownell
@ 2007-10-30  1:54 ` David Brownell
  3 siblings, 0 replies; 60+ messages in thread
From: David Brownell @ 2007-10-30  1:54 UTC (permalink / raw)
  To: Linux Kernel list
  Cc: Felipe Balbi, Bill Gatliff, Haavard Skinnemoen, Andrew Victor,
	Tony Lindgren, Jean Delvare, eric miao, Kevin Hilman, Paul Mundt,
	Ben Dooks

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(+)

--- a/arch/arm/mach-davinci/board-evm.c	2007-10-28 21:03:55.000000000 -0700
+++ b/arch/arm/mach-davinci/board-evm.c	2007-10-29 13:05:40.000000000 -0700
@@ -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));

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-10-30  1:51 ` [patch/rfc 1/4] GPIO implementation framework David Brownell
@ 2007-11-05 21:05   ` David Brownell
  2007-11-13  2:28     ` eric miao
  0 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-05 21:05 UTC (permalink / raw)
  To: Linux Kernel list
  Cc: Felipe Balbi, Bill Gatliff, Haavard Skinnemoen, Andrew Victor,
	Tony Lindgren, Jean Delvare, eric miao, Kevin Hilman, Paul Mundt,
	Ben Dooks

On Monday 29 October 2007, David Brownell wrote:
> 
> 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.


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



^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-05 21:05   ` David Brownell
@ 2007-11-13  2:28     ` eric miao
  2007-11-13 19:06       ` David Brownell
  0 siblings, 1 reply; 60+ messages in thread
From: eric miao @ 2007-11-13  2:28 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

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

On Nov 6, 2007 5:05 AM, David Brownell <david-b@pacbell.net> wrote:
> On Monday 29 October 2007, David Brownell wrote:
> >
> > 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.
>
>
>
> > 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.
>
>
>



-- 
Cheers
- eric

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-13  2:28     ` eric miao
@ 2007-11-13 19:06       ` David Brownell
  2007-11-14  0:57         ` eric miao
  0 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-13 19:06 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Monday 12 November 2007, eric miao wrote:
> 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.


> 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)?


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


> 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).


> 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



> What do you think?
> 
> - eric
> 
> On Nov 6, 2007 5:05 AM, David Brownell <david-b@pacbell.net> wrote:
> > On Monday 29 October 2007, David Brownell wrote:
> > >
> > > 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
> >
> 

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-13 19:06       ` David Brownell
@ 2007-11-14  0:57         ` eric miao
  2007-11-14  1:00           ` eric miao
  2007-11-14  3:30           ` David Brownell
  0 siblings, 2 replies; 60+ messages in thread
From: eric miao @ 2007-11-14  0:57 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Nov 14, 2007 3:06 AM, David Brownell <david-b@pacbell.net> wrote:
> On Monday 12 November 2007, eric miao wrote:
> > 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.
>

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


>
> > 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)?
>

the cost is as trivial as the current one.

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

> 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.
>
>
> > 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).
>
>
> > 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.
>

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*.

> - Dave
>
>
>
>
> > What do you think?
> >
> > - eric
> >
> > On Nov 6, 2007 5:05 AM, David Brownell <david-b@pacbell.net> wrote:
> > > On Monday 29 October 2007, David Brownell wrote:
> > > >
> > > > 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
> > >
> >
>

-- 
Cheers
- eric

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  0:57         ` eric miao
@ 2007-11-14  1:00           ` eric miao
  2007-11-14  1:02             ` eric miao
  2007-11-14  3:25             ` David Brownell
  2007-11-14  3:30           ` David Brownell
  1 sibling, 2 replies; 60+ messages in thread
From: eric miao @ 2007-11-14  1:00 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

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(-)

diff --git a/lib/gpiolib.c b/lib/gpiolib.c
index f2ae689..c627efb 100644
--- a/lib/gpiolib.c
+++ b/lib/gpiolib.c
@@ -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 */
-- 
1.5.2.5.GIT

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  1:00           ` eric miao
@ 2007-11-14  1:02             ` eric miao
  2007-11-14  1:03               ` eric miao
  2007-11-14  3:28               ` David Brownell
  2007-11-14  3:25             ` David Brownell
  1 sibling, 2 replies; 60+ messages in thread
From: eric miao @ 2007-11-14  1:02 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

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(-)

diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index d00a287..ba3e336 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -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 */
diff --git a/lib/gpiolib.c b/lib/gpiolib.c
index c627efb..d52c7f1 100644
--- a/lib/gpiolib.c
+++ b/lib/gpiolib.c
@@ -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")
-- 
1.5.2.5.GIT

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  1:02             ` eric miao
@ 2007-11-14  1:03               ` eric miao
  2007-11-14  1:04                 ` eric miao
  2007-11-14  4:18                 ` David Brownell
  2007-11-14  3:28               ` David Brownell
  1 sibling, 2 replies; 60+ messages in thread
From: eric miao @ 2007-11-14  1:03 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

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(-)

diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index ba3e336..783adcf 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -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
 };
diff --git a/lib/gpiolib.c b/lib/gpiolib.c
index d52c7f1..57e0d10 100644
--- a/lib/gpiolib.c
+++ b/lib/gpiolib.c
@@ -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,
-- 
1.5.2.5.GIT

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  1:03               ` eric miao
@ 2007-11-14  1:04                 ` eric miao
  2007-11-14  1:04                   ` eric miao
  2007-11-14  4:18                 ` David Brownell
  1 sibling, 1 reply; 60+ messages in thread
From: eric miao @ 2007-11-14  1:04 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

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(-)

diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 783adcf..da67038 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -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
diff --git a/lib/gpiolib.c b/lib/gpiolib.c
index 57e0d10..a089597 100644
--- a/lib/gpiolib.c
+++ b/lib/gpiolib.c
@@ -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],
-- 
1.5.2.5.GIT

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  1:04                 ` eric miao
@ 2007-11-14  1:04                   ` eric miao
  2007-11-14  4:36                     ` David Brownell
  0 siblings, 1 reply; 60+ messages in thread
From: eric miao @ 2007-11-14  1:04 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

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(-)

diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index da67038..7e70c67 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -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);
diff --git a/lib/gpiolib.c b/lib/gpiolib.c
index a089597..f9e1c88 100644
--- a/lib/gpiolib.c
+++ b/lib/gpiolib.c
@@ -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;

-- 
1.5.2.5.GIT

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  1:00           ` eric miao
  2007-11-14  1:02             ` eric miao
@ 2007-11-14  3:25             ` David Brownell
  2007-11-14  3:53               ` David Brownell
  2007-11-14  6:37               ` eric miao
  1 sibling, 2 replies; 60+ messages in thread
From: David Brownell @ 2007-11-14  3:25 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Tuesday 13 November 2007, eric miao wrote:
> 
> 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


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  1:02             ` eric miao
  2007-11-14  1:03               ` eric miao
@ 2007-11-14  3:28               ` David Brownell
  1 sibling, 0 replies; 60+ messages in thread
From: David Brownell @ 2007-11-14  3:28 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Tuesday 13 November 2007, eric miao wrote:
> 
> 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.  :)


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  0:57         ` eric miao
  2007-11-14  1:00           ` eric miao
@ 2007-11-14  3:30           ` David Brownell
  2007-11-14  6:40             ` eric miao
  1 sibling, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-14  3:30 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Tuesday 13 November 2007, eric miao wrote:
> > > 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.




^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  3:25             ` David Brownell
@ 2007-11-14  3:53               ` David Brownell
  2007-11-14  6:37               ` eric miao
  1 sibling, 0 replies; 60+ messages in thread
From: David Brownell @ 2007-11-14  3:53 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Tuesday 13 November 2007, David Brownell wrote:
>   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.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
 lib/gpiolib.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

--- g26.orig/lib/gpiolib.c	2007-11-12 15:06:45.000000000 -0800
+++ g26/lib/gpiolib.c	2007-11-12 15:07:36.000000000 -0800
@@ -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 */


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  1:03               ` eric miao
  2007-11-14  1:04                 ` eric miao
@ 2007-11-14  4:18                 ` David Brownell
  2007-11-14  6:46                   ` eric miao
  1 sibling, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-14  4:18 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Tuesday 13 November 2007, eric miao wrote:
> 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.


> +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.

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

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  1:04                   ` eric miao
@ 2007-11-14  4:36                     ` David Brownell
  2007-11-14  6:51                       ` eric miao
  2007-11-17 10:38                       ` Jean Delvare
  0 siblings, 2 replies; 60+ messages in thread
From: David Brownell @ 2007-11-14  4:36 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Tuesday 13 November 2007, eric miao wrote:
> Subject: [PATCH 5/5] move per GPIO "requested" to "struct gpio_desc"
> 

>  struct gpio_desc {
>  	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.


> @@ -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 ...


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

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  3:25             ` David Brownell
  2007-11-14  3:53               ` David Brownell
@ 2007-11-14  6:37               ` eric miao
  1 sibling, 0 replies; 60+ messages in thread
From: eric miao @ 2007-11-14  6:37 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Nov 14, 2007 11:25 AM, David Brownell <david-b@pacbell.net> wrote:
> On Tuesday 13 November 2007, eric miao wrote:
> >
> > 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.  :)
>

Ok, I'll update the patches later.

>
> 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 :-)

> - Dave
>
>



-- 
Cheers
- eric

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  3:30           ` David Brownell
@ 2007-11-14  6:40             ` eric miao
  2007-11-14  7:08               ` David Brownell
  0 siblings, 1 reply; 60+ messages in thread
From: eric miao @ 2007-11-14  6:40 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Nov 14, 2007 11:30 AM, David Brownell <david-b@pacbell.net> wrote:
> On Tuesday 13 November 2007, eric miao wrote:
> > > > 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.

>
>
>



-- 
Cheers
- eric

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  4:18                 ` David Brownell
@ 2007-11-14  6:46                   ` eric miao
  0 siblings, 0 replies; 60+ messages in thread
From: eric miao @ 2007-11-14  6:46 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Nov 14, 2007 12:18 PM, David Brownell <david-b@pacbell.net> wrote:
> On Tuesday 13 November 2007, eric miao wrote:
> > 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.

>
>
> > +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.

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

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

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

> - Dave
>



-- 
Cheers
- eric

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  4:36                     ` David Brownell
@ 2007-11-14  6:51                       ` eric miao
  2007-11-14  7:19                         ` David Brownell
  2007-11-17 10:38                       ` Jean Delvare
  1 sibling, 1 reply; 60+ messages in thread
From: eric miao @ 2007-11-14  6:51 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Nov 14, 2007 12:36 PM, David Brownell <david-b@pacbell.net> wrote:
> On Tuesday 13 November 2007, eric miao wrote:
> > Subject: [PATCH 5/5] move per GPIO "requested" to "struct gpio_desc"
> >
>
> >  struct gpio_desc {
> >       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.
>

Fine.

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

>
> > @@ -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 ...
>
>
> >       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?
>

line length issue, just ignore this if you prefer.

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

-- 
Cheers
- eric

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  6:40             ` eric miao
@ 2007-11-14  7:08               ` David Brownell
  2007-11-27  1:46                 ` David Brownell
  0 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-14  7:08 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Tuesday 13 November 2007, eric miao wrote:
> > > > 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


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  6:51                       ` eric miao
@ 2007-11-14  7:19                         ` David Brownell
  2007-11-14  7:36                           ` eric miao
  0 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-14  7:19 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks


> > >  struct gpio_desc {
> > >       struct gpio_chip *chip;
> > >       unsigned is_out:1;
> > > +     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.


> 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

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  7:19                         ` David Brownell
@ 2007-11-14  7:36                           ` eric miao
  0 siblings, 0 replies; 60+ messages in thread
From: eric miao @ 2007-11-14  7:36 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

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.

On Nov 14, 2007 3:19 PM, David Brownell <david-b@pacbell.net> wrote:
>
> > > >  struct gpio_desc {
> > > >       struct gpio_chip *chip;
> > > >       unsigned is_out:1;
> > > > +     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.
>
>
> > 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.
>

Well, absolutely agree on this.

> - Dave
>



-- 
Cheers
- eric

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  4:36                     ` David Brownell
  2007-11-14  6:51                       ` eric miao
@ 2007-11-17 10:38                       ` Jean Delvare
  2007-11-17 17:36                         ` David Brownell
  1 sibling, 1 reply; 60+ messages in thread
From: Jean Delvare @ 2007-11-17 10:38 UTC (permalink / raw)
  To: David Brownell
  Cc: eric miao, Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Kevin Hilman,
	Paul Mundt, Ben Dooks

On Tue, 13 Nov 2007 20:36:13 -0800, David Brownell wrote:
> On Tuesday 13 November 2007, eric miao wrote:
> >  	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.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-17 10:38                       ` Jean Delvare
@ 2007-11-17 17:36                         ` David Brownell
  2007-11-20 15:20                           ` Jean Delvare
  0 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-17 17:36 UTC (permalink / raw)
  To: Jean Delvare
  Cc: eric miao, Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Kevin Hilman,
	Paul Mundt, Ben Dooks

On Saturday 17 November 2007, Jean Delvare wrote:
> On Tue, 13 Nov 2007 20:36:13 -0800, David Brownell wrote:
> > On Tuesday 13 November 2007, eric miao wrote:
> > >  	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.

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


> 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

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-17 17:36                         ` David Brownell
@ 2007-11-20 15:20                           ` Jean Delvare
  0 siblings, 0 replies; 60+ messages in thread
From: Jean Delvare @ 2007-11-20 15:20 UTC (permalink / raw)
  To: David Brownell
  Cc: eric miao, Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Kevin Hilman,
	Paul Mundt, Ben Dooks

Hi David,

On Sat, 17 Nov 2007 09:36:24 -0800, David Brownell wrote:
> On Saturday 17 November 2007, Jean Delvare wrote:
> > On Tue, 13 Nov 2007 20:36:13 -0800, David Brownell wrote:
> > > On Tuesday 13 November 2007, eric miao wrote:
> > > >  	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.
> 
> It's perfectly correct.  That it's an idiom you don't
> seem to *like* but is distinct from correctness.
> 
> > 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".

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?

> 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,
-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-14  7:08               ` David Brownell
@ 2007-11-27  1:46                 ` David Brownell
  2007-11-27 10:58                   ` eric miao
  0 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-27  1:46 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Tuesday 13 November 2007, David Brownell wrote:
> 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

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-27  1:46                 ` David Brownell
@ 2007-11-27 10:58                   ` eric miao
  2007-11-27 17:26                     ` David Brownell
                                       ` (3 more replies)
  0 siblings, 4 replies; 60+ messages in thread
From: eric miao @ 2007-11-27 10:58 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

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

diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 869b739..e3b2c8f 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -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);
diff --git a/lib/gpiolib.c b/lib/gpiolib.c
index a853715..f24182e 100644
--- a/lib/gpiolib.c
+++ b/lib/gpiolib.c
@@ -28,39 +28,30 @@
 #define	extra_checks	0
 #endif

-/* gpio_lock protects the table of chips and gpio_chip->requested.
+/* gpio_lock protects the table of gpio_desc[] 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 gpio_desc[ARCH_NR_GPIOS];

 /* 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(struct gpio_desc *desc, unsigned gpio)
 {
-	int		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);
+	if (!desc->requested)
+		desc->requested_label = "(auto)";
 #endif
-
-	if (!requested)
-		printk(KERN_DEBUG "GPIO-%d autorequested\n",
-				chip->base + offset);
+	if (!desc->requested)
+		printk(KERN_DEBUG "GPIO-%d autorequested\n", gpio);
 }

 /* 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;
 }

 /**
@@ -75,26 +66,25 @@ 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)
-		return -EINVAL;
-	if ((chip->base + chip->ngpio) >= ARCH_NR_GPIOS)
-		return -EINVAL;
-	if (chip->ngpio > ARCH_GPIOS_PER_CHIP)
+	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) {
+			spin_unlock_irqrestore(&gpio_lock, flags);
+			return -EBUSY;
+		}
+
+	for (id = chip->base; id < chip->base + chip->ngpio; id++)
+		gpio_desc[id].chip = chip;

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

@@ -107,28 +97,21 @@ 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++) {
-		if (gpiochip_is_requested(chip, offset)) {
-			status = -EBUSY;
-			break;
+	for (id = chip->base; id < chip->base + chip->ngpio; id++)
+		if (gpio_desc[id].requested) {
+			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;

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

@@ -139,17 +122,15 @@ 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,
@@ -157,18 +138,16 @@ int gpio_request(unsigned gpio, const char *label)
 	 */

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

+#ifdef CONFIG_DEBUG_FS
+	if (status == 0)
+		desc->requested_label = (label == NULL) ? "?" : label;
+#endif
 done:
 	spin_unlock_irqrestore(&gpio_lock, flags);
 	return status;
@@ -178,27 +157,23 @@ 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)
+	desc = &gpio_desc[gpio];
+
+	if (desc->chip == NULL)
 		goto done;

-#ifdef CONFIG_DEBUG_FS
-	if (chip->requested[gpio])
-		chip->requested[gpio] = NULL;
+	if (desc->requested)
+		desc->requested = 0;
 	else
-		chip = NULL;
-#else
-	if (!test_and_clear_bit(gpio, chip->requested))
-		chip = NULL;
+		WARN_ON(extra_checks);
+
+#ifdef CONFIG_DEBUG_FS
+	desc->requested_label = NULL;
 #endif
-	WARN_ON(extra_checks && chip == NULL);
 done:
 	spin_unlock_irqrestore(&gpio_lock, flags);
 }
@@ -218,17 +193,22 @@ 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;
+
+	gpio_ensure_requested(desc, gpio);
+
 	if (!chip || !chip->get || !chip->direction_input)
 		goto fail;
+
 	gpio -= chip->base;
 	if (gpio >= chip->ngpio)
 		goto fail;
-	gpio_ensure_requested(chip, gpio);

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

@@ -237,8 +217,8 @@ 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);
+	if (status)
+		desc->is_out = 0;
 	return status;
 fail:
 	spin_unlock_irqrestore(&gpio_lock, flags);
@@ -250,17 +230,22 @@ 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;
+
+	gpio_ensure_requested(desc, gpio);
+
 	if (!chip || !chip->set || !chip->direction_output)
 		goto fail;
+
 	gpio -= chip->base;
 	if (gpio >= chip->ngpio)
 		goto fail;
-	gpio_ensure_requested(chip, gpio);

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

@@ -269,8 +254,8 @@ 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);
+	if (status)
+		desc->is_out = 1;
 	return status;
 fail:
 	spin_unlock_irqrestore(&gpio_lock, flags);
@@ -363,27 +348,29 @@ EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>

-
-static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+static int gpiolib_show(struct seq_file *s, void *unused)
 {
-	unsigned	i;
+	struct gpio_chip	*chip;
+	unsigned		id;

-	for (i = 0; i < chip->ngpio; i++) {
-		unsigned	gpio;
-		int		is_out;
+	/* REVISIT this isn't locked against gpio_chip removal ... */

-		if (!chip->requested[i])
-			continue;
+	seq_printf(s, "gpio   chip_name   requested  direction  value  irq\n");

-		gpio = chip->base + i;
-		is_out = test_bit(i, chip->is_out);
+	for (id = 0; id < ARCH_NR_GPIO; id++) {
+		struct gpio_desc *desc = &gpio_desc[id];
+		struct gpio_chip *chip = desc->chip;
+
+		if (chip == NULL)
+			continue;

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

 		if (!is_out) {
 			int		irq = gpio_to_irq(gpio);
@@ -431,32 +418,6 @@ static void gpiolib_dbg_show(struct seq_file *s,
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 -------

On Nov 27, 2007 9:46 AM, David Brownell <david-b@pacbell.net> wrote:
> On Tuesday 13 November 2007, David Brownell wrote:
> > 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
>

-- 
Cheers
- eric

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-27 10:58                   ` eric miao
@ 2007-11-27 17:26                     ` David Brownell
  2007-11-27 19:03                     ` David Brownell
                                       ` (2 subsequent siblings)
  3 siblings, 0 replies; 60+ messages in thread
From: David Brownell @ 2007-11-27 17:26 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

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

On Tuesday 27 November 2007, eric miao wrote:
> 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

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-27 10:58                   ` eric miao
  2007-11-27 17:26                     ` David Brownell
@ 2007-11-27 19:03                     ` David Brownell
  2007-11-27 19:29                     ` David Brownell
  2007-11-28  3:15                     ` [patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc David Brownell
  3 siblings, 0 replies; 60+ messages in thread
From: David Brownell @ 2007-11-27 19:03 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Tuesday 27 November 2007, eric miao wrote:

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


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

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-27 10:58                   ` eric miao
  2007-11-27 17:26                     ` David Brownell
  2007-11-27 19:03                     ` David Brownell
@ 2007-11-27 19:29                     ` David Brownell
  2007-11-28  5:11                       ` eric miao
  2007-11-28  3:15                     ` [patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc David Brownell
  3 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-27 19:29 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Tuesday 27 November 2007, eric miao wrote:
>         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.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* [patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc
  2007-11-27 10:58                   ` eric miao
                                       ` (2 preceding siblings ...)
  2007-11-27 19:29                     ` David Brownell
@ 2007-11-28  3:15                     ` David Brownell
  2007-11-28  9:10                       ` eric miao
  3 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-28  3:15 UTC (permalink / raw)
  To: Linux Kernel list
  Cc: eric miao, Felipe Balbi, Bill Gatliff, Haavard Skinnemoen,
	Andrew Victor, Tony Lindgren, Jean Delvare, Kevin Hilman,
	Paul Mundt, Ben Dooks, Nicolas Pitre

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(-)

--- at91.orig/arch/arm/mach-at91/gpio.c	2007-11-27 14:31:05.000000000 -0800
+++ at91/arch/arm/mach-at91/gpio.c	2007-11-27 14:32:01.000000000 -0800
@@ -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");
--- at91.orig/arch/avr32/mach-at32ap/pio.c	2007-11-27 14:30:03.000000000 -0800
+++ at91/arch/avr32/mach-at32ap/pio.c	2007-11-27 14:30:04.000000000 -0800
@@ -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");
--- at91.orig/drivers/spi/mcp23s08.c	2007-11-27 14:29:20.000000000 -0800
+++ at91/drivers/spi/mcp23s08.c	2007-11-27 14:30:04.000000000 -0800
@@ -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");
--- at91.orig/include/asm-avr32/arch-at32ap/gpio.h	2007-11-27 14:30:03.000000000 -0800
+++ at91/include/asm-avr32/arch-at32ap/gpio.h	2007-11-27 14:30:04.000000000 -0800
@@ -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. */
--- at91.orig/include/asm-generic/gpio.h	2007-11-27 14:29:19.000000000 -0800
+++ at91/include/asm-generic/gpio.h	2007-11-27 14:30:04.000000000 -0800
@@ -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);
--- at91.orig/lib/gpiolib.c	2007-11-27 14:29:20.000000000 -0800
+++ at91/lib/gpiolib.c	2007-11-27 14:30:04.000000000 -0800
@@ -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
  * 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);
 #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 ... */
 
-	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;
 

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 1/4] GPIO implementation framework
  2007-11-27 19:29                     ` David Brownell
@ 2007-11-28  5:11                       ` eric miao
  0 siblings, 0 replies; 60+ messages in thread
From: eric miao @ 2007-11-28  5:11 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks

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(-)

diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index 869b739..34e60ba 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -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 */
diff --git a/lib/gpiolib.c b/lib/gpiolib.c
index a853715..6050af5 100644
--- a/lib/gpiolib.c
+++ b/lib/gpiolib.c
@@ -28,39 +28,30 @@
 #define	extra_checks	0
 #endif

-/* gpio_lock protects the table of chips and gpio_chip->requested.
+/* gpio_lock protects the table of gpio_desc[] 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 gpio_desc[ARCH_NR_GPIOS];

 /* 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(struct gpio_desc *desc, unsigned gpio)
 {
-	int		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);
+	if (!desc->requested)
+		desc->requested_label = "(auto)";
 #endif
-
-	if (!requested)
-		printk(KERN_DEBUG "GPIO-%d autorequested\n",
-				chip->base + offset);
+	if (!desc->requested)
+		printk(KERN_DEBUG "GPIO-%d autorequested\n", gpio);
 }

 /* 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;
 }

 /**
@@ -75,26 +66,25 @@ 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)
-		return -EINVAL;
-	if ((chip->base + chip->ngpio) >= ARCH_NR_GPIOS)
-		return -EINVAL;
-	if (chip->ngpio > ARCH_GPIOS_PER_CHIP)
+	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) {
+			spin_unlock_irqrestore(&gpio_lock, flags);
+			return -EBUSY;
+		}
+
+	for (id = chip->base; id < chip->base + chip->ngpio; id++)
+		gpio_desc[id].chip = chip;

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

@@ -107,28 +97,21 @@ 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++) {
-		if (gpiochip_is_requested(chip, offset)) {
-			status = -EBUSY;
-			break;
+	for (id = chip->base; id < chip->base + chip->ngpio; id++)
+		if (gpio_desc[id].requested) {
+			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;

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

@@ -139,17 +122,15 @@ 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,
@@ -157,18 +138,16 @@ int gpio_request(unsigned gpio, const char *label)
 	 */

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

+#ifdef CONFIG_DEBUG_FS
+	if (status == 0)
+		desc->requested_label = (label == NULL) ? "?" : label;
+#endif
 done:
 	spin_unlock_irqrestore(&gpio_lock, flags);
 	return status;
@@ -178,27 +157,23 @@ 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)
+	desc = &gpio_desc[gpio];
+
+	if (desc->chip == NULL)
 		goto done;

-#ifdef CONFIG_DEBUG_FS
-	if (chip->requested[gpio])
-		chip->requested[gpio] = NULL;
+	if (desc->requested)
+		desc->requested = 0;
 	else
-		chip = NULL;
-#else
-	if (!test_and_clear_bit(gpio, chip->requested))
-		chip = NULL;
+		WARN_ON(extra_checks);
+
+#ifdef CONFIG_DEBUG_FS
+	desc->requested_label = NULL;
 #endif
-	WARN_ON(extra_checks && chip == NULL);
 done:
 	spin_unlock_irqrestore(&gpio_lock, flags);
 }
@@ -218,17 +193,22 @@ 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;
+
+	gpio_ensure_requested(desc, gpio);
+
 	if (!chip || !chip->get || !chip->direction_input)
 		goto fail;
+
 	gpio -= chip->base;
 	if (gpio >= chip->ngpio)
 		goto fail;
-	gpio_ensure_requested(chip, gpio);

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

@@ -238,7 +218,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 +230,22 @@ 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;
+
+	gpio_ensure_requested(desc, gpio);
+
 	if (!chip || !chip->set || !chip->direction_output)
 		goto fail;
+
 	gpio -= chip->base;
 	if (gpio >= chip->ngpio)
 		goto fail;
-	gpio_ensure_requested(chip, gpio);

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

@@ -270,7 +255,7 @@ int gpio_direction_output(unsigned gpio, int value)

 	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);
@@ -363,29 +348,39 @@ EXPORT_SYMBOL_GPL(gpio_set_value_cansleep);
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>

-
-static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+static int gpiolib_show(struct seq_file *s, void *unused)
 {
-	unsigned	i;
+	unsigned		gpio = 0;
+	unsigned		offset;
+
+	/* REVISIT this isn't locked against gpio_chip removal ... */

-	for (i = 0; i < chip->ngpio; i++) {
-		unsigned	gpio;
-		int		is_out;
+	seq_printf(s, "gpio   chip_name    requested  direction  value  irq\n");

-		if (!chip->requested[i])
+	while (gpio < ARCH_NR_GPIOS) {
+		struct gpio_desc *desc = &gpio_desc[gpio];
+		struct gpio_chip *chip = desc->chip;
+
+		if (chip == NULL)
 			continue;

-		gpio = chip->base + i;
-		is_out = test_bit(i, chip->is_out);
+		if (chip->dbg_show) {
+			chip->dbg_show(s, chip);
+			gpio += chip->ngpio;
+			continue;
+		}
+
+		offset = gpio - chip->base;

-		seq_printf(s, " gpio-%-3d (%-12s) %s %s",
-			gpio, chip->requested[i],
-			is_out ? "out" : "in ",
-			chip->get
-				? (chip->get(chip, i) ? "hi" : "lo")
-				: "?  ");
+		seq_printf(s, " %3d %11.11s  %10.10s  %9.9s  %5.5s", gpio,
+				chip->label,
+				desc->requested_label,
+				(desc->is_out) ? "out" : "in",
+				(chip->get) ?
+					(chip->get(chip, offset) ? "hi" : "lo")
+					: "?");

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

@@ -430,32 +425,7 @@ static void gpiolib_dbg_show(struct seq_file *s,
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;
 }
-- 
1.5.2.5.GIT



On Nov 28, 2007 3:29 AM, David Brownell <david-b@pacbell.net> wrote:
> On Tuesday 27 November 2007, eric miao wrote:
> > 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.
>



-- 
Cheers
- eric

^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc
  2007-11-28  3:15                     ` [patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc David Brownell
@ 2007-11-28  9:10                       ` eric miao
  2007-11-28  9:53                         ` David Brownell
  0 siblings, 1 reply; 60+ messages in thread
From: eric miao @ 2007-11-28  9:10 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks, Nicolas Pitre

On Nov 28, 2007 11:15 AM, David Brownell <david-b@pacbell.net> wrote:
> 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(-)
>
> --- at91.orig/arch/arm/mach-at91/gpio.c 2007-11-27 14:31:05.000000000 -0800
> +++ at91/arch/arm/mach-at91/gpio.c      2007-11-27 14:32:01.000000000 -0800
> @@ -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");
> --- at91.orig/arch/avr32/mach-at32ap/pio.c      2007-11-27 14:30:03.000000000 -0800
> +++ at91/arch/avr32/mach-at32ap/pio.c   2007-11-27 14:30:04.000000000 -0800
> @@ -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");
> --- at91.orig/drivers/spi/mcp23s08.c    2007-11-27 14:29:20.000000000 -0800
> +++ at91/drivers/spi/mcp23s08.c 2007-11-27 14:30:04.000000000 -0800
> @@ -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");
> --- at91.orig/include/asm-avr32/arch-at32ap/gpio.h      2007-11-27 14:30:03.000000000 -0800
> +++ at91/include/asm-avr32/arch-at32ap/gpio.h   2007-11-27 14:30:04.000000000 -0800
> @@ -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. */
> --- at91.orig/include/asm-generic/gpio.h        2007-11-27 14:29:19.000000000 -0800
> +++ at91/include/asm-generic/gpio.h     2007-11-27 14:30:04.000000000 -0800
> @@ -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);
> --- at91.orig/lib/gpiolib.c     2007-11-27 14:29:20.000000000 -0800
> +++ at91/lib/gpiolib.c  2007-11-27 14:30:04.000000000 -0800
> @@ -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
>   * 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??

>  #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 ... */
>
> -       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.

-- 
Cheers
- eric

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc
  2007-11-28  9:10                       ` eric miao
@ 2007-11-28  9:53                         ` David Brownell
  0 siblings, 0 replies; 60+ messages in thread
From: David Brownell @ 2007-11-28  9:53 UTC (permalink / raw)
  To: eric miao
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, Jean Delvare,
	Kevin Hilman, Paul Mundt, Ben Dooks, Nicolas Pitre

On Wednesday 28 November 2007, eric miao wrote:
> > +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.


> and maybe you mean desc - gpio_desc??

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


> Except for the above, I feel OK overall.


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2007-10-30  1:51 ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver David Brownell
@ 2007-11-30 12:32   ` Jean Delvare
  2007-11-30 13:04     ` Bill Gatliff
  2007-11-30 18:40     ` David Brownell
  0 siblings, 2 replies; 60+ messages in thread
From: Jean Delvare @ 2007-11-30 12:32 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, eric miao,
	Kevin Hilman, Paul Mundt, Ben Dooks

Hi David,

Sorry for the late review.

Note that I can't test your code.

On Mon, 29 Oct 2007 18:51:48 -0700, David Brownell wrote:
> 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.
> 
> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
> ---
> Note that there's currently a drivers/i2c/chips/pcf8574.c driver.

By now there's also a drivers/i2c/chips/pcf8575.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(+)
> 
> --- a/drivers/i2c/chips/Kconfig	2007-10-28 21:04:06.000000000 -0700
> +++ b/drivers/i2c/chips/Kconfig	2007-10-29 14:16:01.000000000 -0700
> @@ -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.

How?

> +
>  config SENSORS_PCF8574
>  	tristate "Philips PCF8574 and PCF8574A"
>  	depends on EXPERIMENTAL
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ b/include/linux/pcf857x.h	2007-10-28 21:09:49.000000000 -0700
> @@ -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.

> + * @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.

> + *
> + * 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.
> + *
> + * 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.

> + * 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.

> + */
> +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 */
> --- a/drivers/i2c/chips/Makefile	2007-10-28 21:04:06.000000000 -0700
> +++ b/drivers/i2c/chips/Makefile	2007-10-28 21:09:49.000000000 -0700
> @@ -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.

>  obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
>  obj-$(CONFIG_TPS65010)		+= tps65010.o
>  obj-$(CONFIG_SENSORS_TLV320AIC23) += tlv320aic23.o
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ b/drivers/i2c/chips/pcf857x.c	2007-10-29 14:12:21.000000000 -0700
> @@ -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.

> + *
> + * 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/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?

> +
> +#include <asm/gpio.h>
> +
> +
> +/*
> + * The pcf857x chips only expose a one read register and one write register.

Typo: extra "a".

> + * 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 */
> +};
> +
> +/*-------------------------------------------------------------------------*/
> +
> +/* 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.

> +}
> +
> +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

> +}
> +
> +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.

> +}
> +
> +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.

> +
> +/*-------------------------------------------------------------------------*/
> +
> +/* 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.

> +	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)
> +{
> +	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.

> +
> +	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.

> +	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
> +			|| 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...

> +
> +		/* fail if there's no chip present */
> +		status = i2c_smbus_read_byte(client);

... but overwrite it immediately.

> +
> +	/* '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;
> +		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.

> +
> +	} 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.

> +	}
> +
> +	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;
> +	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?

> +			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");

No MODULE_AUTHOR? No entry in MAINTAINERS?

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2007-11-30 12:32   ` Jean Delvare
@ 2007-11-30 13:04     ` Bill Gatliff
  2007-11-30 13:36       ` Jean Delvare
  2007-11-30 18:40     ` David Brownell
  1 sibling, 1 reply; 60+ messages in thread
From: Bill Gatliff @ 2007-11-30 13:04 UTC (permalink / raw)
  To: Jean Delvare
  Cc: David Brownell, Linux Kernel list, Felipe Balbi,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, eric miao,
	Kevin Hilman, Paul Mundt, Ben Dooks

Jean Delvare wrote:
> !!(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.

-- 
Bill Gatliff
bgat@billgatliff.com


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2007-11-30 13:04     ` Bill Gatliff
@ 2007-11-30 13:36       ` Jean Delvare
  2007-11-30 14:09         ` Bill Gatliff
  0 siblings, 1 reply; 60+ messages in thread
From: Jean Delvare @ 2007-11-30 13:36 UTC (permalink / raw)
  To: Bill Gatliff
  Cc: David Brownell, Linux Kernel list, Felipe Balbi,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, eric miao,
	Kevin Hilman, Paul Mundt, Ben Dooks

Hi Bill,

On Fri, 30 Nov 2007 07:04:10 -0600, Bill Gatliff wrote:
> Jean Delvare wrote:
> > !!(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 :)

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2007-11-30 13:36       ` Jean Delvare
@ 2007-11-30 14:09         ` Bill Gatliff
  0 siblings, 0 replies; 60+ messages in thread
From: Bill Gatliff @ 2007-11-30 14:09 UTC (permalink / raw)
  To: Jean Delvare
  Cc: David Brownell, Linux Kernel list, Felipe Balbi,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, eric miao,
	Kevin Hilman, Paul Mundt, Ben Dooks

Jean Delvare wrote:
> Hi Bill,
>
> On Fri, 30 Nov 2007 07:04:10 -0600, Bill Gatliff wrote:
>   
>> Jean Delvare wrote:
>>     
>>> !!(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?
>   

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

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


b.g.

-- 
Bill Gatliff
bgat@billgatliff.com


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2007-11-30 12:32   ` Jean Delvare
  2007-11-30 13:04     ` Bill Gatliff
@ 2007-11-30 18:40     ` David Brownell
  2007-11-30 20:13       ` Jean Delvare
  2007-12-06  3:03       ` [patch/rfc 2/4] pcf857x " David Brownell
  1 sibling, 2 replies; 60+ messages in thread
From: David Brownell @ 2007-11-30 18:40 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, eric miao,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Friday 30 November 2007, Jean Delvare wrote:
> 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


> > --- a/drivers/i2c/chips/Kconfig	2007-10-28 21:04:06.000000000 -0700
> > +++ b/drivers/i2c/chips/Kconfig	2007-10-29 14:16:01.000000000 -0700
> > @@ -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
> > +	  ...
> > +
> > +	  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.


> > +/**
> > + * 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.)


> 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).


> > --- a/drivers/i2c/chips/Makefile	2007-10-28 21:04:06.000000000 -0700
> > +++ b/drivers/i2c/chips/Makefile	2007-10-28 21:09:49.000000000 -0700
> > @@ -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.  ;)


> >  obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
> >  obj-$(CONFIG_TPS65010)		+= tps65010.o
> >  obj-$(CONFIG_SENSORS_TLV320AIC23) += tlv320aic23.o
> > --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> > +++ b/drivers/i2c/chips/pcf857x.c	2007-10-29 14:12:21.000000000 -0700
> > @@ -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.


> > +#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.


> > +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.


> > +                     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.  ;)

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2007-11-30 18:40     ` David Brownell
@ 2007-11-30 20:13       ` Jean Delvare
  2007-11-30 20:59         ` David Brownell
  2007-12-06  3:03       ` [patch/rfc 2/4] pcf857x " David Brownell
  1 sibling, 1 reply; 60+ messages in thread
From: Jean Delvare @ 2007-11-30 20:13 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, eric miao,
	Kevin Hilman, Paul Mundt, Ben Dooks

Hi David,

On Fri, 30 Nov 2007 10:40:54 -0800, David Brownell wrote:
> On Friday 30 November 2007, Jean Delvare wrote:
> > > --- a/drivers/i2c/chips/Kconfig	2007-10-28 21:04:06.000000000 -0700
> > > +++ b/drivers/i2c/chips/Kconfig	2007-10-29 14:16:01.000000000 -0700
> > > @@ -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
> > > +	  ...
> > > +
> > > +	  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.

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

> > > +/**
> > > + * 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.)
> 
> 
> > 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).

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.

> > > --- a/drivers/i2c/chips/Makefile	2007-10-28 21:04:06.000000000 -0700
> > > +++ b/drivers/i2c/chips/Makefile	2007-10-28 21:09:49.000000000 -0700
> > > @@ -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.  ;)

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.

> > > +#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.

> > > +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.

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

> > > +                     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.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2007-11-30 20:13       ` Jean Delvare
@ 2007-11-30 20:59         ` David Brownell
  2008-04-04  2:06           ` Trent Piepho
  0 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-11-30 20:59 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, eric miao,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Friday 30 November 2007, Jean Delvare wrote:
>
> 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.


> > > > +#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.)


> 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

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf857x I2C GPIO expander driver
  2007-11-30 18:40     ` David Brownell
  2007-11-30 20:13       ` Jean Delvare
@ 2007-12-06  3:03       ` David Brownell
  2007-12-06 23:17         ` Jean Delvare
  1 sibling, 1 reply; 60+ messages in thread
From: David Brownell @ 2007-12-06  3:03 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, eric miao,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Friday 30 November 2007, David Brownell wrote:
> 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.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
 drivers/gpio/Kconfig        |   23 +++
 drivers/gpio/Makefile       |    2 
 drivers/gpio/pcf857x.c      |  331 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c/pcf857x.h |   45 +++++
 4 files changed, 401 insertions(+)

--- a/drivers/gpio/Kconfig	2007-12-05 15:13:27.000000000 -0800
+++ b/drivers/gpio/Kconfig	2007-12-05 15:14:12.000000000 -0800
@@ -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
--- a/drivers/gpio/Makefile	2007-12-05 15:14:03.000000000 -0800
+++ b/drivers/gpio/Makefile	2007-12-05 15:14:12.000000000 -0800
@@ -1 +1,3 @@
 # gpio support: dedicated expander chips, etc
+
+obj-$(CONFIG_GPIO_PCF857X)	+= pcf857x.o
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/gpio/pcf857x.c	2007-12-05 15:15:18.000000000 -0800
@@ -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>
+
+
+/*
+ * 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
+ * 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);
+	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));
+}
+
+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)
+{
+	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, };
+	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;
+
+	/* 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.
+	 */
+	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)" : "");
+
+	/* 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);
+	}
+
+	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");
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ b/include/linux/i2c/pcf857x.h	2007-12-05 15:14:12.000000000 -0800
@@ -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;
+
+	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 */

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf857x I2C GPIO expander driver
  2007-12-06  3:03       ` [patch/rfc 2/4] pcf857x " David Brownell
@ 2007-12-06 23:17         ` Jean Delvare
  2007-12-07  4:02           ` David Brownell
  0 siblings, 1 reply; 60+ messages in thread
From: Jean Delvare @ 2007-12-06 23:17 UTC (permalink / raw)
  To: David Brownell
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, eric miao,
	Kevin Hilman, Paul Mundt, Ben Dooks

Hi David,

On Wed, 5 Dec 2007 19:03:12 -0800, David Brownell wrote:
> On Friday 30 November 2007, David Brownell wrote:
> > 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.

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.

> 
> 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.
> 
> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
> ---
>  drivers/gpio/Kconfig        |   23 +++
>  drivers/gpio/Makefile       |    2 
>  drivers/gpio/pcf857x.c      |  331 ++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/i2c/pcf857x.h |   45 +++++
>  4 files changed, 401 insertions(+)
> 
> --- a/drivers/gpio/Kconfig	2007-12-05 15:13:27.000000000 -0800
> +++ b/drivers/gpio/Kconfig	2007-12-05 15:14:12.000000000 -0800
> @@ -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
> --- a/drivers/gpio/Makefile	2007-12-05 15:14:03.000000000 -0800
> +++ b/drivers/gpio/Makefile	2007-12-05 15:14:12.000000000 -0800
> @@ -1 +1,3 @@
>  # gpio support: dedicated expander chips, etc
> +
> +obj-$(CONFIG_GPIO_PCF857X)	+= pcf857x.o
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ b/drivers/gpio/pcf857x.c	2007-12-05 15:15:18.000000000 -0800
> @@ -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>
> +
> +
> +/*
> + * 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?

> + * 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);
> +	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.)

> +}
> +
> +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)
> +{
> +	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.

> +	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;
> +
> +	/* 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.

> +	 */
> +	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)" : "");
> +
> +	/* 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.)

> +	}
> +
> +	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");
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ b/include/linux/i2c/pcf857x.h	2007-12-05 15:14:12.000000000 -0800
> @@ -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;
> +
> +	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.

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf857x I2C GPIO expander driver
  2007-12-06 23:17         ` Jean Delvare
@ 2007-12-07  4:02           ` David Brownell
  0 siblings, 0 replies; 60+ messages in thread
From: David Brownell @ 2007-12-07  4:02 UTC (permalink / raw)
  To: Jean Delvare
  Cc: Linux Kernel list, Felipe Balbi, Bill Gatliff,
	Haavard Skinnemoen, Andrew Victor, Tony Lindgren, eric miao,
	Kevin Hilman, Paul Mundt, Ben Dooks

On Thursday 06 December 2007, Jean Delvare wrote:

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


> > +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.  :)


> > +	/* 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.)

Good point.


> The rest looks fine to me.

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

- Dave


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2007-11-30 20:59         ` David Brownell
@ 2008-04-04  2:06           ` Trent Piepho
  2008-04-04  2:45             ` Ben Nizette
  2008-04-04  8:09             ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver Jean Delvare
  0 siblings, 2 replies; 60+ messages in thread
From: Trent Piepho @ 2008-04-04  2:06 UTC (permalink / raw)
  To: David Brownell; +Cc: Jean Delvare, Linux Kernel list

On Fri, 30 Nov 2007, David Brownell wrote:
> On Friday 30 November 2007, Jean Delvare wrote:
>>
>> 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 <tpiepho@freescale.com>
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.

Signed-off-by: Trent Piepho <tpiepho@freescale.com>
---
  drivers/gpio/gpiolib.c     |  188 ++++++++++++++++++++++++++++++++++++++++++++
  include/asm-generic/gpio.h |    2 +
  2 files changed, 190 insertions(+), 0 deletions(-)

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index d8db2f8..db0677d 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -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)
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index f29a502..b2a5262 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -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;
-- 
1.5.4.1


^ permalink raw reply related	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2008-04-04  2:06           ` Trent Piepho
@ 2008-04-04  2:45             ` Ben Nizette
  2008-04-04  3:33               ` Trent Piepho
  2008-04-04  8:09             ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver Jean Delvare
  1 sibling, 1 reply; 60+ messages in thread
From: Ben Nizette @ 2008-04-04  2:45 UTC (permalink / raw)
  To: Trent Piepho; +Cc: David Brownell, Jean Delvare, Linux Kernel list


On Thu, 2008-04-03 at 19:06 -0700, Trent Piepho wrote:
> On Fri, 30 Nov 2007, David Brownell wrote:
> > On Friday 30 November 2007, Jean Delvare wrote:
> >>
> >> 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 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.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2008-04-04  2:45             ` Ben Nizette
@ 2008-04-04  3:33               ` Trent Piepho
  2008-04-04  4:57                 ` Ben Nizette
  0 siblings, 1 reply; 60+ messages in thread
From: Trent Piepho @ 2008-04-04  3:33 UTC (permalink / raw)
  To: Ben Nizette; +Cc: David Brownell, Jean Delvare, Linux Kernel list

On Fri, 4 Apr 2008, Ben Nizette wrote:
> On Thu, 2008-04-03 at 19:06 -0700, Trent Piepho wrote:
>> On Fri, 30 Nov 2007, David Brownell wrote:
>>> On Friday 30 November 2007, Jean Delvare wrote:
>>>>
>>>> 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 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.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2008-04-04  3:33               ` Trent Piepho
@ 2008-04-04  4:57                 ` Ben Nizette
  2008-04-05  4:05                   ` userspace GPIO access (WAS: [patch/rfc 2/4] pcf875x ...) David Brownell
  0 siblings, 1 reply; 60+ messages in thread
From: Ben Nizette @ 2008-04-04  4:57 UTC (permalink / raw)
  To: Trent Piepho
  Cc: David Brownell, Jean Delvare, Linux Kernel list, Mike Frysinger


On Thu, 2008-04-03 at 20:33 -0700, Trent Piepho wrote:

> 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 :-)

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

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


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2008-04-04  2:06           ` Trent Piepho
  2008-04-04  2:45             ` Ben Nizette
@ 2008-04-04  8:09             ` Jean Delvare
  2008-04-04 19:07               ` Trent Piepho
  2008-04-05  2:53               ` David Brownell
  1 sibling, 2 replies; 60+ messages in thread
From: Jean Delvare @ 2008-04-04  8:09 UTC (permalink / raw)
  To: Trent Piepho; +Cc: David Brownell, Linux Kernel list

Hi Trent,

On Thu, 3 Apr 2008 19:06:27 -0700 (PDT), Trent Piepho wrote:
> On Fri, 30 Nov 2007, David Brownell wrote:
> > On Friday 30 November 2007, Jean Delvare wrote:
> >>
> >> 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 <tpiepho@freescale.com>
> 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.

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

> 
> Signed-off-by: Trent Piepho <tpiepho@freescale.com>
> ---
>   drivers/gpio/gpiolib.c     |  188 ++++++++++++++++++++++++++++++++++++++++++++
>   include/asm-generic/gpio.h |    2 +
>   2 files changed, 190 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
> index d8db2f8..db0677d 100644
> --- a/drivers/gpio/gpiolib.c
> +++ b/drivers/gpio/gpiolib.c
> @@ -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;

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

> +}
> +
> +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.

> +	} 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.

> +	}
> +
> +	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? */

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

> +	}
> +	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.

> +}
> +
> +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.

> +	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;

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

> +	} 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 */

Oh yes it really should ;)

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

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

> +		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)
> diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
> index f29a502..b2a5262 100644
> --- a/include/asm-generic/gpio.h
> +++ b/include/asm-generic/gpio.h
> @@ -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.

>    * @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;


-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2008-04-04  8:09             ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver Jean Delvare
@ 2008-04-04 19:07               ` Trent Piepho
  2008-04-04 19:36                 ` Jean Delvare
  2008-04-05  2:51                 ` David Brownell
  2008-04-05  2:53               ` David Brownell
  1 sibling, 2 replies; 60+ messages in thread
From: Trent Piepho @ 2008-04-04 19:07 UTC (permalink / raw)
  To: Jean Delvare; +Cc: David Brownell, Linux Kernel list

On Fri, 4 Apr 2008, Jean Delvare wrote:
> On Thu, 3 Apr 2008 19:06:27 -0700 (PDT), Trent Piepho wrote:
>> From c65d0fb239b79de7f595e47edb2fb641217e7309 Mon Sep 17 00:00:00 2001
>> From: Trent Piepho <tpiepho@freescale.com>
>> 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.
>>
>
> Purely technical review (I can't say whether this interface is better
> than what has been proposed elsewhere or not):
>
>> +	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?

>> +	} 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.

>> +	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.

>> +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?

>> @@ -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.

>>    *	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.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2008-04-04 19:07               ` Trent Piepho
@ 2008-04-04 19:36                 ` Jean Delvare
  2008-04-04 20:18                   ` Trent Piepho
  2008-04-05  2:51                 ` David Brownell
  1 sibling, 1 reply; 60+ messages in thread
From: Jean Delvare @ 2008-04-04 19:36 UTC (permalink / raw)
  To: Trent Piepho; +Cc: David Brownell, Linux Kernel list

On Fri, 4 Apr 2008 12:07:12 -0700 (PDT), Trent Piepho wrote:
> On Fri, 4 Apr 2008, Jean Delvare wrote:
> > On Thu, 3 Apr 2008 19:06:27 -0700 (PDT), Trent Piepho wrote:
> >> +	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.

> (...)
> >> +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.

> (...)
> >> @@ -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.

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

-- 
Jean Delvare

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2008-04-04 19:36                 ` Jean Delvare
@ 2008-04-04 20:18                   ` Trent Piepho
  0 siblings, 0 replies; 60+ messages in thread
From: Trent Piepho @ 2008-04-04 20:18 UTC (permalink / raw)
  To: Jean Delvare; +Cc: David Brownell, Linux Kernel list

On Fri, 4 Apr 2008, Jean Delvare wrote:
> On Fri, 4 Apr 2008 12:07:12 -0700 (PDT), Trent Piepho wrote:
>> On Fri, 4 Apr 2008, Jean Delvare wrote:
>>> On Thu, 3 Apr 2008 19:06:27 -0700 (PDT), Trent Piepho wrote:
>>>> +	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

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.

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

>>>> +	/* 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.

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.

^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2008-04-04 19:07               ` Trent Piepho
  2008-04-04 19:36                 ` Jean Delvare
@ 2008-04-05  2:51                 ` David Brownell
  1 sibling, 0 replies; 60+ messages in thread
From: David Brownell @ 2008-04-05  2:51 UTC (permalink / raw)
  To: Trent Piepho; +Cc: Jean Delvare, Linux Kernel list

On Friday 04 April 2008, Trent Piepho wrote:
> >> +    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...


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


> 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


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



^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: [patch/rfc 2/4] pcf875x I2C GPIO expander driver
  2008-04-04  8:09             ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver Jean Delvare
  2008-04-04 19:07               ` Trent Piepho
@ 2008-04-05  2:53               ` David Brownell
  1 sibling, 0 replies; 60+ messages in thread
From: David Brownell @ 2008-04-05  2:53 UTC (permalink / raw)
  To: Jean Delvare; +Cc: Trent Piepho, Linux Kernel list

On Friday 04 April 2008, Jean Delvare wrote:
> > +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.
> 
> > +     } 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.

> 
> > +     }
> > +
> > +     if (d)
> > +             gpio_direction_output(n, 0);
> > +     else
> > +             gpio_direction_input(n);
> > +
> > +     return size;
> > +}



^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: userspace GPIO access (WAS: [patch/rfc 2/4] pcf875x ...)
  2008-04-04  4:57                 ` Ben Nizette
@ 2008-04-05  4:05                   ` David Brownell
  2008-04-07 17:56                     ` Trent Piepho
  0 siblings, 1 reply; 60+ messages in thread
From: David Brownell @ 2008-04-05  4:05 UTC (permalink / raw)
  To: Ben Nizette; +Cc: Trent Piepho, Jean Delvare, Linux Kernel list, Mike Frysinger

On Thursday 03 April 2008, Ben Nizette wrote:
> 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


^ permalink raw reply	[flat|nested] 60+ messages in thread

* Re: userspace GPIO access (WAS: [patch/rfc 2/4] pcf875x ...)
  2008-04-05  4:05                   ` userspace GPIO access (WAS: [patch/rfc 2/4] pcf875x ...) David Brownell
@ 2008-04-07 17:56                     ` Trent Piepho
  0 siblings, 0 replies; 60+ messages in thread
From: Trent Piepho @ 2008-04-07 17:56 UTC (permalink / raw)
  To: David Brownell
  Cc: Ben Nizette, Trent Piepho, Jean Delvare, Linux Kernel list,
	Mike Frysinger

On Fri, 4 Apr 2008, David Brownell wrote:
>
> - 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.

>   (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. 
>From my understanding of sysfs, it seems any sysfs based approach has to
be based on a device.

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

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

^ permalink raw reply	[flat|nested] 60+ messages in thread

end of thread, other threads:[~2008-04-07 18:02 UTC | newest]

Thread overview: 60+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <200710291809.29936.david-b@pacbell.net>
2007-10-30  1:51 ` [patch/rfc 1/4] GPIO implementation framework David Brownell
2007-11-05 21:05   ` David Brownell
2007-11-13  2:28     ` eric miao
2007-11-13 19:06       ` David Brownell
2007-11-14  0:57         ` eric miao
2007-11-14  1:00           ` eric miao
2007-11-14  1:02             ` eric miao
2007-11-14  1:03               ` eric miao
2007-11-14  1:04                 ` eric miao
2007-11-14  1:04                   ` eric miao
2007-11-14  4:36                     ` David Brownell
2007-11-14  6:51                       ` eric miao
2007-11-14  7:19                         ` David Brownell
2007-11-14  7:36                           ` eric miao
2007-11-17 10:38                       ` Jean Delvare
2007-11-17 17:36                         ` David Brownell
2007-11-20 15:20                           ` Jean Delvare
2007-11-14  4:18                 ` David Brownell
2007-11-14  6:46                   ` eric miao
2007-11-14  3:28               ` David Brownell
2007-11-14  3:25             ` David Brownell
2007-11-14  3:53               ` David Brownell
2007-11-14  6:37               ` eric miao
2007-11-14  3:30           ` David Brownell
2007-11-14  6:40             ` eric miao
2007-11-14  7:08               ` David Brownell
2007-11-27  1:46                 ` David Brownell
2007-11-27 10:58                   ` eric miao
2007-11-27 17:26                     ` David Brownell
2007-11-27 19:03                     ` David Brownell
2007-11-27 19:29                     ` David Brownell
2007-11-28  5:11                       ` eric miao
2007-11-28  3:15                     ` [patch/rfc 2.6.24-rc3-mm] gpiolib grows a gpio_desc David Brownell
2007-11-28  9:10                       ` eric miao
2007-11-28  9:53                         ` David Brownell
2007-10-30  1:51 ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver David Brownell
2007-11-30 12:32   ` Jean Delvare
2007-11-30 13:04     ` Bill Gatliff
2007-11-30 13:36       ` Jean Delvare
2007-11-30 14:09         ` Bill Gatliff
2007-11-30 18:40     ` David Brownell
2007-11-30 20:13       ` Jean Delvare
2007-11-30 20:59         ` David Brownell
2008-04-04  2:06           ` Trent Piepho
2008-04-04  2:45             ` Ben Nizette
2008-04-04  3:33               ` Trent Piepho
2008-04-04  4:57                 ` Ben Nizette
2008-04-05  4:05                   ` userspace GPIO access (WAS: [patch/rfc 2/4] pcf875x ...) David Brownell
2008-04-07 17:56                     ` Trent Piepho
2008-04-04  8:09             ` [patch/rfc 2/4] pcf875x I2C GPIO expander driver Jean Delvare
2008-04-04 19:07               ` Trent Piepho
2008-04-04 19:36                 ` Jean Delvare
2008-04-04 20:18                   ` Trent Piepho
2008-04-05  2:51                 ` David Brownell
2008-04-05  2:53               ` David Brownell
2007-12-06  3:03       ` [patch/rfc 2/4] pcf857x " David Brownell
2007-12-06 23:17         ` Jean Delvare
2007-12-07  4:02           ` David Brownell
2007-10-30  1:53 ` [patch/rfc 3/4] DaVinci platform uses new GPIOLIB David Brownell
2007-10-30  1:54 ` [patch/rfc 4/4] DaVinci EVM uses pcf857x GPIO driver David Brownell

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).