diff mbox

[RESEND,0/6,v10] gpio: Add block GPIO

Message ID 50CF0744.7040404@grandegger.com (mailing list archive)
State New, archived
Headers show

Commit Message

Wolfgang Grandegger Dec. 17, 2012, 11:51 a.m. UTC
On 12/17/2012 12:37 PM, Wolfgang Grandegger wrote:
> Hi Roland,
> 
> On 12/15/2012 12:49 AM, Roland Stigge wrote:
>> Hi Wolfgang,
>>
>> thank you for the patch!
>>
>> On 14/12/12 18:58, Wolfgang Grandegger wrote:
>>> +static void at91_gpiolib_set_block(struct gpio_chip *chip, unsigned long mask, unsigned long val)
>>> +{
>>> +	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
>>> +	void __iomem *pio = at91_gpio->regbase;
>>> +
>>> +	__raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR));
>>> +}
>>> +
>>
>> Without having an AT91 available right now, I guess the hardware
>> interface of this GPIO chip is different from the GPIO block API. While
>> the hardware has clear and set registers, the val parameter of
>> at91_gpiolib_set_block() should be interpreted as the actual output
>> values. See lpc32xx_gpo_set_block() for an example for handling set and
>> clear registers like this: First, set_bits and clear_bits words are
>> calculated from mask and val parameters, and finally written to the
>> respective hardware registers.
>>
>> Note that one .set_block() can result in writing both the set and clear
>> registers of the hardware when val contains both 0s and 1s in
>> respectively masked positions.
> 
> Oops, I obviously did not test GPIO block write. The patch below does
> work now. Feel free to add it to the next version of your series.

The patch lacks an important fix, sorry. Please consider the updated
patch below.

Wolfgang.


From a1f93ddea9c6c9c6a80d7a02d3c8d9902823fe47 Mon Sep 17 00:00:00 2001
From: Wolfgang Grandegger <wg@grandegger.com>
Date: Mon, 3 Dec 2012 08:31:55 +0100
Subject: [PATCH 1/2] gpio: add GPIO block callback functions for AT91

Signed-off-by: Wolfgang Grandegger <wg@grandegger.com>
---
 arch/arm/mach-at91/gpio.c |   28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

Comments

Russell King - ARM Linux Dec. 17, 2012, 12:10 p.m. UTC | #1
On Mon, Dec 17, 2012 at 12:51:32PM +0100, Wolfgang Grandegger wrote:
> +static void at91_gpiolib_set_block(struct gpio_chip *chip, unsigned long mask, unsigned long val)
> +{
> +	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
> +	void __iomem *pio = at91_gpio->regbase;
> +	u32 set_bits = val & mask;
> +	u32 clr_bits = ~val & mask;
> +
> +	/* GPIO outputs can only be set at once or cleared at once */
> +	if (set_bits)
> +		__raw_writel(set_bits, pio + PIO_SODR);
> +	if (clr_bits)
> +		__raw_writel(clr_bits, pio + PIO_CODR);
> +}

You obviously didn't see my email...
Roland Stigge Dec. 17, 2012, 1:32 p.m. UTC | #2
On 12/17/2012 12:51 PM, Wolfgang Grandegger wrote:
>>> Without having an AT91 available right now, I guess the hardware
>>> interface of this GPIO chip is different from the GPIO block API. While
>>> the hardware has clear and set registers, the val parameter of
>>> at91_gpiolib_set_block() should be interpreted as the actual output
>>> values. See lpc32xx_gpo_set_block() for an example for handling set and
>>> clear registers like this: First, set_bits and clear_bits words are
>>> calculated from mask and val parameters, and finally written to the
>>> respective hardware registers.
>>>
>>> Note that one .set_block() can result in writing both the set and clear
>>> registers of the hardware when val contains both 0s and 1s in
>>> respectively masked positions.
>>
>> Oops, I obviously did not test GPIO block write. The patch below does
>> work now. Feel free to add it to the next version of your series.
> 
> The patch lacks an important fix, sorry. Please consider the updated
> patch below.

Thanks!

And I guess Russell is right: If possible, we should write outputs
simultaneously via ODSR (plus OWER/OWDR/OWSR) instead of separate set/clear.

I wonder if we need to save/restore the state of OWSR at every write
operation or if we need/can cache it. Assuming that block GPIO are the
only code in the kernel that manipulates ODSR.

Further: Can we include this patch for arch/arm/mach-at91 via the gpio
subsystem or does it need to go separately via arm-soc.git?

Thanks,

Roland
Roland Stigge Dec. 17, 2012, 1:51 p.m. UTC | #3
Hi Wolfgang,

On 12/17/2012 02:32 PM, Roland Stigge wrote:
> And I guess Russell is right: If possible, we should write outputs
> simultaneously via ODSR (plus OWER/OWDR/OWSR) instead of separate set/clear.
> 
> I wonder if we need to save/restore the state of OWSR at every write
> operation or if we need/can cache it. Assuming that block GPIO are the
> only code in the kernel that manipulates ODSR.

Can you please test the following:

