Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2] Input: omap4-keypad: Add pinctrl support
From: Linus Walleij @ 2012-10-28 20:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <4099134.xWUIfbbahk@dtor-d630.eng.vmware.com>

On Wed, Oct 24, 2012 at 7:28 PM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:

>> drivers/spi/spi-pl022.c
>
> Default/sleep transitions could be moved into bus code.

No that's not a good idea as long as we have both the platform bus
and the AMBA bus doing essentially the same thing. We will then be
having two copies of the same code in two different busses running
out of sync. There may be other busses too.

But I could prepare static helpers in <linux/pinctrl/consumer.h>
that any bus could use. Or any driver. Probably any driver,
because of this:

As noted the bus cannot really execute the pinctrl calls to
e.g. put a drivers pins into "sleep". Since if e.g. the bus is walking
the suspend() ladder, shall it put the pins into sleep *before*
or *after* calling the suspend() hook in the driver?

The answer is that it does not know. Because drivers have
different needs. Depending on how the hardware and
system is done.

I already tried to make this point:

pinctrl_set_state(state_sleep);
clk_disable();
power_off_voltage_domain();

May for some drivers have to be:

clk_disable();
power_off_voltage_domain();
pinctrl_set_state(state_sleep);

(etc)

I'm not making this up, it is a very real phenomenon on the
Ux500 and I don't think we are unique.

Moving this handling to bus code or anywhere else
invariably implies that resource acquisition/release order
does not matter, and my point is that it does.

>> drivers/i2c/busses/i2c-nomadik.c
>
> Don't see pinctrl in linux-next.

This code is here:
http://marc.info/?l=linux-i2c&m=134986995731695&w=2

Yours,
Linus Walleij

^ permalink raw reply

* [PATCH] spi/pl022: Activate resourses before deactivate them in suspend
From: Ulf Hansson @ 2012-10-28 20:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACRpkdbCfxnSSLR6GoSbAxtfnwOivxCCrLtwnR=9tpwpfACqTQ@mail.gmail.com>

On 28 October 2012 20:52, Linus Walleij <linus.walleij@linaro.org> wrote:
> On Sat, Oct 27, 2012 at 11:46 PM, Mark Brown
> <broonie@opensource.wolfsonmicro.com> wrote:
>> On Fri, Oct 05, 2012 at 09:43:32AM +0200, Ulf Hansson wrote:
>>
>>> To be able to deactivate resourses in suspend, the resourses must
>>> first be surely active. This is done with a pm_runtime_get_sync.
>>> Once the resourses are restored to active state again in resume,
>>> the runtime pm usage count can be decreased with a pm_runtime_put.
>>
>> The PM core will ensure devices are runtime resumed before we enter
>> suspend precisely due to this sort of issue.
>
> I asked the very same question to Ulf (in speech, sorry
> so you couldn't see it...)
>
> So I guess we are talking about drivers/base/main.c
>
> in device_prepare()
> pm_runtime_get_noresume() is called

This will increase the "usage_counter" for the device. It will not
"runtime_resume" the device, though it will prevent it from being
"runtime_suspended".

> and in device_complete()
> pm_runtime_put_sync() is called.
>

> Both put into current for in
> commit 88d26136a256576e444db312179e17af6dd0ea87
> on sep 19th.
>
> Yes it seems like it will do the job.
>
> Ulf can you comment on this...
>
> Yours,
> Linus Walleij

Kind regards
Ulf Hansson

^ permalink raw reply

* [PATCH] pinctrl/nomadik: db8500: fix kp pin group
From: Linus Walleij @ 2012-10-28 20:29 UTC (permalink / raw)
  To: linux-arm-kernel

From: Patrice Chotard <patrice.chotard@stericsson.com>

kp_a_2 pin group was defined but was not declared
as a group of kp function.

Signed-off-by: Patrice Chotard <patrice.chotard@stericsson.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/pinctrl/pinctrl-nomadik-db8500.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/pinctrl/pinctrl-nomadik-db8500.c b/drivers/pinctrl/pinctrl-nomadik-db8500.c
index ef6f26d..6de52e7 100644
--- a/drivers/pinctrl/pinctrl-nomadik-db8500.c
+++ b/drivers/pinctrl/pinctrl-nomadik-db8500.c
@@ -691,6 +691,7 @@ static const struct nmk_pingroup nmk_db8500_groups[] = {
 	DB8500_PIN_GROUP(i2c0_a_1, NMK_GPIO_ALT_A),
 	DB8500_PIN_GROUP(ipgpio0_a_1, NMK_GPIO_ALT_A),
 	DB8500_PIN_GROUP(ipgpio1_a_1, NMK_GPIO_ALT_A),
+	DB8500_PIN_GROUP(kp_a_2, NMK_GPIO_ALT_A),
 	DB8500_PIN_GROUP(msp2sck_a_1, NMK_GPIO_ALT_A),
 	DB8500_PIN_GROUP(msp2_a_1, NMK_GPIO_ALT_A),
 	DB8500_PIN_GROUP(mc4_a_1, NMK_GPIO_ALT_A),
@@ -834,7 +835,7 @@ DB8500_FUNC_GROUPS(msp1, "msp1txrx_a_1", "msp1_a_1", "msp1txrx_b_1");
 DB8500_FUNC_GROUPS(lcdb, "lcdb_a_1");
 DB8500_FUNC_GROUPS(lcd, "lcdvsi0_a_1", "lcdvsi1_a_1", "lcd_d0_d7_a_1",
 	"lcd_d8_d11_a_1", "lcd_d12_d23_a_1", "lcd_b_1");
-DB8500_FUNC_GROUPS(kp, "kp_a_1", "kp_b_1", "kp_b_2", "kp_c_1", "kp_oc1_1");
+DB8500_FUNC_GROUPS(kp, "kp_a_1", "kp_a_2", "kp_b_1", "kp_b_2", "kp_c_1", "kp_oc1_1");
 DB8500_FUNC_GROUPS(mc2, "mc2_a_1", "mc2rstn_c_1");
 DB8500_FUNC_GROUPS(ssp1, "ssp1_a_1");
 DB8500_FUNC_GROUPS(ssp0, "ssp0_a_1");
-- 
1.7.11.3

^ permalink raw reply related

* [PATCH 1/3] ARM: LPC32xx: Remove superfluous irq_alloc_descs()
From: Roland Stigge @ 2012-10-28 20:35 UTC (permalink / raw)
  To: linux-arm-kernel

This patch removes the call to irq_alloc_descs() which always returns an error
since the descriptors are always preallocated already.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---
 arch/arm/mach-lpc32xx/irq.c |   10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

--- linux-2.6.orig/arch/arm/mach-lpc32xx/irq.c
+++ linux-2.6/arch/arm/mach-lpc32xx/irq.c
@@ -412,7 +412,6 @@ static const struct of_device_id mic_of_
 void __init lpc32xx_init_irq(void)
 {
 	unsigned int i;
-	int irq_base;
 
 	/* Setup MIC */
 	__raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_MIC_BASE));
@@ -475,15 +474,8 @@ void __init lpc32xx_init_irq(void)
 
 	of_irq_init(mic_of_match);
 
-	irq_base = irq_alloc_descs(-1, 0, NR_IRQS, 0);
-	if (irq_base < 0) {
-		pr_warn("Cannot allocate irq_descs, assuming pre-allocated\n");
-		irq_base = 0;
-	}
-
 	lpc32xx_mic_domain = irq_domain_add_legacy(lpc32xx_mic_np, NR_IRQS,
-						   irq_base, 0,
-						   &irq_domain_simple_ops,
+						   0, 0, &irq_domain_simple_ops,
 						   NULL);
 	if (!lpc32xx_mic_domain)
 		panic("Unable to add MIC irq domain\n");

^ permalink raw reply

* [PATCH 2/3] ARM: LPC32xx: Relocate calls to irq_set_chained_handler()
From: Roland Stigge @ 2012-10-28 20:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351456522-22309-1-git-send-email-stigge@antcom.de>

This patch fixes the issue of an access to a yet uninitialized data structure
at the point where irq_set_chained_handler() was called by moving the
respective calls to the end of lpc32xx_init_irq().

The call path was:

irq_set_chained_handler()
-> __irq_set_handler()
-> irq_startup()
-> irq_enable()
-> desc->irq_data.chip->irq_unmask()

at which point lpc32xx_unmask_irq() effectively read desc->irq_data.hwirq which
was only later initialized.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---
 arch/arm/mach-lpc32xx/irq.c |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

--- linux-2.6.orig/arch/arm/mach-lpc32xx/irq.c
+++ linux-2.6/arch/arm/mach-lpc32xx/irq.c
@@ -447,10 +447,6 @@ void __init lpc32xx_init_irq(void)
 	__raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC1_BASE));
 	__raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC2_BASE));
 
-	/* MIC SUBIRQx interrupts will route handling to the chain handlers */
-	irq_set_chained_handler(IRQ_LPC32XX_SUB1IRQ, lpc32xx_sic1_handler);
-	irq_set_chained_handler(IRQ_LPC32XX_SUB2IRQ, lpc32xx_sic2_handler);
-
 	/* Initially disable all wake events */
 	__raw_writel(0, LPC32XX_CLKPWR_P01_ER);
 	__raw_writel(0, LPC32XX_CLKPWR_INT_ER);
@@ -479,4 +475,8 @@ void __init lpc32xx_init_irq(void)
 						   NULL);
 	if (!lpc32xx_mic_domain)
 		panic("Unable to add MIC irq domain\n");
+
+	/* MIC SUBIRQx interrupts will route handling to the chain handlers */
+	irq_set_chained_handler(IRQ_LPC32XX_SUB1IRQ, lpc32xx_sic1_handler);
+	irq_set_chained_handler(IRQ_LPC32XX_SUB2IRQ, lpc32xx_sic2_handler);
 }

^ permalink raw reply

* [PATCH 3/3] ARM: LPC32xx: Cleanup irq.c
From: Roland Stigge @ 2012-10-28 20:35 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351456522-22309-1-git-send-email-stigge@antcom.de>

This patch removes the IRQ mask initialization which is already done some lines
above.

This was actually a bug: The init was supposed to set the bits for the
(chained) SUB IRQs. But this is already fixed by the previous patch, doing this
implicitely via irq_set_chained_handler().

Signed-off-by: Roland Stigge <stigge@antcom.de>
---
 arch/arm/mach-lpc32xx/irq.c |    5 -----
 1 file changed, 5 deletions(-)

--- linux-2.6.orig/arch/arm/mach-lpc32xx/irq.c
+++ linux-2.6/arch/arm/mach-lpc32xx/irq.c
@@ -442,11 +442,6 @@ void __init lpc32xx_init_irq(void)
 	lpc32xx_set_default_mappings(SIC1_APR_DEFAULT, SIC1_ATR_DEFAULT, 32);
 	lpc32xx_set_default_mappings(SIC2_APR_DEFAULT, SIC2_ATR_DEFAULT, 64);
 
-	/* mask all interrupts except SUBIRQ */
-	__raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_MIC_BASE));
-	__raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC1_BASE));
-	__raw_writel(0, LPC32XX_INTC_MASK(LPC32XX_SIC2_BASE));
-
 	/* Initially disable all wake events */
 	__raw_writel(0, LPC32XX_CLKPWR_P01_ER);
 	__raw_writel(0, LPC32XX_CLKPWR_INT_ER);

^ permalink raw reply

* [PATCH RESEND 0/5 v6] gpio: Add block GPIO
From: Roland Stigge @ 2012-10-28 20:46 UTC (permalink / raw)
  To: linux-arm-kernel

This set of patches adds:

* Block GPIO API to gpiolib
* Sysfs support for GPIO API, to provide userland access
* Devicetree support to instantiate GPIO blocks via DT
* Example implementations in several gpio drivers since they need
  special accessor functions for block wise GPIO access
* Fix for race condition in gpiolib on device creation

Signed-off-by: Roland Stigge <stigge@antcom.de>
--

Changes since v5:
* Documented sysfs: elaborated on "exported" and "values" attributes
* Documented sysfs: gpiochip is a separate class now
* Aggregated driver support patches for block gpio into one single patch
* Added gpio block driver support for twl6040 and pch

Changes since v4:
* Documented word width
* Bugfix: export/unexport on register/unregister
* Using default dev_attrs for gpio_block_class
* Fix gpiolib: race condition on device creation
* Added driver support for ucb14500, vt8500, xilinx

Changes since v3:
* Added driver support for pca953x, em, pl061, max732x, pcf857x
* Coding style improvements
* Fixed krealloc memory leak in error case
* sysfs: values in hex
* Register blocks in a list
* Narrowing lock scope
* Use S_IWUSR and S_IRUGO instead of direct octal values
* Use for_each_set_bit()
* Change from unsigned to unsigned long for masks and values

Changes since v2:
* Added sysfs support
* Added devicetree support
* Added support for lpc32xx, generic
* Added functions for GPIO block registration
* Added more error checking
* Bit remapping bugfix

Changes since v1:
* API change to 32/64 bit word, bit masks

Thanks to Ryan Mallon, Linus Walleij, Stijn Devriendt, Jean-Christophe
Plagniol-Villard, Mark Brown and Greg Kroah-Hartman for reviewing!

