Add partial support of linux bitmap library

The bitmap is going to be used by the mtu3 driver to allocate
and manage its internal FIFO.
Add the functions from the linux bitmap library required by the mtu3
driver.

Signed-off-by: Alexandre Bailon <abailon@baylibre.com>
diff --git a/arch/arm/include/asm/bitops.h b/arch/arm/include/asm/bitops.h
index b02c454..9cbf06c 100644
--- a/arch/arm/include/asm/bitops.h
+++ b/arch/arm/include/asm/bitops.h
@@ -112,6 +112,45 @@
 
 #define ffz(x)  __ffs(~(x))
 
+
+static inline unsigned long find_next_bit(void *addr, int size, int offset)
+{
+	unsigned long *p = ((unsigned long *)addr) + (offset / BITS_PER_LONG);
+	unsigned long result = offset & ~(BITS_PER_LONG-1);
+	unsigned long tmp;
+
+	if (offset >= size)
+		return size;
+	size -= result;
+	offset %= BITS_PER_LONG;
+	if (offset) {
+		tmp = *(p++);
+		tmp &= (~0UL << offset);
+		if (size < BITS_PER_LONG)
+			goto found_first;
+		if (tmp)
+			goto found_middle;
+		size -= BITS_PER_LONG;
+		result += BITS_PER_LONG;
+	}
+	while (size & ~(BITS_PER_LONG-1)) {
+		if ((tmp = *(p++)))
+			goto found_middle;
+		result += BITS_PER_LONG;
+		size -= BITS_PER_LONG;
+	}
+	if (!size)
+		return result;
+	tmp = *p;
+
+found_first:
+	tmp &= (~0UL >> (BITS_PER_LONG - size));
+	if (tmp == 0UL)		/* Are any bits set? */
+		return result + size;	/* Nope. */
+found_middle:
+	return result + __ffs(tmp);
+}
+
 static inline int find_next_zero_bit(void *addr, int size, int offset)
 {
 	unsigned long *p = ((unsigned long *)addr) + (offset / BITS_PER_LONG);
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 4a54ae0..4f84f35 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -5,6 +5,42 @@
 #include <asm/types.h>
 #include <linux/types.h>
 #include <linux/bitops.h>
+#include <string.h>
+
+extern unsigned long bitmap_find_next_zero_area_off(unsigned long *map,
+                                                    unsigned long size,
+                                                    unsigned long start,
+                                                    unsigned int nr,
+                                                    unsigned long align_mask,
+                                                    unsigned long align_offset);
+
+extern void __bitmap_set(unsigned long *map, unsigned int start, int len);
+extern void __bitmap_clear(unsigned long *map, unsigned int start, int len);
+/**
+ * bitmap_find_next_zero_area - find a contiguous aligned zero area
+ * @map: The address to base the search on
+ * @size: The bitmap size in bits
+ * @start: The bitnumber to start searching at
+ * @nr: The number of zeroed bits we're looking for
+ * @align_mask: Alignment mask for zero area
+ *
+ * The @align_mask should be one less than a power of 2; the effect is that
+ * the bit offset of all zero areas this function finds is multiples of that
+ * power of 2. A @align_mask of 0 means no alignment is required.
+ */
+static inline unsigned long
+bitmap_find_next_zero_area(unsigned long *map,
+                           unsigned long size,
+                           unsigned long start,
+                           unsigned int nr,
+                           unsigned long align_mask)
+{
+        return bitmap_find_next_zero_area_off(map, size, start, nr,
+                                              align_mask, 0);
+}
+
+#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
+#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))
 
 #define small_const_nbits(nbits) \
 	(__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
@@ -20,4 +56,39 @@
 	}
 }
 
