diff mbox

gpio-generic: add bgpio_set_multiple functions

Message ID 1474325.ElrvNqxI5B@pcimr
State New, archived
Headers show

Commit Message

Rojhalat Ibrahim Jan. 13, 2015, 10:37 a.m. UTC
Add set_multiple functions to the generic driver for memory-mapped GPIO
controllers to improve performance when setting multiple outputs
simultaneously.

Signed-off-by: Rojhalat Ibrahim <imr@rtschenk.de>
---
 drivers/gpio/gpio-generic.c |   79 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)


--
2.0.5

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Alexandre Courbot Jan. 14, 2015, 2:43 a.m. UTC | #1
On Tue, Jan 13, 2015 at 7:37 PM, Rojhalat Ibrahim <imr@rtschenk.de> wrote:
> Add set_multiple functions to the generic driver for memory-mapped GPIO
> controllers to improve performance when setting multiple outputs
> simultaneously.

Great idea ; this driver is an obvious candidate to support this.

>
> Signed-off-by: Rojhalat Ibrahim <imr@rtschenk.de>
> ---
>  drivers/gpio/gpio-generic.c |   79 ++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 79 insertions(+)
>
> diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
> index 16f6115..cb6d0b7 100644
> --- a/drivers/gpio/gpio-generic.c
> +++ b/drivers/gpio/gpio-generic.c
> @@ -160,6 +160,31 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
>         spin_unlock_irqrestore(&bgc->lock, flags);
>  }
>
> +static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
> +                              unsigned long *bits)
> +{
> +       struct bgpio_chip *bgc = to_bgpio_chip(gc);
> +       unsigned long flags;
> +       int i;
> +
> +       spin_lock_irqsave(&bgc->lock, flags);
> +
> +       for (i = 0; i < bgc->bits; i++) {
> +               if (*mask == 0)
> +                       break;
> +               if (__test_and_clear_bit(i, mask)) {
> +                       if (test_bit(i, bits))
> +                               bgc->data |= bgc->pin2mask(bgc, i);
> +                       else
> +                               bgc->data &= ~bgc->pin2mask(bgc, i);
> +               }
> +       }
> +
> +       bgc->write_reg(bgc->reg_dat, bgc->data);
> +
> +       spin_unlock_irqrestore(&bgc->lock, flags);
> +}
> +
>  static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
>                                  int val)
>  {
> @@ -172,6 +197,32 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
>                 bgc->write_reg(bgc->reg_clr, mask);
>  }
>
> +static void bgpio_set_multiple_with_clear(struct gpio_chip *gc,
> +                                         unsigned long *mask,
> +                                         unsigned long *bits)
> +{
> +       struct bgpio_chip *bgc = to_bgpio_chip(gc);
> +       unsigned long set_mask = 0;
> +       unsigned long clear_mask = 0;
> +       int i;
> +
> +       for (i = 0; i < bgc->bits; i++) {
> +               if (*mask == 0)
> +                       break;
> +               if (__test_and_clear_bit(i, mask)) {
> +                       if (test_bit(i, bits))
> +                               set_mask |= bgc->pin2mask(bgc, i);
> +                       else
> +                               clear_mask |= bgc->pin2mask(bgc, i);
> +               }
> +       }
> +
> +       if (set_mask)
> +               bgc->write_reg(bgc->reg_set, set_mask);
> +       if (clear_mask)
> +               bgc->write_reg(bgc->reg_clr, clear_mask);
> +}

Isn't this function missing spinlock protection?

> +
>  static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
>  {
>         struct bgpio_chip *bgc = to_bgpio_chip(gc);
> @@ -190,6 +241,31 @@ static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
>         spin_unlock_irqrestore(&bgc->lock, flags);
>  }
>
> +static void bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask,
> +                                  unsigned long *bits)
> +{
> +       struct bgpio_chip *bgc = to_bgpio_chip(gc);
> +       unsigned long flags;
> +       int i;
> +
> +       spin_lock_irqsave(&bgc->lock, flags);
> +
> +       for (i = 0; i < bgc->bits; i++) {
> +               if (*mask == 0)
> +                       break;
> +               if (__test_and_clear_bit(i, mask)) {
> +                       if (test_bit(i, bits))
> +                               bgc->data |= bgc->pin2mask(bgc, i);
> +                       else
> +                               bgc->data &= ~bgc->pin2mask(bgc, i);
> +               }
> +       }
> +
> +       bgc->write_reg(bgc->reg_set, bgc->data);
> +
> +       spin_unlock_irqrestore(&bgc->lock, flags);
> +}