Roland Stigge (5):
 gpio: Add a block GPIO API to gpiolib
 gpio: Add sysfs support to block GPIO API
 gpiolib: Fix default attributes for class
 gpio: Add device tree support to block GPIO API
 gpio: Add block gpio to several gpio drivers
 
 Documentation/ABI/testing/sysfs-gpio                  |   29 +
 Documentation/devicetree/bindings/gpio/gpio-block.txt |   36 +
 Documentation/gpio.txt                                |   47 +
 drivers/gpio/Makefile                                 |    1 
 drivers/gpio/gpio-em.c                                |   23 
 drivers/gpio/gpio-generic.c                           |   56 +
 drivers/gpio/gpio-lpc32xx.c                           |   82 ++
 drivers/gpio/gpio-max730x.c                           |   61 ++
 drivers/gpio/gpio-max732x.c                           |   59 ++
 drivers/gpio/gpio-pca953x.c                           |   64 ++
 drivers/gpio/gpio-pcf857x.c                           |   24 
 drivers/gpio/gpio-pch.c                               |   27 
 drivers/gpio/gpio-pl061.c                             |   17 
 drivers/gpio/gpio-twl6040.c                           |   32 +
 drivers/gpio/gpio-ucb1400.c                           |   23 
 drivers/gpio/gpio-vt8500.c                            |   24 
 drivers/gpio/gpio-xilinx.c                            |   44 +
 drivers/gpio/gpioblock-of.c                           |   84 ++
 drivers/gpio/gpiolib.c                                |  510 ++++++++++++++++--
 include/asm-generic/gpio.h                            |   26 
 include/linux/gpio.h                                  |   87 +++
 21 files changed, 1311 insertions(+), 45 deletions(-)

^ permalink raw reply

* [PATCH RESEND 1/5 v6] gpio: Add a block GPIO API to gpiolib
From: Roland Stigge @ 2012-10-28 20:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351457210-25222-1-git-send-email-stigge@antcom.de>

The recurring task of providing simultaneous access to GPIO lines (especially
for bit banging protocols) needs an appropriate API.

This patch adds a kernel internal "Block GPIO" API that enables simultaneous
access to several GPIOs. This is done by abstracting GPIOs to an n-bit word:
Once requested, it provides access to a group of GPIOs which can range over
multiple GPIO chips.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---

 Documentation/gpio.txt     |   47 +++++++++
 drivers/gpio/gpiolib.c     |  217 +++++++++++++++++++++++++++++++++++++++++++++
 include/asm-generic/gpio.h |   15 +++
 include/linux/gpio.h       |   74 +++++++++++++++
 4 files changed, 353 insertions(+)

--- linux-2.6.orig/Documentation/gpio.txt
+++ linux-2.6/Documentation/gpio.txt
@@ -439,6 +439,53 @@ slower clock delays the rising edge of S
 signaling rate accordingly.
 
 
+Block GPIO
+----------
+
+The above described interface concentrates on handling single GPIOs.  However,
+in applications where it is critical to set several GPIOs at once, this
+interface doesn't work well, e.g. bit-banging protocols via grouped GPIO lines.
+Consider a GPIO controller that is connected via a slow I2C line. When
+switching two or more GPIOs one after another, there can be considerable time
+between those events. This is solved by an interface called Block GPIO:
+
+struct gpio_block *gpio_block_create(unsigned int *gpios, size_t size);
+
+This creates a new block of GPIOs as a list of GPIO numbers with the specified
+size which are accessible via the returned struct gpio_block and the accessor
+functions described below. Please note that you need to request the GPIOs
+separately via gpio_request(). An arbitrary list of globally valid GPIOs can be
+specified, even ranging over several gpio_chips. Actual handling of I/O
+operations will be done on a best effort base, i.e. simultaneous I/O only where
+possible by hardware and implemented in the respective GPIO driver. The number
+of GPIOs in one block is limited to the number of bits in an unsigned long, or
+BITS_PER_LONG, of the respective platform, i.e. typically at least 32 on a 32
+bit system, and at least 64 on a 64 bit system. However, several blocks can be
+defined at once.
+
+unsigned gpio_block_get(struct gpio_block *block);
+void gpio_block_set(struct gpio_block *block, unsigned value);
+
+With those accessor functions, setting and getting the GPIO values is possible,
+analogous to gpio_get_value() and gpio_set_value(). Each bit in the return
+value of gpio_block_get() and in the value argument of gpio_block_set()
+corresponds to a bit specified on gpio_block_create(). Block operations in
+hardware are only possible where the respective GPIO driver implements it,
+falling back to using single GPIO operations where the driver only implements
+single GPIO access.
+
+void gpio_block_free(struct gpio_block *block);
+
+After the GPIO block isn't used anymore, it should be free'd via
+gpio_block_free().
+
+int gpio_block_register(struct gpio_block *block);
+void gpio_block_unregister(struct gpio_block *block);
+
+These functions can be used to register a GPIO block. Blocks registered this
+way will be available via sysfs.
+
+
 What do these conventions omit?
 ===============================
 One of the biggest things these conventions omit is pin multiplexing, since
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -83,6 +83,8 @@ static inline void desc_set_label(struct
 #endif
 }
 