+#ifdef __LITTLE_ENDIAN
+#define BITMAP_MEM_ALIGNMENT 8
+#else
+#define BITMAP_MEM_ALIGNMENT (8 * sizeof(unsigned long))
+#endif
+#define BITMAP_MEM_MASK (BITMAP_MEM_ALIGNMENT - 1)
+
+static __always_inline void bitmap_set(unsigned long *map, unsigned int start,
+                unsigned int nbits)
+{
+        if (__builtin_constant_p(nbits) && nbits == 1)
+                __set_bit(start, map);
+        else if (__builtin_constant_p(start & BITMAP_MEM_MASK) &&
+                 IS_ALIGNED(start, BITMAP_MEM_ALIGNMENT) &&
+                 __builtin_constant_p(nbits & BITMAP_MEM_MASK) &&
+                 IS_ALIGNED(nbits, BITMAP_MEM_ALIGNMENT))
+                memset((char *)map + start / 8, 0xff, nbits / 8);
+        else
+		__bitmap_set(map, start, nbits);
+}
+
+static __always_inline void bitmap_clear(unsigned long *map, unsigned int start,
+                unsigned int nbits)
+{
+        if (__builtin_constant_p(nbits) && nbits == 1)
+                __clear_bit(start, map);
+        else if (__builtin_constant_p(start & BITMAP_MEM_MASK) &&
+                 IS_ALIGNED(start, BITMAP_MEM_ALIGNMENT) &&
+                 __builtin_constant_p(nbits & BITMAP_MEM_MASK) &&
+                 IS_ALIGNED(nbits, BITMAP_MEM_ALIGNMENT))
+                memset((char *)map + start / 8, 0, nbits / 8);
+        else
+		__bitmap_clear(map, start, nbits);
+}
+
 #endif /* __LINUX_BITMAP_H */
diff --git a/lib/Makefile b/lib/Makefile
index 2fffd68..5b4a1bd 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -91,6 +91,7 @@
 obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += fdtdec.o fdtdec_common.o
 obj-y += hang.o
 obj-y += linux_compat.o
+obj-y += linux_bitmap.o
 obj-y += linux_string.o
 obj-$(CONFIG_LMB) += lmb.o
 obj-y += membuff.o
diff --git a/lib/linux_bitmap.c b/lib/linux_bitmap.c
new file mode 100644
index 0000000..1f40269
--- /dev/null
+++ b/lib/linux_bitmap.c
@@ -0,0 +1,81 @@
+#include <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+
+void __bitmap_set(unsigned long *map, unsigned int start, int len)
+{
+        unsigned long *p = map + BIT_WORD(start);
+        const unsigned int size = start + len;
+        int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
+        unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
+
+        while (len - bits_to_set >= 0) {
+                *p |= mask_to_set;
+                len -= bits_to_set;
+                bits_to_set = BITS_PER_LONG;
+                mask_to_set = ~0UL;
+                p++;
+        }
+        if (len) {
+                mask_to_set &= BITMAP_LAST_WORD_MASK(size);
+                *p |= mask_to_set;
+        }
+}
+
+void __bitmap_clear(unsigned long *map, unsigned int start, int len)
+{
+        unsigned long *p = map + BIT_WORD(start);
+        const unsigned int size = start + len;
+        int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
+        unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
+
+        while (len - bits_to_clear >= 0) {
+                *p &= ~mask_to_clear;
+                len -= bits_to_clear;
+                bits_to_clear = BITS_PER_LONG;
+                mask_to_clear = ~0UL;
+                p++;
+        }
+    if (len) {
+                mask_to_clear &= BITMAP_LAST_WORD_MASK(size);
+                *p &= ~mask_to_clear;
+        }
+}
+
+/**
+ * bitmap_find_next_zero_area_off - find a contiguous aligned zero area
+ * @map: The address to base the search on
+ * @size: The bitmap size in bits
+ * @start: The bitnumber to start searching at
+ * @nr: The number of zeroed bits we're looking for
+ * @align_mask: Alignment mask for zero area
+ * @align_offset: Alignment offset for zero area.
+ *
+ * The @align_mask should be one less than a power of 2; the effect is that
+ * the bit offset of all zero areas this function finds plus @align_offset
+ * is multiple of that power of 2.
+ */
+unsigned long bitmap_find_next_zero_area_off(unsigned long *map,
+                                             unsigned long size,
+                                             unsigned long start,
+                                             unsigned int nr,
+                                             unsigned long align_mask,
+                                             unsigned long align_offset)
+{
+        unsigned long index, end, i;
+again:
+      	index = find_next_zero_bit(map, size, start);
+
+        /* Align allocation */
+        index = __ALIGN_MASK(index + align_offset, align_mask) - align_offset;
+
+        end = index + nr;
+        if (end > size)
+                return end;
+        i = find_next_bit(map, end, index);
+        if (i < end) {
+                start = i + 1;
+                goto again;
+        }
+	return index;
+}