+static void at91_gpiolib_set_block(struct gpio_chip *chip, unsigned long mask, unsigned long val)
+{
+       struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
+       void __iomem *pio = at91_gpio->regbase;
+
+       __raw_writel(~mask, pio + PIO_OWDR);
+       __raw_writel(mask, pio + PIO_OWER);
+       __raw_writel(val, pio + PIO_ODSR);
+}

Would caching OWSR be a significant speedup here?

Thanks in advance,

Roland
Wolfgang Grandegger Dec. 17, 2012, 2:57 p.m. UTC | #4
On 12/17/2012 01:10 PM, Russell King - ARM Linux wrote:
> On Mon, Dec 17, 2012 at 12:51:32PM +0100, Wolfgang Grandegger wrote:
>> +static void at91_gpiolib_set_block(struct gpio_chip *chip, unsigned long mask, unsigned long val)
>> +{
>> +	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
>> +	void __iomem *pio = at91_gpio->regbase;
>> +	u32 set_bits = val & mask;
>> +	u32 clr_bits = ~val & mask;
>> +
>> +	/* GPIO outputs can only be set at once or cleared at once */
>> +	if (set_bits)
>> +		__raw_writel(set_bits, pio + PIO_SODR);
>> +	if (clr_bits)
>> +		__raw_writel(clr_bits, pio + PIO_CODR);
>> +}
> 
> You obviously didn't see my email...

Well, I did read you mail but it was not obvious to me how to maintain
compatibility with the existing "set" method... at a first glance.
But the synchronous data output is important and it's even simpler than
I thought. The block-set callback can directly write to ODSR because
"at91_set_gpio_output()" already has set OWER. Will give that a try
later today.

