diff mbox series

[RESEND,v4,3/8] gpio: 104-dio-48e: Utilize for_each_set_clump macro

Message ID 5906381114b14d5b0359510a1d23accbd239eaa5.1538441919.git.vilhelm.gray@gmail.com
State New
Headers show
Series Introduce the for_each_set_clump macro | expand

Commit Message

William Breathitt Gray Oct. 2, 2018, 1:14 a.m. UTC
Replace verbose implementation in get_multiple/set_multiple callbacks
with for_each_set_clump macro to simplify code and improve clarity.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/gpio/gpio-104-dio-48e.c | 67 ++++++++-------------------------
 1 file changed, 16 insertions(+), 51 deletions(-)

Comments

Rasmus Villemoes Oct. 2, 2018, 7 a.m. UTC | #1
On 2018-10-02 03:14, William Breathitt Gray wrote:
>  	/* clear bits array to a clean slate */
>  	bitmap_zero(bits, chip->ngpio);
>  
> -	/* get bits are evaluated a gpio port register at a time */
> -	for (i = 0; i < ARRAY_SIZE(ports); i++) {
> -		/* gpio offset in bits array */
> -		bits_offset = i * gpio_reg_size;
> -
> -		/* word index for bits array */
> -		word_index = BIT_WORD(bits_offset);
> -
> -		/* gpio offset within current word of bits array */
> -		word_offset = bits_offset % BITS_PER_LONG;
> -
> -		/* mask of get bits for current gpio within current word */
> -		word_mask = mask[word_index] & (port_mask << word_offset);
> -		if (!word_mask) {
> -			/* no get bits in this port so skip to next one */
> -			continue;
> -		}
> -
> -		/* read bits from current gpio port */
> +	for_each_set_clump(i, word, offset, mask, ARRAY_SIZE(ports), 8) {
>  		port_state = inb(dio48egpio->base + ports[i]);
> -
> -		/* store acquired bits at respective bits array offset */
> -		bits[word_index] |= port_state << word_offset;
> +		bits[word] |= port_state << offset;

Somewhat unrelated to this series, but is the existing code correct? I'd
expect the RHS to be masked by word_mask; otherwise we might set bits in
bits[] that were not requested? And if one does that, the !word_mask
test is merely an optimization to avoid reading the gpios when the
result would be ignored anyway. Perhaps no caller cares.

Rasmus
William Breathitt Gray Oct. 14, 2018, 4:19 a.m. UTC | #2
On Tue, Oct 02, 2018 at 09:00:45AM +0200, Rasmus Villemoes wrote:
> On 2018-10-02 03:14, William Breathitt Gray wrote:
> >  	/* clear bits array to a clean slate */
> >  	bitmap_zero(bits, chip->ngpio);
> >  
> > -	/* get bits are evaluated a gpio port register at a time */
> > -	for (i = 0; i < ARRAY_SIZE(ports); i++) {
> > -		/* gpio offset in bits array */
> > -		bits_offset = i * gpio_reg_size;
> > -
> > -		/* word index for bits array */
> > -		word_index = BIT_WORD(bits_offset);
> > -
> > -		/* gpio offset within current word of bits array */
> > -		word_offset = bits_offset % BITS_PER_LONG;
> > -
> > -		/* mask of get bits for current gpio within current word */
> > -		word_mask = mask[word_index] & (port_mask << word_offset);
> > -		if (!word_mask) {
> > -			/* no get bits in this port so skip to next one */
> > -			continue;
> > -		}
> > -
> > -		/* read bits from current gpio port */
> > +	for_each_set_clump(i, word, offset, mask, ARRAY_SIZE(ports), 8) {
> >  		port_state = inb(dio48egpio->base + ports[i]);
> > -
> > -		/* store acquired bits at respective bits array offset */
> > -		bits[word_index] |= port_state << word_offset;
> > +		bits[word] |= port_state << offset;
> 
> Somewhat unrelated to this series, but is the existing code correct? I'd
> expect the RHS to be masked by word_mask; otherwise we might set bits in
> bits[] that were not requested? And if one does that, the !word_mask
> test is merely an optimization to avoid reading the gpios when the
> result would be ignored anyway. Perhaps no caller cares.
> 
> Rasmus

I don't think the caller cares in this case. Take a look at the
gpiod_get_array_value_complex function: the desired inputs are collected
before gpio_chip_get_multiple is called and then looped through after --
unrequested bits are simply ignored.

This caller behavior also makes sense because a bit value of 0 in the
bits array does not necessarily mean the input was not requested, but
may instead mean that the value at the input is 0; therefore, the caller
must keep track of the requested inputs rather than try to deduce them
from the values in the bits array.

William Breathitt Gray
Rasmus Villemoes Oct. 15, 2018, 11:59 a.m. UTC | #3
On 2018-10-14 06:19, William Breathitt Gray wrote:

> a bit value of 0 in the
> bits array does not necessarily mean the input was not requested, but
> may instead mean that the value at the input is 0;

sure enough, but...

> therefore, the caller
> must keep track of the requested inputs rather than try to deduce them
> from the values in the bits array.

...I don't agree that this logically follows. A caller might reasonably
expect not to find any bits set in positions other than those in mask. A
simple example would be caller that just tried to ask "are any of
_these_ inputs set"; it would be reasonable to implement that using
bitmap_empty() on the returned bitset, without first having to mask by
the mask he passed in.

Rasmus
William Breathitt Gray Oct. 17, 2018, 1:54 a.m. UTC | #4
On Mon, Oct 15, 2018 at 01:59:33PM +0200, Rasmus Villemoes wrote:
> On 2018-10-14 06:19, William Breathitt Gray wrote:
> 
> > a bit value of 0 in the
> > bits array does not necessarily mean the input was not requested, but
> > may instead mean that the value at the input is 0;
> 
> sure enough, but...
> 
> > therefore, the caller
> > must keep track of the requested inputs rather than try to deduce them
> > from the values in the bits array.
> 
> ...I don't agree that this logically follows. A caller might reasonably
> expect not to find any bits set in positions other than those in mask. A
> simple example would be caller that just tried to ask "are any of
> _these_ inputs set"; it would be reasonable to implement that using
> bitmap_empty() on the returned bitset, without first having to mask by
> the mask he passed in.
> 
> Rasmus

I see your point. It would be good to keep the behavior consistent with
what would be expected by the user -- and adding an additional AND
operation at the end to mask away the unrequested bits should not really
affect the performance to a discernible degree -- so I'll submit a
patchset implementing the mask for these drivers some time this weekend.

William Breathitt Gray
diff mbox series

Patch

diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c
index 9c4e07fcb74b..77eeaa36094c 100644
--- a/drivers/gpio/gpio-104-dio-48e.c
+++ b/drivers/gpio/gpio-104-dio-48e.c
@@ -183,46 +183,23 @@  static int dio48e_gpio_get(struct gpio_chip *chip, unsigned offset)
 	return !!(port_state & mask);
 }
 
+static const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
+
 static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
 	unsigned long *bits)
 {
 	struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
 	size_t i;
-	static const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
-	const unsigned int gpio_reg_size = 8;
-	unsigned int bits_offset;
-	size_t word_index;
-	unsigned int word_offset;
-	unsigned long word_mask;
-	const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
+	size_t word;
+	unsigned int offset;
 	unsigned long port_state;
 
 	/* clear bits array to a clean slate */
 	bitmap_zero(bits, chip->ngpio);
 
-	/* get bits are evaluated a gpio port register at a time */
-	for (i = 0; i < ARRAY_SIZE(ports); i++) {
-		/* gpio offset in bits array */
-		bits_offset = i * gpio_reg_size;
-
-		/* word index for bits array */
-		word_index = BIT_WORD(bits_offset);
-
-		/* gpio offset within current word of bits array */
-		word_offset = bits_offset % BITS_PER_LONG;
-
-		/* mask of get bits for current gpio within current word */
-		word_mask = mask[word_index] & (port_mask << word_offset);
-		if (!word_mask) {
-			/* no get bits in this port so skip to next one */
-			continue;
-		}
-
-		/* read bits from current gpio port */
+	for_each_set_clump(i, word, offset, mask, ARRAY_SIZE(ports), 8) {
 		port_state = inb(dio48egpio->base + ports[i]);
-
-		/* store acquired bits at respective bits array offset */
-		bits[word_index] |= port_state << word_offset;
+		bits[word] |= port_state << offset;
 	}
 
 	return 0;
@@ -252,37 +229,25 @@  static void dio48e_gpio_set_multiple(struct gpio_chip *chip,
 	unsigned long *mask, unsigned long *bits)
 {
 	struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
-	unsigned int i;
-	const unsigned int gpio_reg_size = 8;
-	unsigned int port;
-	unsigned int out_port;
+	size_t i;
+	size_t word;
+	unsigned int offset;
+	unsigned int iomask;
 	unsigned int bitmask;
 	unsigned long flags;
 
-	/* set bits are evaluated a gpio register size at a time */
-	for (i = 0; i < chip->ngpio; i += gpio_reg_size) {
-		/* no more set bits in this mask word; skip to the next word */
-		if (!mask[BIT_WORD(i)]) {
-			i = (BIT_WORD(i) + 1) * BITS_PER_LONG - gpio_reg_size;
-			continue;
-		}
-
-		port = i / gpio_reg_size;
-		out_port = (port > 2) ? port + 1 : port;
-		bitmask = mask[BIT_WORD(i)] & bits[BIT_WORD(i)];
+	for_each_set_clump(i, word, offset, mask, ARRAY_SIZE(ports), 8) {
+		iomask = mask[word] >> offset;
+		bitmask = iomask & (bits[word] >> offset);
 
 		raw_spin_lock_irqsave(&dio48egpio->lock, flags);
 
 		/* update output state data and set device gpio register */
-		dio48egpio->out_state[port] &= ~mask[BIT_WORD(i)];
-		dio48egpio->out_state[port] |= bitmask;
-		outb(dio48egpio->out_state[port], dio48egpio->base + out_port);
+		dio48egpio->out_state[i] &= ~iomask;
+		dio48egpio->out_state[i] |= bitmask;
+		outb(dio48egpio->out_state[i], dio48egpio->base + ports[i]);
 
 		raw_spin_unlock_irqrestore(&dio48egpio->lock, flags);
-
-		/* prepare for next gpio register set */
-		mask[BIT_WORD(i)] >>= gpio_reg_size;
-		bits[BIT_WORD(i)] >>= gpio_reg_size;
 	}
 }