mirror of
https://github.com/torvalds/linux.git
synced 2024-11-28 15:11:31 +00:00
169c474fb2
Pach series "Introduce the for_each_set_clump8 macro", v18. While adding GPIO get_multiple/set_multiple callback support for various drivers, I noticed a pattern of looping manifesting that would be useful standardized as a macro. This patchset introduces the for_each_set_clump8 macro and utilizes it in several GPIO drivers. The for_each_set_clump macro8 facilitates a for-loop syntax that iterates over a memory region entire groups of set bits at a time. For example, suppose you would like to iterate over a 32-bit integer 8 bits at a time, skipping over 8-bit groups with no set bit, where XXXXXXXX represents the current 8-bit group: Example: 10111110 00000000 11111111 00110011 First loop: 10111110 00000000 11111111 XXXXXXXX Second loop: 10111110 00000000 XXXXXXXX 00110011 Third loop: XXXXXXXX 00000000 11111111 00110011 Each iteration of the loop returns the next 8-bit group that has at least one set bit. The for_each_set_clump8 macro has four parameters: * start: set to the bit offset of the current clump * clump: set to the current clump value * bits: bitmap to search within * size: bitmap size in number of bits In this version of the patchset, the for_each_set_clump macro has been reimplemented and simplified based on the suggestions provided by Rasmus Villemoes and Andy Shevchenko in the version 4 submission. In particular, the function of the for_each_set_clump macro has been restricted to handle only 8-bit clumps; the drivers that use the for_each_set_clump macro only handle 8-bit ports so a generic for_each_set_clump implementation is not necessary. Thus, a solution for large clumps (i.e. those larger than the width of a bitmap word) can be postponed until a driver appears that actually requires such a generic for_each_set_clump implementation. For what it's worth, a semi-generic for_each_set_clump (i.e. for clumps smaller than the width of a bitmap word) can be implemented by simply replacing the hardcoded '8' and '0xFF' instances with respective variables. I have not yet had a need for such an implementation, and since it falls short of a true generic for_each_set_clump function, I have decided to forgo such an implementation for now. In addition, the bitmap_get_value8 and bitmap_set_value8 functions are introduced to get and set 8-bit values respectively. Their use is based on the behavior suggested in the patchset version 4 review. This patch (of 14): This macro iterates for each 8-bit group of bits (clump) with set bits, within a bitmap memory region. For each iteration, "start" is set to the bit offset of the found clump, while the respective clump value is stored to the location pointed by "clump". Additionally, the bitmap_get_value8 and bitmap_set_value8 functions are introduced to respectively get and set an 8-bit value in a bitmap memory region. [gustavo@embeddedor.com: fix potential sign-extension overflow] Link: http://lkml.kernel.org/r/20191015184657.GA26541@embeddedor [akpm@linux-foundation.org: s/ULL/UL/, per Joe] [vilhelm.gray@gmail.com: add for_each_set_clump8 documentation] Link: http://lkml.kernel.org/r/20191016161825.301082-1-vilhelm.gray@gmail.com Link: http://lkml.kernel.org/r/893c3b4f03266c9496137cc98ac2b1bd27f92c73.1570641097.git.vilhelm.gray@gmail.com Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com> Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com> Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com> Suggested-by: Rasmus Villemoes <linux@rasmusvillemoes.dk> Suggested-by: Lukas Wunner <lukas@wunner.de> Tested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Linus Walleij <linus.walleij@linaro.org> Cc: Bartosz Golaszewski <bgolaszewski@baylibre.com> Cc: Masahiro Yamada <yamada.masahiro@socionext.com> Cc: Geert Uytterhoeven <geert@linux-m68k.org> Cc: Phil Reid <preid@electromag.com.au> Cc: Geert Uytterhoeven <geert+renesas@glider.be> Cc: Mathias Duckeck <m.duckeck@kunbus.de> Cc: Morten Hein Tiljeset <morten.tiljeset@prevas.dk> Cc: Sean Nyekjaer <sean.nyekjaer@prevas.dk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
231 lines
5.4 KiB
C
231 lines
5.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/* bit search implementation
|
|
*
|
|
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
|
|
* Written by David Howells (dhowells@redhat.com)
|
|
*
|
|
* Copyright (C) 2008 IBM Corporation
|
|
* 'find_last_bit' is written by Rusty Russell <rusty@rustcorp.com.au>
|
|
* (Inspired by David Howell's find_next_bit implementation)
|
|
*
|
|
* Rewritten by Yury Norov <yury.norov@gmail.com> to decrease
|
|
* size and improve performance, 2015.
|
|
*/
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/bitmap.h>
|
|
#include <linux/export.h>
|
|
#include <linux/kernel.h>
|
|
|
|
#if !defined(find_next_bit) || !defined(find_next_zero_bit) || \
|
|
!defined(find_next_and_bit)
|
|
|
|
/*
|
|
* This is a common helper function for find_next_bit, find_next_zero_bit, and
|
|
* find_next_and_bit. The differences are:
|
|
* - The "invert" argument, which is XORed with each fetched word before
|
|
* searching it for one bits.
|
|
* - The optional "addr2", which is anded with "addr1" if present.
|
|
*/
|
|
static inline unsigned long _find_next_bit(const unsigned long *addr1,
|
|
const unsigned long *addr2, unsigned long nbits,
|
|
unsigned long start, unsigned long invert)
|
|
{
|
|
unsigned long tmp;
|
|
|
|
if (unlikely(start >= nbits))
|
|
return nbits;
|
|
|
|
tmp = addr1[start / BITS_PER_LONG];
|
|
if (addr2)
|
|
tmp &= addr2[start / BITS_PER_LONG];
|
|
tmp ^= invert;
|
|
|
|
/* Handle 1st word. */
|
|
tmp &= BITMAP_FIRST_WORD_MASK(start);
|
|
start = round_down(start, BITS_PER_LONG);
|
|
|
|
while (!tmp) {
|
|
start += BITS_PER_LONG;
|
|
if (start >= nbits)
|
|
return nbits;
|
|
|
|
tmp = addr1[start / BITS_PER_LONG];
|
|
if (addr2)
|
|
tmp &= addr2[start / BITS_PER_LONG];
|
|
tmp ^= invert;
|
|
}
|
|
|
|
return min(start + __ffs(tmp), nbits);
|
|
}
|
|
#endif
|
|
|
|
#ifndef find_next_bit
|
|
/*
|
|
* Find the next set bit in a memory region.
|
|
*/
|
|
unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
|
|
unsigned long offset)
|
|
{
|
|
return _find_next_bit(addr, NULL, size, offset, 0UL);
|
|
}
|
|
EXPORT_SYMBOL(find_next_bit);
|
|
#endif
|
|
|
|
#ifndef find_next_zero_bit
|
|
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
|
|
unsigned long offset)
|
|
{
|
|
return _find_next_bit(addr, NULL, size, offset, ~0UL);
|
|
}
|
|
EXPORT_SYMBOL(find_next_zero_bit);
|
|
#endif
|
|
|
|
#if !defined(find_next_and_bit)
|
|
unsigned long find_next_and_bit(const unsigned long *addr1,
|
|
const unsigned long *addr2, unsigned long size,
|
|
unsigned long offset)
|
|
{
|
|
return _find_next_bit(addr1, addr2, size, offset, 0UL);
|
|
}
|
|
EXPORT_SYMBOL(find_next_and_bit);
|
|
#endif
|
|
|
|
#ifndef find_first_bit
|
|
/*
|
|
* Find the first set bit in a memory region.
|
|
*/
|
|
unsigned long find_first_bit(const unsigned long *addr, unsigned long size)
|
|
{
|
|
unsigned long idx;
|
|
|
|
for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
|
|
if (addr[idx])
|
|
return min(idx * BITS_PER_LONG + __ffs(addr[idx]), size);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
EXPORT_SYMBOL(find_first_bit);
|
|
#endif
|
|
|
|
#ifndef find_first_zero_bit
|
|
/*
|
|
* Find the first cleared bit in a memory region.
|
|
*/
|
|
unsigned long find_first_zero_bit(const unsigned long *addr, unsigned long size)
|
|
{
|
|
unsigned long idx;
|
|
|
|
for (idx = 0; idx * BITS_PER_LONG < size; idx++) {
|
|
if (addr[idx] != ~0UL)
|
|
return min(idx * BITS_PER_LONG + ffz(addr[idx]), size);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
EXPORT_SYMBOL(find_first_zero_bit);
|
|
#endif
|
|
|
|
#ifndef find_last_bit
|
|
unsigned long find_last_bit(const unsigned long *addr, unsigned long size)
|
|
{
|
|
if (size) {
|
|
unsigned long val = BITMAP_LAST_WORD_MASK(size);
|
|
unsigned long idx = (size-1) / BITS_PER_LONG;
|
|
|
|
do {
|
|
val &= addr[idx];
|
|
if (val)
|
|
return idx * BITS_PER_LONG + __fls(val);
|
|
|
|
val = ~0ul;
|
|
} while (idx--);
|
|
}
|
|
return size;
|
|
}
|
|
EXPORT_SYMBOL(find_last_bit);
|
|
#endif
|
|
|
|
#ifdef __BIG_ENDIAN
|
|
|
|
/* include/linux/byteorder does not support "unsigned long" type */
|
|
static inline unsigned long ext2_swab(const unsigned long y)
|
|
{
|
|
#if BITS_PER_LONG == 64
|
|
return (unsigned long) __swab64((u64) y);
|
|
#elif BITS_PER_LONG == 32
|
|
return (unsigned long) __swab32((u32) y);
|
|
#else
|
|
#error BITS_PER_LONG not defined
|
|
#endif
|
|
}
|
|
|
|
#if !defined(find_next_bit_le) || !defined(find_next_zero_bit_le)
|
|
static inline unsigned long _find_next_bit_le(const unsigned long *addr1,
|
|
const unsigned long *addr2, unsigned long nbits,
|
|
unsigned long start, unsigned long invert)
|
|
{
|
|
unsigned long tmp;
|
|
|
|
if (unlikely(start >= nbits))
|
|
return nbits;
|
|
|
|
tmp = addr1[start / BITS_PER_LONG];
|
|
if (addr2)
|
|
tmp &= addr2[start / BITS_PER_LONG];
|
|
tmp ^= invert;
|
|
|
|
/* Handle 1st word. */
|
|
tmp &= ext2_swab(BITMAP_FIRST_WORD_MASK(start));
|
|
start = round_down(start, BITS_PER_LONG);
|
|
|
|
while (!tmp) {
|
|
start += BITS_PER_LONG;
|
|
if (start >= nbits)
|
|
return nbits;
|
|
|
|
tmp = addr1[start / BITS_PER_LONG];
|
|
if (addr2)
|
|
tmp &= addr2[start / BITS_PER_LONG];
|
|
tmp ^= invert;
|
|
}
|
|
|
|
return min(start + __ffs(ext2_swab(tmp)), nbits);
|
|
}
|
|
#endif
|
|
|
|
#ifndef find_next_zero_bit_le
|
|
unsigned long find_next_zero_bit_le(const void *addr, unsigned
|
|
long size, unsigned long offset)
|
|
{
|
|
return _find_next_bit_le(addr, NULL, size, offset, ~0UL);
|
|
}
|
|
EXPORT_SYMBOL(find_next_zero_bit_le);
|
|
#endif
|
|
|
|
#ifndef find_next_bit_le
|
|
unsigned long find_next_bit_le(const void *addr, unsigned
|
|
long size, unsigned long offset)
|
|
{
|
|
return _find_next_bit_le(addr, NULL, size, offset, 0UL);
|
|
}
|
|
EXPORT_SYMBOL(find_next_bit_le);
|
|
#endif
|
|
|
|
#endif /* __BIG_ENDIAN */
|
|
|
|
unsigned long find_next_clump8(unsigned long *clump, const unsigned long *addr,
|
|
unsigned long size, unsigned long offset)
|
|
{
|
|
offset = find_next_bit(addr, size, offset);
|
|
if (offset == size)
|
|
return size;
|
|
|
|
offset = round_down(offset, 8);
|
|
*clump = bitmap_get_value8(addr, offset);
|
|
|
|
return offset;
|
|
}
|
|
EXPORT_SYMBOL(find_next_clump8);
|