Couldn't it be possible to factorize a great deal of these 3 functions?

The only difference between bgpio_set_multiple() and
bgpio_set_multiple_set() is the register that is written. In
bgpio_set_multiple_set(), you only handle the set and cleared bits in
different variables.

How about a private function that looks like this:

static void __bgpio_multiple_get_masks(struct bgpio_chip *bgc,
                                       unsigned long *mask, unsigned long *bits,
                                       unsigned long *set_mask,
                                       unsigned long *clear_mask)
{
       int i;

       *set_mask = 0;
       *clear_mask = 0;

       for (i = 0; i < bgc->bits; i++) {
               if (*mask == 0)
                       break;
               if (__test_and_clear_bit(i, mask)) {
                       if (test_bit(i, bits))
                               *set_mask |= bgc->pin2mask(bgc, i);
                       else
                               *clear_mask |= bgc->pin2mask(bgc, i);
               }
       }
}

Then, you could have:

static void bgpio_set_multiple_with_clear(struct gpio_chip *gc,
                                         unsigned long *mask,
                                         unsigned long *bits)
{
       struct bgpio_chip *bgc = to_bgpio_chip(gc);
       unsigned long flags;
       unsigned long set_mask, clear_mask;

       spin_lock_irqsave(&bgc->lock, flags);

       __bgpio_multiple_get_masks(bgc, mask, bits, &set_mask, &clear_mask);

       if (set_mask)
               bgc->write_reg(bgc->reg_set, set_mask);
       if (clear_mask)
               bgc->write_reg(bgc->reg_clr, clear_mask);

       spin_unlock_irqrestore(&bgc->lock, flags);
}

and:

static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
                              unsigned long *bits)
{
       struct bgpio_chip *bgc = to_bgpio_chip(gc);
       unsigned long flags;
       unsigned long set_mask, clear_mask;

       spin_lock_irqsave(&bgc->lock, flags);

       __bgpio_multiple_get_masks(bgc, mask, bits, &set_mask, &clear_mask);

       bgc->data |= set_mask;
       bgc->data &= ~clear_mask;

       bgc->write_reg(bgc->reg_dat, bgc->data);

       spin_unlock_irqrestore(&bgc->lock, flags);
}

... and something similar for __bgpio_multiple_get_masks. This would
probably result in a smaller patch on top or reducing duplicate code.
--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rojhalat Ibrahim Jan. 14, 2015, 1:18 p.m. UTC | #2
On Wednesday 14 January 2015 11:43:54 Alexandre Courbot wrote:
> On Tue, Jan 13, 2015 at 7:37 PM, Rojhalat Ibrahim <imr@rtschenk.de> wrote:
> > Add set_multiple functions to the generic driver for memory-mapped GPIO
> > controllers to improve performance when setting multiple outputs
> > simultaneously.
> 
> Great idea ; this driver is an obvious candidate to support this.
> 
> >
> > Signed-off-by: Rojhalat Ibrahim <imr@rtschenk.de>
> > ---
> >  drivers/gpio/gpio-generic.c |   79 ++++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 79 insertions(+)
> >
> > diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
> > index 16f6115..cb6d0b7 100644
> > --- a/drivers/gpio/gpio-generic.c
> > +++ b/drivers/gpio/gpio-generic.c
> > @@ -160,6 +160,31 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
> >         spin_unlock_irqrestore(&bgc->lock, flags);
> >  }
> >
> > +static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
> > +                              unsigned long *bits)
> > +{
> > +       struct bgpio_chip *bgc = to_bgpio_chip(gc);
> > +       unsigned long flags;
> > +       int i;
> > +
> > +       spin_lock_irqsave(&bgc->lock, flags);
> > +
> > +       for (i = 0; i < bgc->bits; i++) {
> > +               if (*mask == 0)
> > +                       break;
> > +               if (__test_and_clear_bit(i, mask)) {
> > +                       if (test_bit(i, bits))
> > +                               bgc->data |= bgc->pin2mask(bgc, i);
> > +                       else
> > +                               bgc->data &= ~bgc->pin2mask(bgc, i);
> > +               }
> > +       }
> > +
> > +       bgc->write_reg(bgc->reg_dat, bgc->data);
> > +
> > +       spin_unlock_irqrestore(&bgc->lock, flags);
> > +}
> > +
> >  static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
> >                                  int val)
> >  {
> > @@ -172,6 +197,32 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
> >                 bgc->write_reg(bgc->reg_clr, mask);
> >  }
> >
> > +static void bgpio_set_multiple_with_clear(struct gpio_chip *gc,
> > +                                         unsigned long *mask,
> > +                                         unsigned long *bits)
> > +{
> > +       struct bgpio_chip *bgc = to_bgpio_chip(gc);
> > +       unsigned long set_mask = 0;
> > +       unsigned long clear_mask = 0;
> > +       int i;
> > +
> > +       for (i = 0; i < bgc->bits; i++) {
> > +               if (*mask == 0)
> > +                       break;
> > +               if (__test_and_clear_bit(i, mask)) {
> > +                       if (test_bit(i, bits))
> > +                               set_mask |= bgc->pin2mask(bgc, i);
> > +                       else
> > +                               clear_mask |= bgc->pin2mask(bgc, i);
> > +               }
> > +       }
> > +
> > +       if (set_mask)
> > +               bgc->write_reg(bgc->reg_set, set_mask);
> > +       if (clear_mask)
> > +               bgc->write_reg(bgc->reg_clr, clear_mask);
> > +}
> 
> Isn't this function missing spinlock protection?
> 