Wolfgang.
Wolfgang Grandegger Dec. 17, 2012, 4:28 p.m. UTC | #5
On 12/17/2012 02:51 PM, Roland Stigge wrote:
> Hi Wolfgang,
> 
> On 12/17/2012 02:32 PM, Roland Stigge wrote:
>> And I guess Russell is right: If possible, we should write outputs
>> simultaneously via ODSR (plus OWER/OWDR/OWSR) instead of separate set/clear.
>>
>> I wonder if we need to save/restore the state of OWSR at every write
>> operation or if we need/can cache it. Assuming that block GPIO are the
>> only code in the kernel that manipulates ODSR.
> 
> Can you please test the following:
> 
> +static void at91_gpiolib_set_block(struct gpio_chip *chip, unsigned long mask, unsigned long val)
> +{
> +       struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
> +       void __iomem *pio = at91_gpio->regbase;
> +
> +       __raw_writel(~mask, pio + PIO_OWDR);

This would also disable normal GPIOs configured for output! From the
manual I understand that if the pin is configured for output, we could
either use PIO_SODR/PIO_CODR to set/clear the bits individually or
PIO_ODSR for synchronous data output. But than we need to care about the
non-block GPIO outputs as well... requiring a read-modify-write cycle :(.

Wolfgang.
Roland Stigge Dec. 17, 2012, 5:15 p.m. UTC | #6
On 12/17/2012 05:28 PM, Wolfgang Grandegger wrote:
> On 12/17/2012 02:51 PM, Roland Stigge wrote:
>> Hi Wolfgang,
>>> And I guess Russell is right: If possible, we should write outputs
>>> simultaneously via ODSR (plus OWER/OWDR/OWSR) instead of separate set/clear.
>>>
>>> I wonder if we need to save/restore the state of OWSR at every write
>>> operation or if we need/can cache it. Assuming that block GPIO are the
>>> only code in the kernel that manipulates ODSR.
>>
>> Can you please test the following:
>>
>> +static void at91_gpiolib_set_block(struct gpio_chip *chip, unsigned long mask, unsigned long val)
>> +{
>> +       struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
>> +       void __iomem *pio = at91_gpio->regbase;
>> +
>> +       __raw_writel(~mask, pio + PIO_OWDR);
> 
> This would also disable normal GPIOs configured for output! From the
> manual I understand that if the pin is configured for output, we could
> either use PIO_SODR/PIO_CODR to set/clear the bits individually or
> PIO_ODSR for synchronous data output. But than we need to care about the
> non-block GPIO outputs as well... requiring a read-modify-write cycle :(.

From the manual, I read about OWER: "Enables writing PIO_ODSR for the
I/O line" (analogous for OWDR). Would interpret this as affecting ODSR
(for block GPIO) but not SODR/CODR (as currently with single GPIOs).

Have you tried? ;-)

Thanks in advance,

Roland
Wolfgang Grandegger Dec. 17, 2012, 5:37 p.m. UTC | #7
On 12/17/2012 06:15 PM, Roland Stigge wrote:
> On 12/17/2012 05:28 PM, Wolfgang Grandegger wrote:
>> On 12/17/2012 02:51 PM, Roland Stigge wrote:
>>> Hi Wolfgang,
>>>> And I guess Russell is right: If possible, we should write outputs
>>>> simultaneously via ODSR (plus OWER/OWDR/OWSR) instead of separate set/clear.
>>>>
>>>> I wonder if we need to save/restore the state of OWSR at every write
>>>> operation or if we need/can cache it. Assuming that block GPIO are the
>>>> only code in the kernel that manipulates ODSR.
>>>
>>> Can you please test the following:
>>>
>>> +static void at91_gpiolib_set_block(struct gpio_chip *chip, unsigned long mask, unsigned long val)
>>> +{
>>> +       struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
>>> +       void __iomem *pio = at91_gpio->regbase;
>>> +
>>> +       __raw_writel(~mask, pio + PIO_OWDR);
>>
>> This would also disable normal GPIOs configured for output! From the
>> manual I understand that if the pin is configured for output, we could
>> either use PIO_SODR/PIO_CODR to set/clear the bits individually or
>> PIO_ODSR for synchronous data output. But than we need to care about the
>> non-block GPIO outputs as well... requiring a read-modify-write cycle :(.
> 
>>From the manual, I read about OWER: "Enables writing PIO_ODSR for the
> I/O line" (analogous for OWDR). Would interpret this as affecting ODSR
> (for block GPIO) but not SODR/CODR (as currently with single GPIOs).
> 
> Have you tried? ;-)

Grrr, I mixed OER with OWER, sorry for the noise. Back to your approach,
which works.

	/* Do synchronous data output with a single write access */
	__raw_writel(~mask, pio + PIO_OWDR);
	__raw_writel(mask, pio + PIO_OWER);
	__raw_writel(val, pio + PIO_ODSR);

For caching we would need a storage. Not sure if it's worth compared to
a context switch into the kernel.

Wolfgang.
Roland Stigge Dec. 17, 2012, 6:02 p.m. UTC | #8
On 12/17/2012 06:37 PM, Wolfgang Grandegger wrote:
> 	/* Do synchronous data output with a single write access */
> 	__raw_writel(~mask, pio + PIO_OWDR);
> 	__raw_writel(mask, pio + PIO_OWER);
> 	__raw_writel(val, pio + PIO_ODSR);
> 
> For caching we would need a storage. Not sure if it's worth compared to
> a context switch into the kernel.

Block GPIO is not only for you in userspace. ;-) You can also implement
efficient n-bit bus I/O in kernel drivers, n-bit-banging. :-) So not
always context switches involved.

Roland
diff mbox

Patch

diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c
index be42cf0..77c6f91 100644
--- a/arch/arm/mach-at91/gpio.c
+++ b/arch/arm/mach-at91/gpio.c
@@ -48,7 +48,9 @@  struct at91_gpio_chip {
 
 static void at91_gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip);
 static void at91_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val);
+static void at91_gpiolib_set_block(struct gpio_chip *chip, unsigned long mask, unsigned long val);
 static int at91_gpiolib_get(struct gpio_chip *chip, unsigned offset);
+static unsigned long at91_gpiolib_get_block(struct gpio_chip *chip, unsigned long mask);
 static int at91_gpiolib_direction_output(struct gpio_chip *chip,
 					 unsigned offset, int val);
 static int at91_gpiolib_direction_input(struct gpio_chip *chip,
@@ -62,7 +64,9 @@  static int at91_gpiolib_to_irq(struct gpio_chip *chip, unsigned offset);
 			.direction_input  = at91_gpiolib_direction_input, \
 			.direction_output = at91_gpiolib_direction_output, \
 			.get		  = at91_gpiolib_get,		\
+			.get_block	  = at91_gpiolib_get_block,	\
 			.set		  = at91_gpiolib_set,		\
+			.set_block	  = at91_gpiolib_set_block,	\
 			.dbg_show	  = at91_gpiolib_dbg_show,	\
 			.to_irq		  = at91_gpiolib_to_irq,	\
 			.ngpio		  = nr_gpio,			\
@@ -896,6 +900,16 @@  static int at91_gpiolib_get(struct gpio_chip *chip, unsigned offset)
 	return (pdsr & mask) != 0;
 }
 
+static unsigned long at91_gpiolib_get_block(struct gpio_chip *chip, unsigned long mask)
+{
+	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
+	void __iomem *pio = at91_gpio->regbase;
+	u32 pdsr;
+
+	pdsr = __raw_readl(pio + PIO_PDSR);
+	return pdsr & mask;
+}
+
 static void at91_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val)
 {
 	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
@@ -905,6 +919,20 @@  static void at91_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val)
 	__raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR));
 }
 
+static void at91_gpiolib_set_block(struct gpio_chip *chip, unsigned long mask, unsigned long val)
+{
+	struct at91_gpio_chip *at91_gpio = to_at91_gpio_chip(chip);
+	void __iomem *pio = at91_gpio->regbase;
+	u32 set_bits = val & mask;
+	u32 clr_bits = ~val & mask;
+
+	/* GPIO outputs can only be set at once or cleared at once */
+	if (set_bits)
+		__raw_writel(set_bits, pio + PIO_SODR);
+	if (clr_bits)
+		__raw_writel(clr_bits, pio + PIO_CODR);
+}
+
 static void at91_gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
 {
 	int i;