+static LIST_HEAD(gpio_block_list);
+
 /* 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
@@ -1676,6 +1678,221 @@ void __gpio_set_value(unsigned gpio, int
 }
 EXPORT_SYMBOL_GPL(__gpio_set_value);
 
+static int gpio_block_chip_index(struct gpio_block *block, struct gpio_chip *gc)
+{
+	int i;
+
+	for (i = 0; i < block->nchip; i++) {
+		if (block->gbc[i].gc == gc)
+			return i;
+	}
+	return -1;
+}
+
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+				     const char *name)
+{
+	struct gpio_block *block;
+	struct gpio_block_chip *gbc;
+	struct gpio_remap *remap;
+	void *tmp;
+	int i;
+
+	if (size < 1 || size > sizeof(unsigned long) * 8)
+		return ERR_PTR(-EINVAL);
+
+	for (i = 0; i < size; i++)
+		if (!gpio_is_valid(gpios[i]))
+			return ERR_PTR(-EINVAL);
+
+	block = kzalloc(sizeof(struct gpio_block), GFP_KERNEL);
+	if (!block)
+		return ERR_PTR(-ENOMEM);
+
+	block->name = name;
+	block->ngpio = size;
+	block->gpio = kzalloc(sizeof(*block->gpio) * size, GFP_KERNEL);
+	if (!block->gpio)
+		goto err1;
+
+	memcpy(block->gpio, gpios, sizeof(*block->gpio) * size);
+
+	for (i = 0; i < size; i++) {
+		struct gpio_chip *gc = gpio_to_chip(gpios[i]);
+		int bit = gpios[i] - gc->base;
+		int index = gpio_block_chip_index(block, gc);
+
+		if (index < 0) {
+			block->nchip++;
+			tmp = krealloc(block->gbc,
+				       sizeof(struct gpio_block_chip) *
+				       block->nchip, GFP_KERNEL);
+			if (!tmp) {
+				kfree(block->gbc);
+				goto err2;
+			}
+			block->gbc = tmp;
+			gbc = &block->gbc[block->nchip - 1];
+			gbc->gc = gc;
+			gbc->remap = NULL;
+			gbc->nremap = 0;
+			gbc->mask = 0;
+		} else {
+			gbc = &block->gbc[index];
+		}
+		/* represents the mask necessary on calls to the driver's
+		 * .get_block() and .set_block()
+		 */
+		gbc->mask |= BIT(bit);
+
+		/* collect gpios that are specified together, represented by
+		 * neighboring bits
+		 *
+		 * Note that even though in setting remap is given a negative
+		 * index, the next lines guard that the potential out-of-bounds
+		 * pointer is not dereferenced when out of bounds.
+		 */
+		remap = &gbc->remap[gbc->nremap - 1];
+		if (!gbc->nremap || (bit - i != remap->offset)) {
+			gbc->nremap++;
+			tmp = krealloc(gbc->remap,
+					      sizeof(struct gpio_remap) *
+					      gbc->nremap, GFP_KERNEL);
+			if (!tmp) {
+				kfree(gbc->remap);
+				goto err3;
+			}
+			gbc->remap = tmp;
+			remap = &gbc->remap[gbc->nremap - 1];
+			remap->offset = bit - i;
+			remap->mask = 0;
+		}
+
+		/* represents the mask necessary for bit reordering between
+		 * gpio_block (i.e. as specified on gpio_block_get() and
+		 * gpio_block_set()) and gpio_chip domain (i.e. as specified on
+		 * the driver's .set_block() and .get_block())
+		 */
+		remap->mask |= BIT(i);
+	}
+
+	return block;
+err3:
+	for (i = 0; i < block->nchip - 1; i++)
+		kfree(block->gbc[i].remap);
+	kfree(block->gbc);
+err2:
+	kfree(block->gpio);
+err1:
+	kfree(block);
+	return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL_GPL(gpio_block_create);
+
+void gpio_block_free(struct gpio_block *block)
+{
+	int i;
+
+	for (i = 0; i < block->nchip; i++)
+		kfree(block->gbc[i].remap);
+	kfree(block->gpio);
+	kfree(block->gbc);
+	kfree(block);
+}
+EXPORT_SYMBOL_GPL(gpio_block_free);
+
+unsigned long gpio_block_get(const struct gpio_block *block)
+{
+	struct gpio_block_chip *gbc;
+	int i, j;
+	unsigned long values = 0;
+
+	for (i = 0; i < block->nchip; i++) {
+		unsigned long remapped = 0;
+
+		gbc = &block->gbc[i];
+
+		if (gbc->gc->get_block) {
+			remapped = gbc->gc->get_block(gbc->gc, gbc->mask);
+		} else {
+			/* emulate */
+			for_each_set_bit(j, &gbc->mask, BITS_PER_LONG)
+				remapped |= gbc->gc->get(gbc->gc,
+						gbc->gc->base + j) << j;
+		}
+
+		for (j = 0; j < gbc->nremap; j++) {
+			struct gpio_remap *gr = &gbc->remap[j];
+
+			values |= (remapped >> gr->offset) & gr->mask;
+		}
+	}
+
+	return values;
+}
+EXPORT_SYMBOL_GPL(gpio_block_get);
+
+void gpio_block_set(struct gpio_block *block, unsigned long values)
+{
+	struct gpio_block_chip *gbc;
+	int i, j;
+
+	for (i = 0; i < block->nchip; i++) {
+		unsigned long remapped = 0;
+
+		gbc = &block->gbc[i];
+
+		for (j = 0; j < gbc->nremap; j++) {
+			struct gpio_remap *gr = &gbc->remap[j];
+
+			remapped |= (values & gr->mask) << gr->offset;
+		}
+		if (gbc->gc->set_block) {
+			gbc->gc->set_block(gbc->gc, gbc->mask, remapped);
+		} else {
+			/* emulate */
+			for_each_set_bit(j, &gbc->mask, BITS_PER_LONG)
+				gbc->gc->set(gbc->gc, gbc->gc->base + j,
+					     (remapped >> j) & 1);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(gpio_block_set);
+
+struct gpio_block *gpio_block_find_by_name(const char *name)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (!strcmp(i->name, name))
+			return i;
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(gpio_block_find_by_name);
+
+int gpio_block_register(struct gpio_block *block)
+{
+	if (gpio_block_find_by_name(block->name))
+		return -EBUSY;
+
+	list_add(&block->list, &gpio_block_list);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(gpio_block_register);
+
+void gpio_block_unregister(struct gpio_block *block)
+{
+	struct gpio_block *i;
+
+	list_for_each_entry(i, &gpio_block_list, list)
+		if (i == block) {
+			list_del(&i->list);
+			break;
+		}
+}
+EXPORT_SYMBOL_GPL(gpio_block_unregister);
+
 /**
  * __gpio_cansleep() - report whether gpio value access will sleep
  * @gpio: gpio in question
--- linux-2.6.orig/include/asm-generic/gpio.h
+++ linux-2.6/include/asm-generic/gpio.h
@@ -43,6 +43,7 @@ static inline bool gpio_is_valid(int num
 
 struct device;
 struct gpio;
+struct gpio_block;
 struct seq_file;
 struct module;
 struct device_node;
@@ -105,6 +106,8 @@ struct gpio_chip {
 						unsigned offset);
 	int			(*get)(struct gpio_chip *chip,
 						unsigned offset);
+	unsigned long		(*get_block)(struct gpio_chip *chip,
+					     unsigned long mask);
 	int			(*direction_output)(struct gpio_chip *chip,
 						unsigned offset, int value);
 	int			(*set_debounce)(struct gpio_chip *chip,
@@ -112,6 +115,9 @@ struct gpio_chip {
 
 	void			(*set)(struct gpio_chip *chip,
 						unsigned offset, int value);
+	void			(*set_block)(struct gpio_chip *chip,
+					     unsigned long mask,
+					     unsigned long values);
 
 	int			(*to_irq)(struct gpio_chip *chip,
 						unsigned offset);
@@ -171,6 +177,15 @@ extern void gpio_set_value_cansleep(unsi
 extern int __gpio_get_value(unsigned gpio);
 extern void __gpio_set_value(unsigned gpio, int value);
 
+extern struct gpio_block *gpio_block_create(unsigned *gpio, size_t size,
+					    const char *name);
+extern void gpio_block_free(struct gpio_block *block);
+extern unsigned long gpio_block_get(const struct gpio_block *block);
+extern void gpio_block_set(struct gpio_block *block, unsigned long values);
+extern struct gpio_block *gpio_block_find_by_name(const char *name);
+extern int gpio_block_register(struct gpio_block *block);
+extern void gpio_block_unregister(struct gpio_block *block);
+
 extern int __gpio_cansleep(unsigned gpio);
 
 extern int __gpio_to_irq(unsigned gpio);
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -2,6 +2,8 @@
 #define __LINUX_GPIO_H
 
 #include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/list.h>
 
 /* see Documentation/gpio.txt */
 
@@ -39,6 +41,43 @@ struct gpio {
 	const char	*label;
 };
 
+/*
+ * struct gpio_remap - a structure for describing a bit mapping
+ * @mask:	a bit mask
+ * @offset:	how many bits to shift to the left (negative: to the right)
+ *
+ * When we are mapping bit values from one word to another (here: from GPIO
+ * block domain to GPIO driver domain) we first mask them out with mask and
+ * shift them as specified with offset. More complicated mappings are done by
+ * grouping several of those structs and adding the results together.
+ */
+struct gpio_remap {
+	unsigned long		mask;
+	int			offset;
+};
+
+struct gpio_block_chip {
+	struct gpio_chip	*gc;
+	struct gpio_remap	*remap;
+	int			nremap;
+	unsigned long		mask;
+};
+
+/**
+ * struct gpio_block - a structure describing a list of GPIOs for simultaneous
+ *                     operations
+ */
+struct gpio_block {
+	struct gpio_block_chip	*gbc;
+	size_t			nchip;
+	const char		*name;
+
+	int			ngpio;
+	unsigned		*gpio;
+
+	struct list_head	list;
+};
+
 #ifdef CONFIG_GENERIC_GPIO
 
 #ifdef CONFIG_ARCH_HAVE_CUSTOM_GPIO_H
@@ -169,6 +208,41 @@ static inline void gpio_set_value(unsign
 	WARN_ON(1);
 }
 
+static inline
+struct gpio_block *gpio_block_create(unsigned *gpios, size_t size,
+				     const char *name)
+{
+	WARN_ON(1);
+	return NULL;
+}
+
+static inline void gpio_block_free(struct gpio_block *block)
+{
+	WARN_ON(1);
+}
+
+static inline unsigned long gpio_block_get(const struct gpio_block *block)
+{
+	WARN_ON(1);
+	return 0;
+}
+
+static inline void gpio_block_set(struct gpio_block *block, unsigned long value)
+{
+	WARN_ON(1);
+}
+
+static inline int gpio_block_register(struct gpio_block *block)
+{
+	WARN_ON(1);
+	return 0;
+}
+
+static inline void gpio_block_unregister(struct gpio_block *block)
+{
+	WARN_ON(1);
+}
+
 static inline int gpio_cansleep(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as {in,out}put */

^ permalink raw reply

* [PATCH RESEND 2/5 v6] gpio: Add sysfs support to block GPIO API
From: Roland Stigge @ 2012-10-28 20:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351457210-25222-1-git-send-email-stigge@antcom.de>

This patch adds sysfs support to the block GPIO API.

Signed-off-by: Roland Stigge <stigge@antcom.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

---
 Documentation/ABI/testing/sysfs-gpio |   18 ++
 drivers/gpio/gpiolib.c               |  214 +++++++++++++++++++++++++++++++++++
 include/asm-generic/gpio.h           |   11 +
 include/linux/gpio.h                 |   13 ++
 4 files changed, 256 insertions(+)

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -25,3 +25,21 @@ Description:
 	    /label ... (r/o) descriptive, not necessarily unique
 	    /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
 
+What:		/sys/class/gpioblock/
+Date:		October 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:
+
+  Block GPIO devices are visible in sysfs as soon as they are registered
+  (e.g. via devicetree definition). For actual I/O use, their "exported"
+  boolean attribute must be set to "1". Then, the attribute "values" is
+  created and at the same time, the GPIOs in the block are requested for
+  exclusive use by sysfs.
+
+    /sys/class/gpioblock
+	/BLOCKNAME ... for each GPIO block name
+	    /ngpio ... (r/o) number of GPIOs in this group
+	    /exported ... sysfs export state of this group (0, 1)
+	    /value ... current value as 32 or 64 bit integer in hex
+                       (only available if /exported is 1)
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -972,6 +972,214 @@ static void gpiochip_unexport(struct gpi
 				chip->label, status);
 }
 
+static ssize_t gpio_block_ngpio_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block	*block = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", block->ngpio);
+}
+
+static ssize_t gpio_block_value_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	const struct gpio_block *block = dev_get_drvdata(dev);
+
+	return sprintf(buf, sizeof(unsigned long) == 4 ? "0x%08lx\n" :
+		       "0x%016lx\n", gpio_block_get(block));
+}
+
+static bool gpio_block_is_output(struct gpio_block *block)
+{
+	int i;
+
+	for (i = 0; i < block->ngpio; i++)
+		if (!test_bit(FLAG_IS_OUT, &gpio_desc[block->gpio[i]].flags))
+			return false;
+	return true;
+}
+
+static ssize_t gpio_block_value_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t size)
+{
+	ssize_t			status;
+	struct gpio_block	*block = dev_get_drvdata(dev);
+	unsigned long		value;
+
+	status = kstrtoul(buf, 0, &value);
+	if (status == 0) {
+		mutex_lock(&sysfs_lock);
+		if (gpio_block_is_output(block)) {
+			gpio_block_set(block, value);
+			status = size;
+		} else {
+			status = -EPERM;
+		}
+		mutex_unlock(&sysfs_lock);
+	}
+	return status;
+}
+
+static struct device_attribute
+dev_attr_block_value = __ATTR(value, S_IWUSR | S_IRUGO, gpio_block_value_show,
+			      gpio_block_value_store);
+
+static struct class gpio_block_class;
+
+static int gpio_block_value_is_exported(struct gpio_block *block)
+{
+	struct device		*dev;
+	struct sysfs_dirent	*sd = NULL;
+
+	mutex_lock(&sysfs_lock);
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev)
+		goto out;
+
+	sd = sysfs_get_dirent(dev->kobj.sd, NULL, "value");
+
+out:
+	mutex_unlock(&sysfs_lock);
+	return !!sd;
+}
+
+static ssize_t gpio_block_exported_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct gpio_block	*block = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%u\n", gpio_block_value_is_exported(block));
+}
+
+static int gpio_block_value_export(struct gpio_block *block)
+{
+	struct device	*dev;
+	int		status;
+	int		i;
+
+	mutex_lock(&sysfs_lock);
+
+	for (i = 0; i < block->ngpio; i++) {
+		status = gpio_request(block->gpio[i], "sysfs");
+		if (status)
+			goto out;
+	}
+
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev) {
+		status = -ENODEV;
+		goto out;
+	}
+
+	status = device_create_file(dev, &dev_attr_block_value);
+	if (status)
+		goto out;
+
+	mutex_unlock(&sysfs_lock);
+	return 0;
+
+out:
+	while (--i >= 0)
+		gpio_free(block->gpio[i]);
+
+	mutex_unlock(&sysfs_lock);
+	return status;
+}
+
+static int gpio_block_value_unexport(struct gpio_block *block)
+{
+	struct device	*dev;
+	int		i;
+
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (!dev)
+		return -ENODEV;
+
+	for (i = 0; i < block->ngpio; i++)
+		gpio_free(block->gpio[i]);
+
+	device_remove_file(dev, &dev_attr_block_value);
+
+	return 0;
+}
+
+static ssize_t gpio_block_exported_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t size)
+{
+	long	value;
+	int	status;
+	struct	gpio_block *block = dev_get_drvdata(dev);
+	int	exported = gpio_block_value_is_exported(block);
+
+	status = kstrtoul(buf, 0, &value);
+	if (status < 0)
+		goto err;
+
+	if (value != exported) {
+		if (value)
+			status = gpio_block_value_export(block);
+		else
+			status = gpio_block_value_unexport(block);
+		if (!status)
+			status = size;
+	} else {
+		status = size;
+	}
+err:
+	return status;
+}
+
+static struct device_attribute gpio_block_attrs[] = {
+	__ATTR(exported, S_IWUSR | S_IRUGO, gpio_block_exported_show,
+	       gpio_block_exported_store),
+	__ATTR(ngpio, S_IRUGO, gpio_block_ngpio_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct class gpio_block_class = {
+	.name =         "gpioblock",
+	.owner =        THIS_MODULE,
+
+	.dev_attrs =  gpio_block_attrs,
+};
+
+int gpio_block_export(struct gpio_block *block)
+{
+	int		status = 0;
+	struct device	*dev;
+
+	/* can't export until sysfs is available ... */
+	if (!gpio_block_class.p) {
+		pr_debug("%s: called too early!\n", __func__);
+		return -ENOENT;
+	}
+
+	mutex_lock(&sysfs_lock);
+	dev = device_create(&gpio_block_class, NULL, MKDEV(0, 0), block,
+			    block->name);
+	if (IS_ERR(dev))
+		status = PTR_ERR(dev);
+	mutex_unlock(&sysfs_lock);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(gpio_block_export);
+
+void gpio_block_unexport(struct gpio_block *block)
+{
+	struct device *dev;
+
+	mutex_lock(&sysfs_lock);
+	dev = class_find_device(&gpio_block_class, NULL, block, match_export);
+	if (dev)
+		device_unregister(dev);
+	mutex_unlock(&sysfs_lock);
+}
+EXPORT_SYMBOL_GPL(gpio_block_unexport);
+
 static int __init gpiolib_sysfs_init(void)
 {
 	int		status;
@@ -982,6 +1190,10 @@ static int __init gpiolib_sysfs_init(voi
 	if (status < 0)
 		return status;
 
+	status = class_register(&gpio_block_class);
+	if (status < 0)
+		return status;
+
 	/* Scan and register the gpio_chips which registered very
 	 * early (e.g. before the class_register above was called).
 	 *
@@ -1876,6 +2088,7 @@ int gpio_block_register(struct gpio_bloc
 		return -EBUSY;
 
 	list_add(&block->list, &gpio_block_list);
+	gpio_block_export(block);
 
 	return 0;
 }
@@ -1888,6 +2101,7 @@ void gpio_block_unregister(struct gpio_b
 	list_for_each_entry(i, &gpio_block_list, list)
 		if (i == block) {
 			list_del(&i->list);
+			gpio_block_unexport(block);
 			break;
 		}
 }
--- linux-2.6.orig/include/asm-generic/gpio.h
+++ linux-2.6/include/asm-generic/gpio.h
@@ -211,6 +211,8 @@ extern int gpio_export_link(struct devic
 			unsigned gpio);
 extern int gpio_sysfs_set_active_low(unsigned gpio, int value);
 extern void gpio_unexport(unsigned gpio);
+extern int gpio_block_export(struct gpio_block *block);
+extern void gpio_block_unexport(struct gpio_block *block);
 
 #endif	/* CONFIG_GPIO_SYSFS */
 
@@ -270,6 +272,15 @@ static inline int gpio_sysfs_set_active_
 static inline void gpio_unexport(unsigned gpio)
 {
 }
+
+static inline int gpio_block_export(struct gpio_block *block)
+{
+	return -ENOSYS;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+}
 #endif	/* CONFIG_GPIO_SYSFS */
 
 #endif /* _ASM_GENERIC_GPIO_H */
--- linux-2.6.orig/include/linux/gpio.h
+++ linux-2.6/include/linux/gpio.h
@@ -291,6 +291,19 @@ static inline void gpio_unexport(unsigne
 	WARN_ON(1);
 }
 
+static inline int gpio_block_export(struct gpio_block *block)
+{
+	/* GPIO block can never have been requested or set as {in,out}put */
+	WARN_ON(1);
+	return -EINVAL;
+}
+
+static inline void gpio_block_unexport(struct gpio_block *block)
+{
+	/* GPIO block can never have been exported */
+	WARN_ON(1);
+}
+
 static inline int gpio_to_irq(unsigned gpio)
 {
 	/* GPIO can never have been requested or set as input */

^ permalink raw reply

* [PATCH RESEND 3/5 v6] gpiolib: Fix default attributes for class
From: Roland Stigge @ 2012-10-28 20:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351457210-25222-1-git-send-email-stigge@antcom.de>

There is a race condition between creating a gpio or gpiochip device and adding
default attributes. This patch fixes this by defining the default attributes as
dev_attrs of the class. For this, it was necessary to create a separate
gpiochip_class besides gpio_class.

Signed-off-by: Roland Stigge <stigge@antcom.de>
---
 Documentation/ABI/testing/sysfs-gpio |   11 ++++
 drivers/gpio/gpiolib.c               |   79 +++++++++++++++--------------------
 2 files changed, 45 insertions(+), 45 deletions(-)

--- linux-2.6.orig/Documentation/ABI/testing/sysfs-gpio
+++ linux-2.6/Documentation/ABI/testing/sysfs-gpio
@@ -20,6 +20,17 @@ Description:
 	    /value ... always readable, writes fail for input GPIOs
 	    /direction ... r/w as: in, out (default low); write: high, low
 	    /edge ... r/w as: none, falling, rising, both
+
+What:		/sys/class/gpiochip/
+Date:		October 2012
+KernelVersion:	3.7
+Contact:	Roland Stigge <stigge@antcom.de>
+Description:
+
+  Each gpiochip is represented by a separate device having the following
+  attributes:
+
+    /sys/class/gpiochip
 	/gpiochipN ... for each gpiochip; #N is its first GPIO
 	    /base ... (r/o) same as N
 	    /label ... (r/o) descriptive, not necessarily unique
--- linux-2.6.orig/drivers/gpio/gpiolib.c
+++ linux-2.6/drivers/gpio/gpiolib.c
@@ -321,9 +321,6 @@ static ssize_t gpio_value_store(struct d
 	return status;
 }
 
-static const DEVICE_ATTR(value, 0644,
-		gpio_value_show, gpio_value_store);
-
 static irqreturn_t gpio_sysfs_irq(int irq, void *priv)
 {
 	struct sysfs_dirent	*value_sd = priv;
@@ -544,19 +541,6 @@ static ssize_t gpio_active_low_store(str
 	return status ? : size;
 }
 
-static const DEVICE_ATTR(active_low, 0644,
-		gpio_active_low_show, gpio_active_low_store);
-
-static const struct attribute *gpio_attrs[] = {
-	&dev_attr_value.attr,
-	&dev_attr_active_low.attr,
-	NULL,
-};
-
-static const struct attribute_group gpio_attr_group = {
-	.attrs = (struct attribute **) gpio_attrs,
-};
-
 /*
  * /sys/class/gpio/gpiochipN/
  *   /base ... matching gpio_chip.base (N)
@@ -571,7 +555,6 @@ static ssize_t chip_base_show(struct dev
 
 	return sprintf(buf, "%d\n", chip->base);
 }
-static DEVICE_ATTR(base, 0444, chip_base_show, NULL);
 
 static ssize_t chip_label_show(struct device *dev,
 			       struct device_attribute *attr, char *buf)
@@ -580,7 +563,6 @@ static ssize_t chip_label_show(struct de
 
 	return sprintf(buf, "%s\n", chip->label ? : "");
 }
-static DEVICE_ATTR(label, 0444, chip_label_show, NULL);
 
 static ssize_t chip_ngpio_show(struct device *dev,
 			       struct device_attribute *attr, char *buf)
@@ -589,18 +571,6 @@ static ssize_t chip_ngpio_show(struct de
 
 	return sprintf(buf, "%u\n", chip->ngpio);
 }
-static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL);
-
-static const struct attribute *gpiochip_attrs[] = {
-	&dev_attr_base.attr,
-	&dev_attr_label.attr,
-	&dev_attr_ngpio.attr,
-	NULL,
-};
-
-static const struct attribute_group gpiochip_attr_group = {
-	.attrs = (struct attribute **) gpiochip_attrs,
-};
 
 /*
  * /sys/class/gpio/export ... write-only
@@ -677,11 +647,32 @@ static struct class_attribute gpio_class
 	__ATTR_NULL,
 };
 
+static struct device_attribute gpio_attrs[] = {
+	__ATTR(active_low, 0644, gpio_active_low_show, gpio_active_low_store),
+	__ATTR(value, 0644, gpio_value_show, gpio_value_store),
+	__ATTR_NULL,
+};
+
 static struct class gpio_class = {
 	.name =		"gpio",
 	.owner =	THIS_MODULE,
 
-	.class_attrs =	gpio_class_attrs,
+	.class_attrs = gpio_class_attrs,
+	.dev_attrs = gpio_attrs,
+};
+
+static struct device_attribute gpiochip_attrs[] = {
+	__ATTR(label, 0444, chip_label_show, NULL),
+	__ATTR(base, 0444, chip_base_show, NULL),
+	__ATTR(ngpio, 0444, chip_ngpio_show, NULL),
+	__ATTR_NULL,
+};
+
+static struct class gpiochip_class = {
+	.name =		"gpiochip",
+	.owner =	THIS_MODULE,
+
+	.dev_attrs =	gpiochip_attrs,
 };
 
 
@@ -738,10 +729,7 @@ int gpio_export(unsigned gpio, bool dire
 		dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0),
 				desc, ioname ? ioname : "gpio%u", gpio);
 		if (!IS_ERR(dev)) {
-			status = sysfs_create_group(&dev->kobj,
-						&gpio_attr_group);
-
-			if (!status && direction_may_change)
+			if (direction_may_change)
 				status = device_create_file(dev,
 						&dev_attr_direction);
 
@@ -911,25 +899,22 @@ EXPORT_SYMBOL_GPL(gpio_unexport);
 
 static int gpiochip_export(struct gpio_chip *chip)
 {
-	int		status;
+	int		status = 0;
 	struct device	*dev;
 
 	/* Many systems register gpio chips for SOC support very early,
 	 * before driver model support is available.  In those cases we
 	 * export this later, in gpiolib_sysfs_init() ... here we just
-	 * verify that _some_ field of gpio_class got initialized.
+	 * verify that _some_ field of gpiochip_class got initialized.
 	 */
-	if (!gpio_class.p)
+	if (!gpiochip_class.p)
 		return 0;
 
 	/* use chip->base for the ID; it's already known to be unique */
 	mutex_lock(&sysfs_lock);
-	dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip,
-				"gpiochip%d", chip->base);
-	if (!IS_ERR(dev)) {
-		status = sysfs_create_group(&dev->kobj,
-				&gpiochip_attr_group);
-	} else
+	dev = device_create(&gpiochip_class, chip->dev, MKDEV(0, 0), chip,
+			    "gpiochip%d", chip->base);
+	if (IS_ERR(dev))
 		status = PTR_ERR(dev);
 	chip->exported = (status == 0);
 	mutex_unlock(&sysfs_lock);
@@ -957,7 +942,7 @@ static void gpiochip_unexport(struct gpi
 	struct device		*dev;
 
 	mutex_lock(&sysfs_lock);
-	dev = class_find_device(&gpio_class, NULL, chip, match_export);
+	dev = class_find_device(&gpiochip_class, NULL, chip, match_export);
 	if (dev) {
 		put_device(dev);
 		device_unregister(dev);
@@ -1190,6 +1175,10 @@ static int __init gpiolib_sysfs_init(voi
 	if (status < 0)
 		return status;
 
+	status = class_register(&gpiochip_class);
+	if (status < 0)
+		return status;
+
 	status = class_register(&gpio_block_class);
 	if (status < 0)
 		return status;

^ permalink raw reply

* [PATCH RESEND 4/5 v6] gpio: Add device tree support to block GPIO API
From: Roland Stigge @ 2012-10-28 20:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351457210-25222-1-git-send-email-stigge@antcom.de>

This patch adds device tree support to the block GPIO API.

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 Documentation/devicetree/bindings/gpio/gpio-block.txt |   36 +++++++
 drivers/gpio/Makefile                                 |    1 
 drivers/gpio/gpioblock-of.c                           |   84 ++++++++++++++++++
 3 files changed, 121 insertions(+)

--- /dev/null
+++ linux-2.6/Documentation/devicetree/bindings/gpio/gpio-block.txt
@@ -0,0 +1,36 @@
+Block GPIO definition
+=====================
+
+This binding specifies arbitrary blocks of gpios, combining gpios from one or
+more GPIO controllers together, to form a word for r/w access.
+
+Required property:
+- compatible: must be "linux,gpio-block"
+
+Required subnodes:
+- the name will be the registered name of the block
+- property "gpios" is a list of gpios for the respective block
+
+Example:
+
+        blockgpio {
+                compatible = "linux,gpio-block";
+
+                block0 {
+                        gpios = <&gpio 3 0 0>,
+                                <&gpio 3 1 0>;
+                };
+                block1 {
+                        gpios = <&gpio 4 1 0>,
+                                <&gpio 4 3 0>,
+                                <&gpio 4 2 0>,
+                                <&gpio 4 4 0>,
+                                <&gpio 4 5 0>,
+                                <&gpio 4 6 0>,
+                                <&gpio 4 7 0>,
+                                <&gpio 4 8 0>,
+                                <&gpio 4 9 0>,
+                                <&gpio 4 10 0>,
+                                <&gpio 4 19 0>;
+                };
+        };
--- linux-2.6.orig/drivers/gpio/Makefile
+++ linux-2.6/drivers/gpio/Makefile
@@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO)	+= -DDEBUG
 
 obj-$(CONFIG_GPIOLIB)		+= gpiolib.o devres.o
 obj-$(CONFIG_OF_GPIO)		+= gpiolib-of.o
+obj-$(CONFIG_OF_GPIO)		+= gpioblock-of.o
 
 # Device drivers. Generally keep list sorted alphabetically
 obj-$(CONFIG_GPIO_GENERIC)	+= gpio-generic.o
--- /dev/null
+++ linux-2.6/drivers/gpio/gpioblock-of.c
@@ -0,0 +1,84 @@
+/*
+ * OF implementation for Block GPIO
+ *
+ * Copyright (C) 2012 Roland Stigge <stigge@antcom.de>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+static int __devinit gpioblock_of_probe(struct platform_device *pdev)
+{
+	struct device_node *block;
+	unsigned *gpios;
+	int ngpio;
+	int ret;
+	struct gpio_block *gb;
+
+	for_each_available_child_of_node(pdev->dev.of_node, block) {
+		int i;
+
+		ngpio = of_gpio_count(block);
+		gpios = kzalloc(ngpio * sizeof(*gpios), GFP_KERNEL);
+		if (!gpios)
+			return -ENOMEM;
+		for (i = 0; i < ngpio; i++) {
+			ret = of_get_gpio(block, i);
+			if (ret < 0)
+				return ret; /* expect -EPROBE_DEFER */
+			gpios[i] = ret;
+		}
+		gb = gpio_block_create(gpios, ngpio, block->name);
+		if (IS_ERR(gb)) {
+			dev_err(&pdev->dev,
+				"Error creating GPIO block from device tree\n");
+			return PTR_ERR(gb);
+		}
+		ret = gpio_block_register(gb);
+		if (ret < 0) {
+			gpio_block_free(gb);
+			return ret;
+		}
+		kfree(gpios);
+		dev_info(&pdev->dev, "Registered gpio block %s: %d gpios\n",
+			 block->name, ngpio);
+	}
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id gpioblock_of_match[] __devinitdata = {
+	{ .compatible = "linux,gpio-block", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpioblock_of_match);
+#endif
+
+static struct platform_driver gpioblock_of_driver = {
+	.driver	= {
+		.name = "gpio-block",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(gpioblock_of_match),
+
+	},
+	.probe	= gpioblock_of_probe,
+};
+
+module_platform_driver(gpioblock_of_driver);
+
+MODULE_DESCRIPTION("GPIO Block driver");
+MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpioblock-of");

^ permalink raw reply

* [PATCH RESEND 5/5 v6] gpio: Add block gpio to several gpio drivers
From: Roland Stigge @ 2012-10-28 20:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351457210-25222-1-git-send-email-stigge@antcom.de>

This patch adds block GPIO support to several gpio drivers.

Signed-off-by: Roland Stigge <stigge@antcom.de>

---
 drivers/gpio/gpio-em.c      |   23 ++++++++++++
 drivers/gpio/gpio-generic.c |   56 ++++++++++++++++++++++++++++++
 drivers/gpio/gpio-lpc32xx.c |   82 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/gpio/gpio-max730x.c |   61 ++++++++++++++++++++++++++++++++
 drivers/gpio/gpio-max732x.c |   59 +++++++++++++++++++++++++++++++
 drivers/gpio/gpio-pca953x.c |   64 ++++++++++++++++++++++++++++++++++
 drivers/gpio/gpio-pcf857x.c |   24 ++++++++++++
 drivers/gpio/gpio-pch.c     |   27 ++++++++++++++
 drivers/gpio/gpio-pl061.c   |   17 +++++++++
 drivers/gpio/gpio-twl6040.c |   32 +++++++++++++++++
 drivers/gpio/gpio-ucb1400.c |   23 ++++++++++++
 drivers/gpio/gpio-vt8500.c  |   24 ++++++++++++
 drivers/gpio/gpio-xilinx.c  |   44 +++++++++++++++++++++++
 13 files changed, 536 insertions(+)

--- linux-2.6.orig/drivers/gpio/gpio-em.c
+++ linux-2.6/drivers/gpio/gpio-em.c
@@ -203,6 +203,27 @@ static void em_gio_set(struct gpio_chip
 		__em_gio_set(chip, GIO_OH, offset - 16, value);
 }
 
+static unsigned long em_gio_get_block(struct gpio_chip *chip,
+				      unsigned long mask)
+{
+	return (int)(em_gio_read(gpio_to_priv(chip), GIO_I) & mask);
+}
+
+static void em_gio_set_block(struct gpio_chip *chip, unsigned long mask,
+			     unsigned long values)
+{
+	unsigned long mask_ol = mask & 0xFFFF;
+	unsigned long mask_oh = mask >> 16;
+
+	unsigned long values_ol = values & mask_ol;
+	unsigned long values_oh = (values >> 16) & mask_oh;
+
+	em_gio_write(gpio_to_priv(chip), GIO_OL,
+		     mask_ol << 16 | values_ol);
+	em_gio_write(gpio_to_priv(chip), GIO_OH,
+		     mask_oh << 16 | values_oh);
+}
+
 static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset,
 				   int value)
 {
@@ -317,8 +338,10 @@ static int __devinit em_gio_probe(struct
 	gpio_chip = &p->gpio_chip;
 	gpio_chip->direction_input = em_gio_direction_input;
 	gpio_chip->get = em_gio_get;
+	gpio_chip->get_block = em_gio_get_block;
 	gpio_chip->direction_output = em_gio_direction_output;
 	gpio_chip->set = em_gio_set;
+	gpio_chip->set_block = em_gio_set_block;
 	gpio_chip->to_irq = em_gio_to_irq;
 	gpio_chip->label = name;
 	gpio_chip->owner = THIS_MODULE;
--- linux-2.6.orig/drivers/gpio/gpio-generic.c
+++ linux-2.6/drivers/gpio/gpio-generic.c
@@ -122,6 +122,13 @@ static int bgpio_get(struct gpio_chip *g
 	return bgc->read_reg(bgc->reg_dat) & bgc->pin2mask(bgc, gpio);
 }
 
+static unsigned long bgpio_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+
+	return bgc->read_reg(bgc->reg_dat) & mask;
+}
+
 static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
 {
 	struct bgpio_chip *bgc = to_bgpio_chip(gc);
@@ -170,6 +177,51 @@ static void bgpio_set_set(struct gpio_ch
 	spin_unlock_irqrestore(&bgc->lock, flags);
 }
 
+static void bgpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bgc->lock, flags);
+
+	bgc->data &= ~mask;
+	bgc->data |= values & mask;
+
+	bgc->write_reg(bgc->reg_dat, bgc->data);
+
+	spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
+static void bgpio_set_with_clear_block(struct gpio_chip *gc, unsigned long mask,
+				       unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long set_bits = values & mask;
+	unsigned long clr_bits = ~values & mask;
+
+	if (set_bits)
+		bgc->write_reg(bgc->reg_set, set_bits);
+	if (clr_bits)
+		bgc->write_reg(bgc->reg_set, clr_bits);
+}
+
+static void bgpio_set_set_block(struct gpio_chip *gc, unsigned long mask,
+				unsigned long values)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long flags;
+
+	spin_lock_irqsave(&bgc->lock, flags);
+
+	bgc->data &= ~mask;
+	bgc->data |= values & mask;
+
+	bgc->write_reg(bgc->reg_set, bgc->data);
+
+	spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
 static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
 {
 	return 0;
@@ -317,14 +369,18 @@ static int bgpio_setup_io(struct bgpio_c
 		bgc->reg_set = set;
 		bgc->reg_clr = clr;
 		bgc->gc.set = bgpio_set_with_clear;
+		bgc->gc.set_block = bgpio_set_with_clear_block;
 	} else if (set && !clr) {
 		bgc->reg_set = set;
 		bgc->gc.set = bgpio_set_set;
+		bgc->gc.set_block = bgpio_set_set_block;
 	} else {
 		bgc->gc.set = bgpio_set;
+		bgc->gc.set_block = bgpio_set_block;
 	}
 
 	bgc->gc.get = bgpio_get;
+	bgc->gc.get_block = bgpio_get_block;
 
 	return 0;
 }
--- linux-2.6.orig/drivers/gpio/gpio-lpc32xx.c
+++ linux-2.6/drivers/gpio/gpio-lpc32xx.c
@@ -297,6 +297,22 @@ static int lpc32xx_gpio_get_value_p3(str
 	return __get_gpio_state_p3(group, pin);
 }
 
+static unsigned long lpc32xx_gpio_get_block_p3(struct gpio_chip *chip,
+					       unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->inp_state);
+
+	/* In P3_INP_STATE, the 6 GPIOs are scattered over the register,
+	 * rearranging to bits 0-5
+	 */
+	bits >>= 10;
+	bits &= 0x401F;
+	bits |= (bits & 0x4000) >> 9;
+
+	return bits & mask & 0x3F;
+}
+
 static int lpc32xx_gpi_get_value(struct gpio_chip *chip, unsigned pin)
 {
 	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
@@ -304,6 +320,15 @@ static int lpc32xx_gpi_get_value(struct
 	return __get_gpi_state_p3(group, pin);
 }
 
+static unsigned long lpc32xx_gpi_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->inp_state);
+
+	return bits & mask;
+}
+
 static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
 	int value)
 {
@@ -351,6 +376,27 @@ static void lpc32xx_gpio_set_value_p3(st
 	__set_gpio_level_p3(group, pin, value);
 }
 
+static void lpc32xx_gpio_set_block_p3(struct gpio_chip *chip,
+				      unsigned long mask,
+				      unsigned long values)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 set_bits = values & mask;
+	u32 clr_bits = ~values & mask;
+
+	/* States of GPIO 0-5 start at bit 25 */
+	set_bits <<= 25;
+	clr_bits <<= 25;
+
+	/* Note: On LPC32xx, GPOs can only be set at once or cleared at once,
+	 *       but not set and cleared@once
+	 */
+	if (set_bits)
+		__raw_writel(set_bits, group->gpio_grp->outp_set);
+	if (clr_bits)
+		__raw_writel(clr_bits, group->gpio_grp->outp_clr);
+}
+
 static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin,
 	int value)
 {
@@ -366,6 +412,31 @@ static int lpc32xx_gpo_get_value(struct
 	return __get_gpo_state_p3(group, pin);
 }
 
+static void lpc32xx_gpo_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long values)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 set_bits = values & mask;
+	u32 clr_bits = ~values & mask;
+
+	/* Note: On LPC32xx, GPOs can only be set at once or cleared at once,
+	 *       but not set and cleared at once
+	 */
+	if (set_bits)
+		__raw_writel(set_bits, group->gpio_grp->outp_set);
+	if (clr_bits)
+		__raw_writel(clr_bits, group->gpio_grp->outp_clr);
+}
+
+static unsigned long lpc32xx_gpo_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+	u32 bits = __raw_readl(group->gpio_grp->outp_state);
+
+	return bits & mask;
+}
+
 static int lpc32xx_gpio_request(struct gpio_chip *chip, unsigned pin)
 {
 	if (pin < chip->ngpio)
@@ -440,8 +511,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p0",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_p01,
 			.base			= LPC32XX_GPIO_P0_GRP,
@@ -456,8 +529,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p1",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_p01,
 			.base			= LPC32XX_GPIO_P1_GRP,
@@ -472,8 +547,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p2",
 			.direction_input	= lpc32xx_gpio_dir_input_p012,
 			.get			= lpc32xx_gpio_get_value_p012,
+			.get_block		= lpc32xx_gpi_get_block,
 			.direction_output	= lpc32xx_gpio_dir_output_p012,
 			.set			= lpc32xx_gpio_set_value_p012,
+			.set_block		= lpc32xx_gpo_set_block,
 			.request		= lpc32xx_gpio_request,
 			.base			= LPC32XX_GPIO_P2_GRP,
 			.ngpio			= LPC32XX_GPIO_P2_MAX,
@@ -487,8 +564,10 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpio_p3",
 			.direction_input	= lpc32xx_gpio_dir_input_p3,
 			.get			= lpc32xx_gpio_get_value_p3,
+			.get_block		= lpc32xx_gpio_get_block_p3,
 			.direction_output	= lpc32xx_gpio_dir_output_p3,
 			.set			= lpc32xx_gpio_set_value_p3,
+			.set_block		= lpc32xx_gpio_set_block_p3,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_gpio_p3,
 			.base			= LPC32XX_GPIO_P3_GRP,
@@ -503,6 +582,7 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpi_p3",
 			.direction_input	= lpc32xx_gpio_dir_in_always,
 			.get			= lpc32xx_gpi_get_value,
+			.get_block		= lpc32xx_gpi_get_block,
 			.request		= lpc32xx_gpio_request,
 			.to_irq			= lpc32xx_gpio_to_irq_gpi_p3,
 			.base			= LPC32XX_GPI_P3_GRP,
@@ -517,7 +597,9 @@ static struct lpc32xx_gpio_chip lpc32xx_
 			.label			= "gpo_p3",
 			.direction_output	= lpc32xx_gpio_dir_out_always,
 			.set			= lpc32xx_gpo_set_value,
+			.set_block		= lpc32xx_gpo_set_block,
 			.get			= lpc32xx_gpo_get_value,
+			.get_block		= lpc32xx_gpo_get_block,
 			.request		= lpc32xx_gpio_request,
 			.base			= LPC32XX_GPO_P3_GRP,
 			.ngpio			= LPC32XX_GPO_P3_MAX,
--- linux-2.6.orig/drivers/gpio/gpio-max730x.c
+++ linux-2.6/drivers/gpio/gpio-max730x.c
@@ -146,6 +146,44 @@ static int max7301_get(struct gpio_chip
 	return level;
 }
 
+static unsigned long max7301_get_block(struct gpio_chip *chip,
+				       unsigned long mask)
+{
+	struct max7301 *ts = container_of(chip, struct max7301, chip);
+	int i, j;
+	unsigned long result = 0;
+
+	for (i = 0; i < 4; i++) {
+		if ((mask >> (i * 8)) & 0xFF) { /* i/o only if necessary */
+			u8 in_level = ts->read(ts->dev, 0x44 + i * 8);
+			u8 in_mask = 0;
+			u8 out_level = (ts->out_level >> (i * 8 + 4)) & 0xFF;
+			u8 out_mask = 0;
+
+			for (j = 0; j < 8; j++) {
+				int offset = 4 + i * 8 + j;
+				int config = (ts->port_config[offset >> 2] >>
+					      ((offset & 3) << 1)) &
+					PIN_CONFIG_MASK;
+
+				switch (config) {
+				case PIN_CONFIG_OUT:
+					out_mask |= BIT(j);
+					break;
+				case PIN_CONFIG_IN_WO_PULLUP:
+				case PIN_CONFIG_IN_PULLUP:
+					in_mask |= BIT(j);
+				}
+			}
+
+			result |= ((unsigned long)(in_level & in_mask) |
+				   (out_level & out_mask)) << (i * 8);
+		}
+	}
+
+	return result & mask;
+}
+
 static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)
 {
 	struct max7301 *ts = container_of(chip, struct max7301, chip);
@@ -160,6 +198,27 @@ static void max7301_set(struct gpio_chip
 	mutex_unlock(&ts->lock);
 }
 
+static void max7301_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct max7301 *ts = container_of(chip, struct max7301, chip);
+	unsigned long changes;
+	int i;
+
+	mutex_lock(&ts->lock);
+
+	changes = (ts->out_level ^ (values << 4)) & (mask << 4);
+	ts->out_level ^= changes;
+
+	for (i = 0; i < 4; i++) {
+		if ((changes >> (i * 8 + 4)) & 0xFF) /* i/o only on change */
+			ts->write(ts->dev, 0x44 + i * 8,
+				  (ts->out_level >> (i * 8 + 4)) & 0xFF);
+	}
+
+	mutex_unlock(&ts->lock);
+}
+
 int __devinit __max730x_probe(struct max7301 *ts)
 {
 	struct device *dev = ts->dev;
@@ -183,8 +242,10 @@ int __devinit __max730x_probe(struct max
 
 	ts->chip.direction_input = max7301_direction_input;
 	ts->chip.get = max7301_get;
+	ts->chip.get_block = max7301_get_block;
 	ts->chip.direction_output = max7301_direction_output;
 	ts->chip.set = max7301_set;
+	ts->chip.set_block = max7301_set_block;
 
 	ts->chip.base = pdata->base;
 	ts->chip.ngpio = PIN_NUMBER;
--- linux-2.6.orig/drivers/gpio/gpio-max732x.c
+++ linux-2.6/drivers/gpio/gpio-max732x.c
@@ -219,6 +219,63 @@ out:
 	mutex_unlock(&chip->lock);
 }
 
+static unsigned long max732x_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct max732x_chip *chip;
+	uint8_t lo = 0;
+	uint8_t hi = 0;
+	int ret;
+
+	chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+	if (mask & 0xFF) {
+		ret = max732x_readb(chip, is_group_a(chip, 0), &lo);
+		if (ret < 0)
+			return 0;
+	}
+	if (mask & 0xFF00) {
+		ret = max732x_readb(chip, is_group_a(chip, 8), &hi);
+		if (ret < 0)
+			return 0;
+	}
+
+	return (((unsigned long)hi << 8) | lo) & mask;
+}
+
+static void max732x_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct max732x_chip *chip;
+	uint8_t reg_out;
+	uint8_t lo_mask = mask & 0xFF;
+	uint8_t hi_mask = (mask >> 8) & 0xFF;
+	int ret;
+
+	chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+	mutex_lock(&chip->lock);
+
+	if (lo_mask) {
+		reg_out = (chip->reg_out[0] & ~lo_mask) | (values & lo_mask);
+		ret = max732x_writeb(chip, is_group_a(chip, 0), reg_out);
+		if (ret < 0)
+			goto out;
+		chip->reg_out[0] = reg_out;
+	}
+	if (hi_mask) {
+		reg_out = (chip->reg_out[1] & ~hi_mask) |
+			((values >> 8) & hi_mask);
+		ret = max732x_writeb(chip, is_group_a(chip, 8), reg_out);
+		if (ret < 0)
+			goto out;
+		chip->reg_out[1] = reg_out;
+	}
+
+out:
+	mutex_unlock(&chip->lock);
+}
+
 static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
 {
 	struct max732x_chip *chip;
@@ -562,8 +619,10 @@ static int __devinit max732x_setup_gpio(
 	if (chip->dir_output) {
 		gc->direction_output = max732x_gpio_direction_output;
 		gc->set = max732x_gpio_set_value;
+		gc->set_block = max732x_gpio_set_block;
 	}
 	gc->get = max732x_gpio_get_value;
+	gc->get_block = max732x_gpio_get_block;
 	gc->can_sleep = 1;
 
 	gc->base = gpio_start;
--- linux-2.6.orig/drivers/gpio/gpio-pca953x.c
+++ linux-2.6/drivers/gpio/gpio-pca953x.c
@@ -300,6 +300,68 @@ exit:
 	mutex_unlock(&chip->i2c_lock);
 }
 
+static unsigned long pca953x_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct pca953x_chip *chip;
+	u32 reg_val;
+	int ret, offset = 0;
+
+	chip = container_of(gc, struct pca953x_chip, gpio_chip);
+
+	mutex_lock(&chip->i2c_lock);
+	switch (chip->chip_type) {
+	case PCA953X_TYPE:
+		offset = PCA953X_INPUT;
+		break;
+	case PCA957X_TYPE:
+		offset = PCA957X_IN;
+		break;
+	}
+	ret = pca953x_read_reg(chip, offset, &reg_val);
+	mutex_unlock(&chip->i2c_lock);
+	if (ret < 0) {
+		/* NOTE:  diagnostic already emitted; that's all we should
+		 * do unless gpio_*_value_cansleep() calls become different
+		 * from their nonsleeping siblings (and report faults).
+		 */
+		return 0;
+	}
+
+	return reg_val & mask;
+}
+
+static void pca953x_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct pca953x_chip *chip;
+	u32 reg_val;
+	int ret, offset = 0;
+
+	chip = container_of(gc, struct pca953x_chip, gpio_chip);
+
+	mutex_lock(&chip->i2c_lock);
+
+	reg_val = chip->reg_output & ~mask;
+	reg_val |= values & mask;
+
+	switch (chip->chip_type) {
+	case PCA953X_TYPE:
+		offset = PCA953X_OUTPUT;
+		break;
+	case PCA957X_TYPE:
+		offset = PCA957X_OUT;
+		break;
+	}
+	ret = pca953x_write_reg(chip, offset, reg_val);
+	if (ret)
+		goto exit;
+
+	chip->reg_output = reg_val;
+exit:
+	mutex_unlock(&chip->i2c_lock);
+}
+
 static void pca953x_setup_gpio(struct pca953x_chip *chip, int gpios)
 {
 	struct gpio_chip *gc;
@@ -310,6 +372,8 @@ static void pca953x_setup_gpio(struct pc
 	gc->direction_output = pca953x_gpio_direction_output;
 	gc->get = pca953x_gpio_get_value;
 	gc->set = pca953x_gpio_set_value;
+	gc->get_block = pca953x_gpio_get_block;
+	gc->set_block = pca953x_gpio_set_block;
 	gc->can_sleep = 1;
 
 	gc->base = chip->gpio_start;
--- linux-2.6.orig/drivers/gpio/gpio-pcf857x.c
+++ linux-2.6/drivers/gpio/gpio-pcf857x.c
@@ -158,6 +158,28 @@ static void pcf857x_set(struct gpio_chip
 	pcf857x_output(chip, offset, value);
 }
 
+static unsigned long pcf857x_get_block(struct gpio_chip *chip,
+				       unsigned long mask)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+	int		value;
+
+	value = gpio->read(gpio->client);
+	return (value < 0) ? 0 : (value & mask);
+}
+
+static void pcf857x_set_block(struct gpio_chip *chip, unsigned long mask,
+			      unsigned long values)
+{
+	struct pcf857x	*gpio = container_of(chip, struct pcf857x, chip);
+
+	mutex_lock(&gpio->lock);
+	gpio->out &= ~mask;
+	gpio->out |= values & mask;
+	gpio->write(gpio->client, gpio->out);
+	mutex_unlock(&gpio->lock);
+}
+
 /*-------------------------------------------------------------------------*/
 
 static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset)