I followed the lead of the bgpio_set_with_clear function which also does not
use a spinlock. With dedicated set and clear registers it shouldn't be
necessary.

> > +
> >  static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
> >  {
> >         struct bgpio_chip *bgc = to_bgpio_chip(gc);
> > @@ -190,6 +241,31 @@ static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
> >         spin_unlock_irqrestore(&bgc->lock, flags);
> >  }
> >
> > +static void bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask,
> > +                                  unsigned long *bits)
> > +{
> > +       struct bgpio_chip *bgc = to_bgpio_chip(gc);
> > +       unsigned long flags;
> > +       int i;
> > +
> > +       spin_lock_irqsave(&bgc->lock, flags);
> > +
> > +       for (i = 0; i < bgc->bits; i++) {
> > +               if (*mask == 0)
> > +                       break;
> > +               if (__test_and_clear_bit(i, mask)) {
> > +                       if (test_bit(i, bits))
> > +                               bgc->data |= bgc->pin2mask(bgc, i);
> > +                       else
> > +                               bgc->data &= ~bgc->pin2mask(bgc, i);
> > +               }
> > +       }
> > +
> > +       bgc->write_reg(bgc->reg_set, bgc->data);
> > +
> > +       spin_unlock_irqrestore(&bgc->lock, flags);
> > +}
> 
> Couldn't it be possible to factorize a great deal of these 3 functions?
> 
> The only difference between bgpio_set_multiple() and
> bgpio_set_multiple_set() is the register that is written. In
> bgpio_set_multiple_set(), you only handle the set and cleared bits in
> different variables.
> 
> How about a private function that looks like this:
> 
> static void __bgpio_multiple_get_masks(struct bgpio_chip *bgc,
>                                        unsigned long *mask, unsigned long *bits,
>                                        unsigned long *set_mask,
>                                        unsigned long *clear_mask)
> {
>        int i;
> 
>        *set_mask = 0;
>        *clear_mask = 0;
> 
>        for (i = 0; i < bgc->bits; i++) {
>                if (*mask == 0)
>                        break;
>                if (__test_and_clear_bit(i, mask)) {
>                        if (test_bit(i, bits))
>                                *set_mask |= bgc->pin2mask(bgc, i);
>                        else
>                                *clear_mask |= bgc->pin2mask(bgc, i);
>                }
>        }
> }
> 
> Then, you could have:
> 
> static void bgpio_set_multiple_with_clear(struct gpio_chip *gc,
>                                          unsigned long *mask,
>                                          unsigned long *bits)
> {
>        struct bgpio_chip *bgc = to_bgpio_chip(gc);
>        unsigned long flags;
>        unsigned long set_mask, clear_mask;
> 
>        spin_lock_irqsave(&bgc->lock, flags);
> 
>        __bgpio_multiple_get_masks(bgc, mask, bits, &set_mask, &clear_mask);
> 
>        if (set_mask)
>                bgc->write_reg(bgc->reg_set, set_mask);
>        if (clear_mask)
>                bgc->write_reg(bgc->reg_clr, clear_mask);
> 
>        spin_unlock_irqrestore(&bgc->lock, flags);
> }
> 
> and:
> 
> static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
>                               unsigned long *bits)
> {
>        struct bgpio_chip *bgc = to_bgpio_chip(gc);
>        unsigned long flags;
>        unsigned long set_mask, clear_mask;
> 
>        spin_lock_irqsave(&bgc->lock, flags);
> 
>        __bgpio_multiple_get_masks(bgc, mask, bits, &set_mask, &clear_mask);
> 
>        bgc->data |= set_mask;
>        bgc->data &= ~clear_mask;
> 
>        bgc->write_reg(bgc->reg_dat, bgc->data);
> 
>        spin_unlock_irqrestore(&bgc->lock, flags);
> }
> 
> ... and something similar for __bgpio_multiple_get_masks. This would
> probably result in a smaller patch on top or reducing duplicate code.

You are right, of course. I'll post a revised version.

--
To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c
index 16f6115..cb6d0b7 100644
--- a/drivers/gpio/gpio-generic.c
+++ b/drivers/gpio/gpio-generic.c
@@ -160,6 +160,31 @@  static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
 	spin_unlock_irqrestore(&bgc->lock, flags);
 }
 
+static void bgpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
+			       unsigned long *bits)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&bgc->lock, flags);
+
+	for (i = 0; i < bgc->bits; i++) {
+		if (*mask == 0)
+			break;
+		if (__test_and_clear_bit(i, mask)) {
+			if (test_bit(i, bits))
+				bgc->data |= bgc->pin2mask(bgc, i);
+			else
+				bgc->data &= ~bgc->pin2mask(bgc, i);
+		}
+	}
+
+	bgc->write_reg(bgc->reg_dat, bgc->data);
+
+	spin_unlock_irqrestore(&bgc->lock, flags);
+}
+
 static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
 				 int val)
 {
@@ -172,6 +197,32 @@  static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
 		bgc->write_reg(bgc->reg_clr, mask);
 }
 
+static void bgpio_set_multiple_with_clear(struct gpio_chip *gc,
+					  unsigned long *mask,
+					  unsigned long *bits)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long set_mask = 0;
+	unsigned long clear_mask = 0;
+	int i;
+
+	for (i = 0; i < bgc->bits; i++) {
+		if (*mask == 0)
+			break;
+		if (__test_and_clear_bit(i, mask)) {
+			if (test_bit(i, bits))
+				set_mask |= bgc->pin2mask(bgc, i);
+			else
+				clear_mask |= bgc->pin2mask(bgc, i);
+		}
+	}
+
+	if (set_mask)
+		bgc->write_reg(bgc->reg_set, set_mask);
+	if (clear_mask)
+		bgc->write_reg(bgc->reg_clr, clear_mask);
+}
+
 static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
 {
 	struct bgpio_chip *bgc = to_bgpio_chip(gc);
@@ -190,6 +241,31 @@  static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val)
 	spin_unlock_irqrestore(&bgc->lock, flags);
 }
 
+static void bgpio_set_multiple_set(struct gpio_chip *gc, unsigned long *mask,
+				   unsigned long *bits)
+{
+	struct bgpio_chip *bgc = to_bgpio_chip(gc);
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&bgc->lock, flags);
+
+	for (i = 0; i < bgc->bits; i++) {
+		if (*mask == 0)
+			break;
+		if (__test_and_clear_bit(i, mask)) {
+			if (test_bit(i, bits))
+				bgc->data |= bgc->pin2mask(bgc, i);
+			else
+				bgc->data &= ~bgc->pin2mask(bgc, i);
+		}
+	}
+
+	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;
@@ -354,11 +430,14 @@  static int bgpio_setup_io(struct bgpio_chip *bgc,
 		bgc->reg_set = set;
 		bgc->reg_clr = clr;
 		bgc->gc.set = bgpio_set_with_clear;
+		bgc->gc.set_multiple = bgpio_set_multiple_with_clear;
 	} else if (set && !clr) {
 		bgc->reg_set = set;
 		bgc->gc.set = bgpio_set_set;
+		bgc->gc.set_multiple = bgpio_set_multiple_set;
 	} else {
 		bgc->gc.set = bgpio_set;
+		bgc->gc.set_multiple = bgpio_set_multiple;
 	}
 
 	bgc->gc.get = bgpio_get;