@@ -280,6 +302,8 @@ static int pcf857x_probe(struct i2c_clie
 	gpio->chip.owner		= THIS_MODULE;
 	gpio->chip.get			= pcf857x_get;
 	gpio->chip.set			= pcf857x_set;
+	gpio->chip.get_block		= pcf857x_get_block;
+	gpio->chip.set_block		= pcf857x_set_block;
 	gpio->chip.direction_input	= pcf857x_input;
 	gpio->chip.direction_output	= pcf857x_output;
 	gpio->chip.ngpio		= id->driver_data;
--- linux-2.6.orig/drivers/gpio/gpio-pch.c
+++ linux-2.6/drivers/gpio/gpio-pch.c
@@ -122,6 +122,23 @@ static void pch_gpio_set(struct gpio_chi
 	spin_unlock_irqrestore(&chip->spinlock, flags);
 }
 
+static void pch_gpio_set_block(struct gpio_chip *gpio, unsigned long mask,
+			       unsigned long values)
+{
+	u32 reg_val;
+	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->spinlock, flags);
+	reg_val = ioread32(&chip->reg->po);
+
+	reg_val &= ~mask;
+	reg_val |= values & mask;
+
+	iowrite32(reg_val, &chip->reg->po);
+	spin_unlock_irqrestore(&chip->spinlock, flags);
+}
+
 static int pch_gpio_get(struct gpio_chip *gpio, unsigned nr)
 {
 	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
@@ -129,6 +146,14 @@ static int pch_gpio_get(struct gpio_chip
 	return ioread32(&chip->reg->pi) & (1 << nr);
 }
 
+static unsigned long pch_gpio_get_block(struct gpio_chip *gpio,
+					unsigned long mask)
+{
+	struct pch_gpio *chip =	container_of(gpio, struct pch_gpio, gpio);
+
+	return ioread32(&chip->reg->pi) & mask;
+}
+
 static int pch_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,
 				     int val)
 {
@@ -218,8 +243,10 @@ static void pch_gpio_setup(struct pch_gp
 	gpio->owner = THIS_MODULE;
 	gpio->direction_input = pch_gpio_direction_input;
 	gpio->get = pch_gpio_get;
+	gpio->get_block = pch_gpio_get_block;
 	gpio->direction_output = pch_gpio_direction_output;
 	gpio->set = pch_gpio_set;
+	gpio->set_block = pch_gpio_set_block;
 	gpio->dbg_show = NULL;
 	gpio->base = -1;
 	gpio->ngpio = gpio_pins[chip->ioh];
--- linux-2.6.orig/drivers/gpio/gpio-pl061.c
+++ linux-2.6/drivers/gpio/gpio-pl061.c
@@ -123,6 +123,21 @@ static void pl061_set_value(struct gpio_
 	writeb(!!value << offset, chip->base + (1 << (offset + 2)));
 }
 
+static unsigned long pl061_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+	return !!readb(chip->base + (mask << 2));
+}
+
+static void pl061_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
+
+	writeb(values & mask, chip->base + (mask << 2));
+}
+
 static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
 {
 	struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
@@ -256,6 +271,8 @@ static int pl061_probe(struct amba_devic
 	chip->gc.direction_output = pl061_direction_output;
 	chip->gc.get = pl061_get_value;
 	chip->gc.set = pl061_set_value;
+	chip->gc.get_block = pl061_get_block;
+	chip->gc.set_block = pl061_set_block;
 	chip->gc.to_irq = pl061_to_irq;
 	chip->gc.ngpio = PL061_GPIO_NR;
 	chip->gc.label = dev_name(&dev->dev);
--- linux-2.6.orig/drivers/gpio/gpio-twl6040.c
+++ linux-2.6/drivers/gpio/gpio-twl6040.c
@@ -46,6 +46,19 @@ static int twl6040gpo_get(struct gpio_ch
 	return (ret >> offset) & 1;
 }
 
+static unsigned long twl6040gpo_get_block(struct gpio_chip *chip,
+					  unsigned long mask)
+{
+	struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+	int ret = 0;
+
+	ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+	if (ret < 0)
+		return 0;
+
+	return ret & mask;
+}
+
 static int twl6040gpo_direction_out(struct gpio_chip *chip, unsigned offset,
 				    int value)
 {
@@ -71,12 +84,31 @@ static void twl6040gpo_set(struct gpio_c
 	twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
 }
 
+static void twl6040gpo_set_block(struct gpio_chip *chip, unsigned long mask,
+				 unsigned long values)
+{
+	struct twl6040 *twl6040 = dev_get_drvdata(chip->dev->parent);
+	int ret;
+	u8 gpoctl;
+
+	ret = twl6040_reg_read(twl6040, TWL6040_REG_GPOCTL);
+	if (ret < 0)
+		return;
+
+	gpoctl = ret & ~mask;
+	gpoctl |= values & mask;
+
+	twl6040_reg_write(twl6040, TWL6040_REG_GPOCTL, gpoctl);
+}
+
 static struct gpio_chip twl6040gpo_chip = {
 	.label			= "twl6040",
 	.owner			= THIS_MODULE,
 	.get			= twl6040gpo_get,
+	.get_block		= twl6040gpo_get_block,
 	.direction_output	= twl6040gpo_direction_out,
 	.set			= twl6040gpo_set,
+	.set_block		= twl6040gpo_set_block,
 	.can_sleep		= 1,
 };
 
--- linux-2.6.orig/drivers/gpio/gpio-ucb1400.c
+++ linux-2.6/drivers/gpio/gpio-ucb1400.c
@@ -45,6 +45,27 @@ static void ucb1400_gpio_set(struct gpio
 	ucb1400_gpio_set_value(gpio->ac97, off, val);
 }
 
+static unsigned long ucb1400_gpio_get_block(struct gpio_chip *gc,
+					    unsigned long mask)
+{
+	struct ucb1400_gpio *gpio;
+	gpio = container_of(gc, struct ucb1400_gpio, gc);
+	return ucb1400_reg_read(gpio->ac97, UCB_IO_DATA) & mask;
+}
+
+static void ucb1400_gpio_set_block(struct gpio_chip *gc, unsigned long mask,
+				   unsigned long values)
+{
+	struct ucb1400_gpio *gpio;
+	u16 tmp;
+	gpio = container_of(gc, struct ucb1400_gpio, gc);
+
+	tmp = ucb1400_reg_read(gpio->ac97, UCB_IO_DATA) & ~mask;
+	tmp |= values & mask;
+
+	ucb1400_reg_write(gpio->ac97, UCB_IO_DATA, tmp);
+}
+
 static int ucb1400_gpio_probe(struct platform_device *dev)
 {
 	struct ucb1400_gpio *ucb = dev->dev.platform_data;
@@ -66,6 +87,8 @@ static int ucb1400_gpio_probe(struct pla
 	ucb->gc.direction_output = ucb1400_gpio_dir_out;
 	ucb->gc.get = ucb1400_gpio_get;
 	ucb->gc.set = ucb1400_gpio_set;
+	ucb->gc.get_block = ucb1400_gpio_get_block;
+	ucb->gc.set_block = ucb1400_gpio_set_block;
 	ucb->gc.can_sleep = 1;
 
 	err = gpiochip_add(&ucb->gc);
--- linux-2.6.orig/drivers/gpio/gpio-vt8500.c
+++ linux-2.6/drivers/gpio/gpio-vt8500.c
@@ -209,6 +209,28 @@ static void vt8500_gpio_set_value(struct
 	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
 }
 
+static unsigned long vt8500_gpio_get_block(struct gpio_chip *chip,
+					   unsigned long mask)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	return readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) &
+								mask;
+}
+
+static void vt8500_gpio_set_block(struct gpio_chip *chip, unsigned long mask,
+				  unsigned long  values)
+{
+	struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+	u32 val = readl_relaxed(vt8500_chip->base +
+				vt8500_chip->regs->data_out);
+	val &= ~mask;
+	val |= values & mask;
+
+	writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
+}
+
 static int vt8500_of_xlate(struct gpio_chip *gc,
 			    const struct of_phandle_args *gpiospec, u32 *flags)
 {
@@ -251,6 +273,8 @@ static int vt8500_add_chips(struct platf
 		chip->direction_output = vt8500_gpio_direction_output;
 		chip->get = vt8500_gpio_get_value;
 		chip->set = vt8500_gpio_set_value;
+		chip->get_block = vt8500_gpio_get_block;
+		chip->set_block = vt8500_gpio_set_block;
 		chip->can_sleep = 0;
 		chip->base = pin_cnt;
 		chip->ngpio = data->banks[i].ngpio;
--- linux-2.6.orig/drivers/gpio/gpio-xilinx.c
+++ linux-2.6/drivers/gpio/gpio-xilinx.c
@@ -49,6 +49,21 @@ static int xgpio_get(struct gpio_chip *g
 }
 
 /**
+ * xgpio_get_block - Read a block of specified signals of the GPIO device.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit mask (bit 0 == 0th GPIO) for GPIOs to get
+ *
+ * This function reads a block of specified signal of the GPIO device, returned
+ * as a bit mask, each bit representing a GPIO
+ */
+static unsigned long xgpio_get_block(struct gpio_chip *gc, unsigned long mask)
+{
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+
+	return in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) & mask;
+}
+
+/**
  * xgpio_set - Write the specified signal of the GPIO device.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -77,6 +92,33 @@ static void xgpio_set(struct gpio_chip *
 }
 
 /**
+ * xgpio_set_block - Write a block of specified signals of the GPIO device.
+ * @gc:     Pointer to gpio_chip device structure.
+ * @mask:   Bit mask (bit 0 == 0th GPIO) for GPIOs to set
+ * @values: Bit mapped values
+ *
+ * This function writes the specified values in to the specified signals of the
+ * GPIO device.
+ */
+static void xgpio_set_block(struct gpio_chip *gc, unsigned long mask,
+			    unsigned long values)
+{
+	unsigned long flags;
+	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
+	struct xgpio_instance *chip =
+	    container_of(mm_gc, struct xgpio_instance, mmchip);
+
+	spin_lock_irqsave(&chip->gpio_lock, flags);
+
+	chip->gpio_state &= ~mask;
+	chip->gpio_state |= mask & values;
+
+	out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state);
+
+	spin_unlock_irqrestore(&chip->gpio_lock, flags);
+}
+
+/**
  * xgpio_dir_in - Set the direction of the specified GPIO signal as input.
  * @gc:     Pointer to gpio_chip device structure.
  * @gpio:   GPIO signal number.
@@ -195,6 +237,8 @@ static int __devinit xgpio_of_probe(stru
 	chip->mmchip.gc.direction_output = xgpio_dir_out;
 	chip->mmchip.gc.get = xgpio_get;
 	chip->mmchip.gc.set = xgpio_set;
+	chip->mmchip.gc.get_block = xgpio_get_block;
+	chip->mmchip.gc.set_block = xgpio_set_block;
 
 	chip->mmchip.save_regs = xgpio_save_regs;
 

^ permalink raw reply

* [PATCH] ARM: kirkwood: Increase NAND chip-delay for DNS-320
From: Jamie Lentin @ 2012-10-28 20:47 UTC (permalink / raw)
  To: linux-arm-kernel

The default chip-delay of 25ns is a bit too tight for some DNS-320's,
increase to 35ns.

Signed-off-by: Jamie Lentin <jm@lentin.co.uk>
---
I now own 2 DNS-320's, the first of which is happy with a mainline
kernel, the second fills the console with "Bad eraseblock xxx at
0x00000xxxxxxx" for every eraseblock and refuses to access the NAND.
Beyond this they appear identical, and report the same NAND chip
(I haven't physically checked that it's the same, however).

The patch below fixes it, however:-
* Is there something else I should be trying, rather than potentially
masking the problem?
* Is chip-delay too low for kirkwood generally?

Cheers,

 arch/arm/boot/dts/kirkwood-dns320.dts |    4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/kirkwood-dns320.dts b/arch/arm/boot/dts/kirkwood-dns320.dts
index 5bb0bf3..abe17a4 100644
--- a/arch/arm/boot/dts/kirkwood-dns320.dts
+++ b/arch/arm/boot/dts/kirkwood-dns320.dts
@@ -50,5 +50,9 @@
 			clock-frequency = <166666667>;
 			status = "okay";
 		};
+
+		nand at 3000000 {
+			chip-delay = <35>;
+		};
 	};
 };
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH] ARM: kirkwood: Increase NAND chip-delay for DNS-320
From: Andrew Lunn @ 2012-10-28 20:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351457262-11506-1-git-send-email-jm@lentin.co.uk>

On Sun, Oct 28, 2012 at 08:47:42PM +0000, Jamie Lentin wrote:
> The default chip-delay of 25ns is a bit too tight for some DNS-320's,
> increase to 35ns.
> 
> Signed-off-by: Jamie Lentin <jm@lentin.co.uk>
> ---
> I now own 2 DNS-320's, the first of which is happy with a mainline
> kernel, the second fills the console with "Bad eraseblock xxx at
> 0x00000xxxxxxx" for every eraseblock and refuses to access the NAND.
> Beyond this they appear identical, and report the same NAND chip
> (I haven't physically checked that it's the same, however).
> 
> The patch below fixes it, however:-
> * Is there something else I should be trying, rather than potentially
> masking the problem?
> * Is chip-delay too low for kirkwood generally?

Do you know what NAND chip it is? We can check the datasheet and see
what is specified.

All kirkwood's use 25ns and i don't think there have been any problems
reported. But that does not mean Dlink have mounted a slower device.

	  Andrew

^ permalink raw reply

* [PATCH 0/9] ARM: Kirkwood: Convert to pinctrl
From: Jamie Lentin @ 2012-10-28 21:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121028165711.GG15824@lunn.ch>

On Sun, 28 Oct 2012, Andrew Lunn wrote:

> On Wed, Oct 24, 2012 at 04:53:45PM +0200, Andrew Lunn wrote:
>> This patchset converts all DT kirkwood boards to using pinctrl.
>
> Thanks for the feedback everybody gave.
>
> Progress has been made:
>
> The bug causing the lockups has been found, fixed and passed upstream.
>
> I factored out the repeated pinmux definitions for sata0, sata1, spi,
> twsi0, uart0, uart1.
>
> I fixed a few types reported by testers.
>
> I found a solution to many of the gpio_request() failures. The ones
> for controlling power to SATA or USB i've replaced with a regulator in
> DT. The regulator will grab the GPIO, set it to output, and driver it
> high/low as required. I just hope we don't have an ordering issue.
> The regulator framework seems to load quite early and probes all the
> regulators. This fails, because pinctrl has not yet loaded, so the
> gpio framework returns EPROBE_DEFER. This is not fatal to the
> regulator framework, it tries again later. But this later is after
> most of the rest of the drivers have loaded. So we might get into
> issues where uboot has not enabled power to USB/SATA, the driver is
> probed and fails because of the lack of power, and once things of gone
> wrong, we turn the power on.
>
> I still expect problems with GPIOs used for power off. I've not yet
> touched them. Again, a regulator makes sense, but there is no 'out of
> the box' regulator type or property which adds itself to pm_power_off.
>
> The last problem is:
>
>        /* Set NAS to turn back on after a power failure */
>        dnskw_gpio_register(37, "dnskw:power:recover", 1);
>
> I think its a bit of a stretch calling this a regulator.

Definitely.

> Since the current patchset is 18 patches, plus a few other bug fixes
> and dependencies, i'm not going to post them all here. I think we are
> still at the smoke/no-smoke stage of testing. Please pull from:
>
> git://github.com/lunn/linux.git v3.7-rc2-pinctrl-v2
>
> and let me know if things work, what typo's need fixing, where the
> smoke comes out, etc.

No smoke came out, the SATA ports were both powered up. Beyond what's 
already known about, no problems.

>
>      Thanks
> 	Andrew
>

-- 
Jamie Lentin

^ permalink raw reply

* [PATCH] spi/pl022: Activate resourses before deactivate them in suspend
From: Alan Stern @ 2012-10-28 21:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAPDyKFoOJz6608b4z2PoW8ODvTJVZRq-yB0itfXnuoXdR1SpGQ@mail.gmail.com>

On Sun, 28 Oct 2012, Ulf Hansson wrote:

> On 28 October 2012 20:52, Linus Walleij <linus.walleij@linaro.org> wrote:
> > On Sat, Oct 27, 2012 at 11:46 PM, Mark Brown
> > <broonie@opensource.wolfsonmicro.com> wrote:
> >> On Fri, Oct 05, 2012 at 09:43:32AM +0200, Ulf Hansson wrote:
> >>
> >>> To be able to deactivate resourses in suspend, the resourses must
> >>> first be surely active. This is done with a pm_runtime_get_sync.
> >>> Once the resourses are restored to active state again in resume,
> >>> the runtime pm usage count can be decreased with a pm_runtime_put.
> >>
> >> The PM core will ensure devices are runtime resumed before we enter
> >> suspend precisely due to this sort of issue.
> >
> > I asked the very same question to Ulf (in speech, sorry
> > so you couldn't see it...)
> >
> > So I guess we are talking about drivers/base/main.c
> >
> > in device_prepare()
> > pm_runtime_get_noresume() is called
> 
> This will increase the "usage_counter" for the device. It will not
> "runtime_resume" the device, though it will prevent it from being
> "runtime_suspended".

Indeed.  The PM core does _not_ insure that devices are runtime resumed
before going into system suspend.  Some subsystems may do this (the PCI
subsystem does, for example -- see
drivers/pci/pci-driver.c:pci_pm_prepare()), but the PM core doesn't.

Alan Stern

^ permalink raw reply

* [PATCH RESEND 1/2] clk: vt8500: Fix SDMMC clock special cases
From: Tony Prisk @ 2012-10-28 21:40 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds some additional handling for the SDMMC special case
in round_rate and set_rate which results in invalid divisor messages
at boot time.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
 drivers/clk/clk-vt8500.c |   18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c
index a885600..fe25570 100644
--- a/drivers/clk/clk-vt8500.c
+++ b/drivers/clk/clk-vt8500.c
@@ -120,8 +120,17 @@ static unsigned long vt8500_dclk_recalc_rate(struct clk_hw *hw,
 static long vt8500_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
 				unsigned long *prate)
 {
+	struct clk_device *cdev = to_clk_device(hw);
 	u32 divisor = *prate / rate;
 
+	/*
+	 * If this is a request for SDMMC we have to adjust the divisor
+	 * when >31 to use the fixed predivisor
+	 */
+	if ((cdev->div_mask == 0x3F) && (divisor > 31)) {
+		divisor = 64 * ((divisor / 64) + 1);
+	}
+
 	return *prate / divisor;
 }
 
@@ -135,6 +144,15 @@ static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
 	if (divisor == cdev->div_mask + 1)
 		divisor = 0;
 
+	/* SDMMC mask may need to be corrected before testing if its valid */
+	if ((cdev->div_mask == 0x3F) && (divisor > 31)) {
+		/*
+		 * Bit 5 is a fixed /64 predivisor. If the requested divisor
+		 * is >31 then correct for the fixed divisor being required.
+		 */
+		divisor = 0x20 + (divisor / 64);
+	}
+
 	if (divisor > cdev->div_mask) {
 		pr_err("%s: invalid divisor for clock\n", __func__);
 		return -EINVAL;
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 2/2] clk: vt8500: Remove dependancy on pmc_base
From: Tony Prisk @ 2012-10-28 21:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351460431-1048-1-git-send-email-linux@prisktech.co.nz>

This patch removes the requirement to pass a Power Management
Controller base address to vtwm_clk_init(), in preparation of
separating the power management code from arch-vt8500/vt8500.c.

Signed-off-by: Tony Prisk <linux@prisktech.co.nz>
---
Mike,

Do you mind if this patch goes through arm-soc, given it is self-contained,
so I can base the other PM changes off it?

Regards
Tony P

 arch/arm/mach-vt8500/common.h |    2 +-
 arch/arm/mach-vt8500/vt8500.c |    2 +-
 drivers/clk/clk-vt8500.c      |   20 ++++++++++++++++----
 3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/arch/arm/mach-vt8500/common.h b/arch/arm/mach-vt8500/common.h
index 2b24196..e38b17f 100644
--- a/arch/arm/mach-vt8500/common.h
+++ b/arch/arm/mach-vt8500/common.h
@@ -23,6 +23,6 @@ int __init vt8500_irq_init(struct device_node *node,
 				struct device_node *parent);
 
 /* defined in drivers/clk/clk-vt8500.c */
-void __init vtwm_clk_init(void __iomem *pmc_base);
+void __init vtwm_clk_init(void);
 
 #endif
diff --git a/arch/arm/mach-vt8500/vt8500.c b/arch/arm/mach-vt8500/vt8500.c
index 8d3871f..0ee2a16 100644
--- a/arch/arm/mach-vt8500/vt8500.c
+++ b/arch/arm/mach-vt8500/vt8500.c
@@ -162,7 +162,7 @@ void __init vt8500_init(void)
 	else
 		pr_err("%s: PMC Hibernation register could not be remapped, not enabling power off!\n", __func__);
 
-	vtwm_clk_init(pmc_base);
+	vtwm_clk_init();
 
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
 }
diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c
index fe25570..820f39a 100644
--- a/drivers/clk/clk-vt8500.c
+++ b/drivers/clk/clk-vt8500.c
@@ -15,6 +15,7 @@
 
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/slab.h>
 #include <linux/bitops.h>
 #include <linux/clkdev.h>
@@ -517,12 +518,23 @@ static const __initconst struct of_device_id clk_match[] = {
 	{ /* sentinel */ }
 };
 
-void __init vtwm_clk_init(void __iomem *base)
+void __init vtwm_clk_init(void)
 {
-	if (!base)
-		return;
+	struct device_node *np =
+			of_find_compatible_node(NULL, NULL, "via,vt8500-pmc");
+
+	if (!np) {
+		pr_err("%s: power management device node missing\n", __func__);
+		return -ENODEV;
+	}
 
-	pmc_base = base;
+	pmc_base = of_iomap(np, 0);
+	of_node_put(np);
+
+	if (!pmc_base) {
+		pr_err("%s: of_iomap(pmc) failed\n", __func__);
+		return -ENOMEM;
+	}
 
 	of_clk_init(clk_match);
 }
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH V3 1/3] clk: mvebu: add armada-370-xp specific clocks
From: Sebastian Hesselbarth @ 2012-10-28 21:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1350303499-30868-2-git-send-email-gregory.clement@free-electrons.com>

On 10/15/2012 02:18 PM, Gregory CLEMENT wrote:
> Add Armada 370/XP specific clocks: core clocks and CPU clocks.
>
> The CPU clocks are only for Armada XP for the SMP mode.
>
> ...
> +static struct core_clk_fn armada_370_clk_fn = {
> +	.get_tclk_freq = armada_370_get_tclk_freq,
> +	.get_pck_freq = armada_370_get_pck_freq,
> +	.get_fab_freq_opt = armada_370_get_fab_freq_opt,
> +};
> +
> +static struct core_clk_fn armada_xp_clk_fn = {
> +	.get_tclk_freq = armada_xp_get_tclk_freq,
> +	.get_pck_freq = armada_xp_get_pck_freq,
> +	.get_fab_freq_opt = armada_xp_get_fab_freq_opt,
> +};
> +
> +static const __initconst struct of_device_id clk_match[] = {
> +	{
> +	 .compatible = "marvell,armada-370-core-clockctrl",
> +	 .data =&armada_370_clk_fn,
> +	 },
> +
> +	{
> +	 .compatible = "marvell,armada-xp-core-clockctrl",
> +	 .data =&armada_xp_clk_fn,
> +	 },
> +	{
> +	 /* sentinel */
> +	 }
> +};

Gregory,

armada_370_clk_fn and armada_xp_clk_fn cause section mismatches
as they are referenced within __initconst.

You should either rename them to armada_xp_clk_ops or annotate them
with the appropriate attribute.

Sebastian

^ permalink raw reply

* [PATCH] ARM: kirkwood: Increase NAND chip-delay for DNS-320
From: Jamie Lentin @ 2012-10-28 21:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20121028205956.GH15143@lunn.ch>

On Sun, 28 Oct 2012, Andrew Lunn wrote:

> On Sun, Oct 28, 2012 at 08:47:42PM +0000, Jamie Lentin wrote:
>> The default chip-delay of 25ns is a bit too tight for some DNS-320's,
>> increase to 35ns.
>>
>> Signed-off-by: Jamie Lentin <jm@lentin.co.uk>
>> ---
>> I now own 2 DNS-320's, the first of which is happy with a mainline
>> kernel, the second fills the console with "Bad eraseblock xxx at
>> 0x00000xxxxxxx" for every eraseblock and refuses to access the NAND.
>> Beyond this they appear identical, and report the same NAND chip
>> (I haven't physically checked that it's the same, however).
>>
>> The patch below fixes it, however:-
>> * Is there something else I should be trying, rather than potentially
>> masking the problem?
>> * Is chip-delay too low for kirkwood generally?
>
> Do you know what NAND chip it is? We can check the datasheet and see
> what is specified.

The chip markings are: SAMSUNG 146 K9F1G08U0D SCB0 (FKJ554PWN)

Unfortunately the other NAS is too far away to get at, but D-link haven't 
stuck to exactly the same chip. This DNS-320 review shows a different 
Samsung part:

http://diit.cz/data/images/thumb/67491_2b9e5e4fbd.jpg?1307884464

The datasheet for the K9F1G08U0D says max 35usecs "Data Transfer from Cell 
to Register", whereas K9F1G08U0C (what the DNS-320 review shows) says 
25usecs. If this is the value in the datasheet to check, then this would 
explain the problems.

> All kirkwood's use 25ns and i don't think there have been any problems
> reported. But that does not mean Dlink have mounted a slower device.

I've not had anyone with this NAS report similar problems. It could just 
be I've been particuarly lucky...

>
> 	  Andrew
>

-- 
Jamie Lentin

^ permalink raw reply

* [PATCH v2 1/5] ARM: PXA: Add z2-usb-switch driver
From: Marek Vasut @ 2012-10-28 21:57 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351438946-5331-1-git-send-email-anarsoul@gmail.com>

Dear Vasily Khoruzhick,

> This driver controls mode of USB port #2 pins - device or host.

Please supply proper commit message. This short message describes nothing.

[...]

> @@ -0,0 +1,100 @@
> +/*
> + * USB mode switcher for Z2
> + *
> + * Copyright (c) 2011 Vasily Khoruzhick

2012

> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +
> +#include <mach/pxa27x.h>
> +#include <mach/pxa27x-udc.h>
> +
> +#include <asm/io.h>
> +
> +#define MIN(a, b) ((a) < (b) ? (a) : (b))

min() is already defined in kernel.h

> +static ssize_t usb_mode_show(struct device *dev, struct device_attribute
> *attr, +	char *buf)
> +{
> +	if (UP2OCR & UP2OCR_HXS)
> +		return sprintf(buf, "host\n");
> +	else
> +		return sprintf(buf, "device\n");
> +}
> +
> +static ssize_t usb_mode_set(struct device *dev, struct device_attribute
> *attr, +	const char *buf, size_t count)
> +{
> +	if (strncmp(buf, "host", MIN(count, 4)) == 0) {
> +		UP2OCR = UP2OCR_HXS | UP2OCR_HXOE | UP2OCR_DPPDE | UP2OCR_DMPDE;
> +		return count;
> +	} else if (strncmp(buf, "device", MIN(count, 6)) == 0) {
> +		UP2OCR = UP2OCR_HXOE | UP2OCR_DPPUE;
> +		return count;
> +	}
> +	return -EINVAL;
> +}
> +
> +static DEVICE_ATTR(usb_mode, 0644, usb_mode_show, usb_mode_set);

I wonder if we have no better means to control enforcement of mode.

> +static const struct attribute *attrs[] = {
> +	&dev_attr_usb_mode.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group attr_group = {
> +	.attrs	= (struct attribute **)attrs,
> +};

Isn't there some macro to do this assignment?

> +static int z2_usb_switch_probe(struct platform_device *dev)

Missing __devinit

> +{
> +	int res;
> +
> +	res = sysfs_create_group(&dev->dev.kobj, &attr_group);
> +	if (res)
> +		return res;
> +
> +	UP2OCR = UP2OCR_HXOE | UP2OCR_DPPUE;
> +
> +	return 0;
> +}
> +
> +static int __devexit z2_usb_switch_remove(struct platform_device *dev)
> +{
> +	UP2OCR = UP2OCR_HXOE;
> +	sysfs_remove_group(&dev->dev.kobj, &attr_group);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver z2_usb_switch_driver = {
> +	.probe = z2_usb_switch_probe,
> +	.remove = __devexit_p(z2_usb_switch_remove),
> +
> +	.driver = {
> +		.name = "z2-usb-switch",
> +		.owner = THIS_MODULE,
> +	},
> +};
> +
> +
> +static int __init z2_usb_switch_init(void)
> +{
> +	return platform_driver_register(&z2_usb_switch_driver);
> +}
> +
> +static void __exit z2_usb_switch_exit(void)
> +{
> +	platform_driver_unregister(&z2_usb_switch_driver);
> +}
> +
> +module_init(z2_usb_switch_init);
> +module_exit(z2_usb_switch_exit);

module_platform_driver()

Best regards,
Marek Vasut

^ permalink raw reply

* [PATCH 2/5] ARM: PXA: Zipit Z2: Add USB host and device support
From: Marek Vasut @ 2012-10-28 21:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351438555-4668-2-git-send-email-anarsoul@gmail.com>

Dear Vasily Khoruzhick,

missing commit message.

> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> ---
>  arch/arm/mach-pxa/z2.c | 52
> ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52
> insertions(+)
> 
> diff --git a/arch/arm/mach-pxa/z2.c b/arch/arm/mach-pxa/z2.c
> index c97485f..ce90fa9 100644
> --- a/arch/arm/mach-pxa/z2.c
> +++ b/arch/arm/mach-pxa/z2.c
> @@ -41,6 +41,9 @@
>  #include <linux/platform_data/mmc-pxamci.h>
>  #include <linux/platform_data/keypad-pxa27x.h>
>  #include <mach/pm.h>
> +#include <mach/pxa27x-udc.h>
> +#include <mach/udc.h>
> +#include <linux/platform_data/usb-ohci-pxa27x.h>
> 
>  #include "generic.h"
>  #include "devices.h"
> @@ -680,6 +683,52 @@ static void __init z2_pmic_init(void)
>  static inline void z2_pmic_init(void) {}
>  #endif
> 
> +/*************************************************************************
> ***** + * USB Switch
> +
> **************************************************************************
> ****/ +static struct platform_device z2_usb_switch = {
> +	.name		= "z2-usb-switch",
> +	.id		= -1,
> +};
> +
> +static void __init z2_usb_switch_init(void)
> +{
> +	platform_device_register(&z2_usb_switch);
> +}
> +
> +/*************************************************************************
> ***** + * USB Gadget
> +
> **************************************************************************
> ****/ +#if defined(CONFIG_USB_GADGET_PXA27X) \
> +	|| defined(CONFIG_USB_GADGET_PXA27X_MODULE)
> +static int z2_udc_is_connected(void)
> +{
> +	return 1;
> +}
> +
> +static struct pxa2xx_udc_mach_info z2_udc_info __initdata = {
> +	.udc_is_connected	= z2_udc_is_connected,
> +	.gpio_pullup		= -1,
> +};
> +
> +static void __init z2_udc_init(void)
> +{
> +	pxa_set_udc_info(&z2_udc_info);
> +}
> +#else
> +static inline void z2_udc_init(void) {}
> +#endif

We really should work on the DT here.

> +/*************************************************************************
> ***** + * USB Host (OHCI)
> +
> **************************************************************************
> ****/ +static struct pxaohci_platform_data z2_ohci_platform_data = {
> +	.port_mode	= PMM_PERPORT_MODE,
> +	.flags		= ENABLE_PORT2 | NO_OC_PROTECTION,
> +	.power_on_delay	= 10,
> +	.power_budget	= 500,
> +};
> +
>  #ifdef CONFIG_PM
>  static void z2_power_off(void)
>  {
> @@ -705,10 +754,12 @@ static void __init z2_init(void)
>  	pxa_set_ffuart_info(NULL);
>  	pxa_set_btuart_info(NULL);
>  	pxa_set_stuart_info(NULL);
> +	pxa_set_ohci_info(&z2_ohci_platform_data);
> 
>  	z2_lcd_init();
>  	z2_mmc_init();
>  	z2_mkp_init();
> +	z2_udc_init();

This patch adds _host_ ? So why do you have udc in here ?

Besides, pxa_set_ohci_info() should also be wrapped in some z2_uhc_init()

>  	z2_i2c_init();
>  	z2_spi_init();
>  	z2_nor_init();
> @@ -716,6 +767,7 @@ static void __init z2_init(void)
>  	z2_leds_init();
>  	z2_keys_init();
>  	z2_pmic_init();
> +	z2_usb_switch_init();
> 
>  	pm_power_off = z2_power_off;
>  }

Best regards,
Marek Vasut

^ permalink raw reply

* [PATCH 4/5] ARM: PXA: Zipit Z2: Change active_state of power button
From: Marek Vasut @ 2012-10-28 22:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351438555-4668-4-git-send-email-anarsoul@gmail.com>

Dear Vasily Khoruzhick,

> From: mcmajeres <mark@engine12.com>

Read [1]

> Otherwise userspace might be confused about button state
> 
> Signed-off-by: mcmajeres <mark@engine12.com>

Attribution of code shall be done only towards people known by their name.

> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> ---
>  arch/arm/mach-pxa/z2.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/arm/mach-pxa/z2.c b/arch/arm/mach-pxa/z2.c
> index ce90fa9..fac7cba 100644
> --- a/arch/arm/mach-pxa/z2.c
> +++ b/arch/arm/mach-pxa/z2.c
> @@ -437,7 +437,7 @@ static struct gpio_keys_button z2_pxa_buttons[] = {
>  	{
>  		.code		= KEY_POWER,
>  		.gpio		= GPIO1_ZIPITZ2_POWER_BUTTON,
> -		.active_low	= 0,
> +		.active_low	= 1,
>  		.desc		= "Power Button",
>  		.wakeup		= 1,
>  		.type		= EV_KEY,

[1] http://lwn.net/Articles/195643/

Best regards,
Marek Vasut

^ permalink raw reply

* [PATCH 5/5] ARM: PXA: Zipit Z2: Fix backlight PWM device number
From: Marek Vasut @ 2012-10-28 22:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1351438555-4668-5-git-send-email-anarsoul@gmail.com>

Dear Vasily Khoruzhick,

> Recent changes to PXA PWM support changed the PXA27X PWM device
> numbering scheme, so keyboard and LCD backlight is not
> working anymore on Z2.
> 
> Fix it and move from legacy to new PWM API.

The proper fix here would be to start moving towards DT.

> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
[...]

Best regards,
Marek Vasut

^ permalink raw reply

* [PATCH v3] Introduce minimal irqchip infrastructure
From: Thomas Petazzoni @ 2012-10-28 22:19 UTC (permalink / raw)
  To: linux-arm-kernel

Thomas, Rob, Arnd,

Here is a small set of patches that introduces a small irqchip
infrastructure that allows the registration of irqchip drivers without
having each of those drivers to expose a public API in
include/linux/irqchip/ (patch 1/4), moves the only existing irqchip
driver, irq-bcm2835 to this infrastructure (patch 2/4), moves the
irqchip driver for the Armada 370 / Armada XP SoCs to the
drivers/irqchip directory (patch 3/4) and finally adds the
drivers/irqchip directory to the IRQ subsystem entry in the
MAINTAINERS file (patch 4/4).

Changes since v2:

 * Fixed the entry in the MAINTAINERS file. Noticed by Rob Herring.

 * Simplified the Kconfig logic by making the IRQCHIP option enabled
   by default as soon as OF_IRQ is enabled. The individual ARCH_<foo>
   Kconfig options no longer have to select IRQCHIP. Also, the option
   was renamed from USE_IRQCHIP to just IRQCHIP. Suggested by Rob
   Herring.

 * Added Reviewed-by tags given by Rob Herring.

Changes since v1:

 * Add a new patch mentionning the drivers/irqchip in the list of
   directories part of the IRQ subsystem maintained by Thomas Gleixner
   in the MAINTAINERS file. Requested by Arnd Bergmann.

 * Reduce the amount of code movement in the irq-bcm2835.c and
   irq-armada-370-xp.c files by using one forward declaration for the
   IRQ handling entry point. Requested by Stephen Warren.

 * Rename the armctrl_of_init() function to bcm2835_irqchip_init() as
   requested by Stephen Warren.

 * Added the formal Acked-by and Reviewed-by received from Stephen
   Warren.

Thanks,

Thomas

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox