diff mbox

[U-Boot,1/7] Tegra2: Add bitfield access macros

Message ID 1304038371-22931-2-git-send-email-sjg@chromium.org
State Superseded, archived
Headers show

Commit Message

Simon Glass April 29, 2011, 12:52 a.m. UTC
To use these, set things up like this:

struct uart_ctlr *uart = (struct uart_ctlr *)UART_PA_START;

 #define UART_PA_START 0x67000000       /* Physical address of UART */
 #define UART_FBCON_RANGE  5:3          /* Bit range for the FBCON field */
enum {                                  /* An enum with allowed values */
        UART_FBCON_OFF,
        UART_FBCON_ON,
        UART_FBCON_MULTI,
        UART_FBCON_SLAVE,
};

This defines a bit field of 3 bits starting at bit 5 and extending down
to bit 3, i.e. 5:3

Then:
bf_unpack(UART_FBCON)
        - return the value of bits 5:3 (shifted down to bits 2:0)

bf_pack(UART_FBCON, 4)
        - return a word with that field set to 4 (so in this case (4 << 3))

bf_update(UART_FBCON, word, val)
        - update a field within word so that its value is val.

bf_writel(UART_FBCON, 6, &uart->fbcon)
        - set the UART's FBCON field to 6

bf_enum_writel(UART_FBCON, MULTI, &uart->fbcon)
        - set the UART's FBCON field to MULTI

Signed-off-by: Simon Glass <sjg@chromium.org>
---
 arch/arm/include/asm/arch-tegra2/bitfield.h |  151 ++++++++++++++++++
 test/Makefile                               |   36 ++++
 test/bitfield.c                             |  230 +++++++++++++++++++++++++++
 3 files changed, 417 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-tegra2/bitfield.h
 create mode 100644 test/Makefile
 create mode 100644 test/bitfield.c

--
1.7.3.1
diff mbox

Patch

diff --git a/arch/arm/include/asm/arch-tegra2/bitfield.h b/arch/arm/include/asm/arch-tegra2/bitfield.h
new file mode 100644
index 0000000..6a1bbfa
--- /dev/null
+++ b/arch/arm/include/asm/arch-tegra2/bitfield.h
@@ -0,0 +1,151 @@ 
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __TEGRA2_BITFIELD_H
+#define __TEGRA2_BITFIELD_H
+
+/*
+ * Macros for working with bit fields. To use these, set things up like this:
+ *
+ * #define UART_PA_START 0x67000000	Physical address of UART
+ * #define UART_FBCON_RANGE  5:3	Bit range for the FBCON field
+ * enum {				An enum with allowed values
+ *	UART_FBCON_OFF,
+ *	UART_FBCON_ON,
+ *	UART_FBCON_MULTI,
+ *	UART_FBCON_SLAVE,
+ * };
+ * struct uart_ctlr *uart = (struct uart_ctlr *)UART_PA_START;
+ *
+ * This defines a bit field of 3 bits starting at bit 5 and extending down
+ * to bit 3, i.e. 5:3
+ *
+ * Then:
+ *    bf_unpack(UART_FBCON)
+ *	- return the value of bits 5:3 (shifted down to bits 2:0)
+ *
+ *    bf_pack(UART_FBCON, 4)
+ *	- return a word with that field set to 4 (so in this case (4 << 3))
+ *
+ *    bf_update(UART_FBCON, word, val)
+ *	- update a field within word so that its value is val.
+ *
+ *    bf_enum_writel(UART_FBCON, MULTI, &uart->fbcon)
+ *	- set the UART's FBCON field to MULTI
+ *
+ *
+ * Why have bitfield macros?
+ *   1. Reability
+ *   2. Maintainability
+ *   3. Less error prone
+ *
+ * For example, this:
+ *
+ *	int RegVal = 0;
+ *	RegVal= readl(UsbBase+USB_SUSP_CTRL);
+ *	RegVal |= Bit11;
+ *	writel(RegVal, UsbBase+USB_SUSP_CTRL);
+ *	if(UsbBase == NV_ADDRESS_MAP_USB3_BASE)
+ *	{
+ *		RegVal = readl(UsbBase+USB_SUSP_CTRL);
+ *		RegVal |= Bit12;
+ *		writel(RegVal, UsbBase+USB_SUSP_CTRL);
+ *	}
+ *
+ * becomes this:
+ *
+ *	bitfield_writel(UTMIP_RESET, 1, &usbctlr->susp_ctrl);
+ *	if (id == PERIPH_ID_USB3)
+ *		bitfield_writel(UTMIP_PHY_ENB, 1, &usbctlr->susp_ctrl);
+ */
+
+/* Returns the low bit of the bitfield */
+#define BITFIELD_LOWBIT(range)	((0 ? range) & 0x1f)
+
+/* Returns the high bit of the bitfield */
+#define BITFIELD_HIGHBIT(range)	(1 ? range)
+
+/* Returns the width of the bitfield (in bits) */
+#define BITFIELD_WIDTH(range)	\
+	(BITFIELD_HIGHBIT(range) - BITFIELD_LOWBIT(range) + 1)
+
+
+/*
+ * Returns the number of bits the bitfield needs to be shifted left to pack it.
+ * This is just the same as the low bit.
+ */
+#define bf_shift(field)	BITFIELD_LOWBIT(field ## _RANGE)
+
+/* Returns the unshifted mask for the field (i.e. LSB of mask is bit 0) */
+#define bf_rawmask(field) (0xfffffffful >> \
+		(32 - BITFIELD_WIDTH(field ## _RANGE)))
+
+/* Returns the mask for a field. Clear these bits to zero the field */
+#define bf_mask(field) \
+		(bf_rawmask(field) << (bf_shift(field)))
+
+/* Unpacks and returns a value extracted from a field */
+#define bf_unpack(field, word) \
+	(((unsigned)(word) >> bf_shift(field)) & bf_rawmask(field))
+
+/*
+ * Packs a value into a field - this masks the value to ensure it does not
+ * overflow into another field.
+ */
+#define bf_pack(field, value) \
+	((((unsigned)(value)) & bf_rawmask(field)) \
+		<< bf_shift(field))
+
+/* Sets the value of a field in a word to the given value */
+#define bf_update(field, word, value)		\
+	((word) = ((word) & ~bf_mask(field)) |	\
+		bf_pack(field, value))
+
+/*
+ * Sets the value of a field in a register to the given value using
+ * readl/writel
+ */
+#define bf_writel(field, value, reg) ({		\
+	u32 *__reg = (u32 *)(reg);			\
+	u32 __oldval = readl(__reg);			\
+	bf_update(field, __oldval, value);		\
+	writel(__oldval, __reg);				\
+	})
+
+/* Unpacks a field from a register using readl */
+#define bf_readl(field, reg)			\
+	bf_unpack(field, readl(reg))
+
+/*
+ * Clears a field in a register using writel - like
+ * bf_writel(field, 0, reg)
+ */
+#define bf_clearl(field, reg) bf_writel(field, 0, reg)
+
+/*
+ * Sets the value of a field in a register to the given enum.
+ *
+ * The enum should have the field as a prefix.
+ */
+#define bf_enum_writel(field, _enum, reg) \
+		bf_writel(field, field ## _ ## _enum, reg)
+
+#endif
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..6d8c6a7
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,36 @@ 
+# Copyright (c) 2011 The Chromium OS Authors.
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+
+TESTS=bitfield
+
+INC=../arch/arm/include/asm/arch-tegra2
+CFLAGS=-DDEBUG -I$(INC)
+
+all: tests run
+
+tests: $(TESTS)
+
+bitfield: bitfield.o
+
+bitfield.o: $(INC)/bitfield.h
+
+run:
+	@echo "Running tests $(TESTS)"
+	@./bitfield
+	@echo "Tests completed."
diff --git a/test/bitfield.c b/test/bitfield.c
new file mode 100644
index 0000000..5fc381d
--- /dev/null
+++ b/test/bitfield.c
@@ -0,0 +1,230 @@ 
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/*
+ * Bitfield test routines
+ *
+ */
+
+#include <stdio.h>
+
+#include "bitfield.h"
+
+
+#define FULL_RANGE		31 : 0
+#define HIGH_RANGE		31 : 30
+#define MID_RANGE		23 : 16
+#define LOW_RANGE		2 : 0
+#define HIGH_SINGLE_RANGE	31 : 31
+#define MID_SINGLE_RANGE	16 : 16
+#define LOW_SINGLE_RANGE	0 : 0
+
+static int test_count;
+
+#ifdef DEBUG
+#define assert(x)	\
+	({ test_count++; \
+	if (!(x)) \
+		printf("Assertion failure '%s' %s line %d\n", \
+			#x, __FILE__, __LINE__) })
+#define asserteq(x, y)	\
+	({ int _x = x; int _y = y; test_count++; \
+	if (_x != _y) \
+		printf("Assertion failure at %s:%d: '%s' %#x != '%s' %#x\n", \
+			__FILE__, __LINE__, #x, _x, #y, _y); })
+#else
+#define assert(x) (test_count++)
+#define asserteq(x, y) (test_count++)
+#endif
+
+
+static void test_low_high(void)
+{
+	asserteq(BITFIELD_HIGHBIT(FULL_RANGE), 31);
+	asserteq(BITFIELD_LOWBIT(FULL_RANGE), 0);
+	asserteq(BITFIELD_HIGHBIT(HIGH_RANGE), 31);
+	asserteq(BITFIELD_LOWBIT(HIGH_RANGE), 30);
+	asserteq(BITFIELD_HIGHBIT(MID_RANGE), 23);
+	asserteq(BITFIELD_LOWBIT(MID_RANGE), 16);
+	asserteq(BITFIELD_HIGHBIT(LOW_RANGE), 2);
+	asserteq(BITFIELD_LOWBIT(LOW_RANGE), 0);
+	asserteq(BITFIELD_HIGHBIT(HIGH_SINGLE_RANGE), 31);
+	asserteq(BITFIELD_LOWBIT(HIGH_SINGLE_RANGE), 31);
+	asserteq(BITFIELD_HIGHBIT(MID_SINGLE_RANGE), 16);
+	asserteq(BITFIELD_LOWBIT(MID_SINGLE_RANGE), 16);
+	asserteq(BITFIELD_HIGHBIT(LOW_SINGLE_RANGE), 0);
+	asserteq(BITFIELD_LOWBIT(LOW_SINGLE_RANGE), 0);
+}
+
+static void test_width(void)
+{
+	asserteq(BITFIELD_WIDTH(FULL_RANGE), 32);
+	asserteq(BITFIELD_WIDTH(HIGH_RANGE), 2);
+	asserteq(BITFIELD_WIDTH(MID_RANGE), 8);
+	asserteq(BITFIELD_WIDTH(LOW_RANGE), 3);
+	asserteq(BITFIELD_WIDTH(HIGH_SINGLE_RANGE), 1);
+	asserteq(BITFIELD_WIDTH(MID_SINGLE_RANGE), 1);
+	asserteq(BITFIELD_WIDTH(LOW_SINGLE_RANGE), 1);
+}
+
+static void test_shift(void)
+{
+	asserteq(bf_shift(FULL), 0);
+	asserteq(bf_shift(HIGH), 30);
+	asserteq(bf_shift(MID), 16);
+	asserteq(bf_shift(LOW), 0);
+	asserteq(bf_shift(HIGH_SINGLE), 31);
+	asserteq(bf_shift(MID_SINGLE), 16);
+	asserteq(bf_shift(LOW_SINGLE), 0);
+}
+
+static void test_rawmask(void)
+{
+	asserteq(bf_rawmask(FULL), 0xffffffffU);
+	asserteq(bf_rawmask(HIGH), 0x3);
+	asserteq(bf_rawmask(MID), 0xff);
+	asserteq(bf_rawmask(LOW), 0x7);
+	asserteq(bf_rawmask(HIGH_SINGLE), 0x1);
+	asserteq(bf_rawmask(MID_SINGLE), 0x1);
+	asserteq(bf_rawmask(LOW_SINGLE), 0x1);
+}
+
+static void test_mask(void)
+{
+	asserteq(bf_mask(FULL), 0xffffffffU);
+	asserteq(bf_mask(HIGH), 0xc0000000);
+	asserteq(bf_mask(MID), 0x00ff0000);
+	asserteq(bf_mask(LOW), 0x7);
+	asserteq(bf_mask(HIGH_SINGLE), 0x80000000U);
+	asserteq(bf_mask(MID_SINGLE), 0x00010000);
+	asserteq(bf_mask(LOW_SINGLE), 0x1);
+}
+
+static void test_unpack(void)
+{
+	asserteq(bf_unpack(FULL, 0), 0);
+	asserteq(bf_unpack(FULL, -1U), -1U);
+	asserteq(bf_unpack(FULL, 0x12345678), 0x12345678);
+	asserteq(bf_unpack(FULL, 0x87654321), 0x87654321);
+	asserteq(bf_unpack(FULL, 0xa5a5a5a6), 0xa5a5a5a6);
+
+	asserteq(bf_unpack(HIGH, 0), 0);
+	asserteq(bf_unpack(HIGH, -1U), 3);
+	asserteq(bf_unpack(HIGH, 0x12345678), 0);
+	asserteq(bf_unpack(HIGH, 0x87654321), 2);
+	asserteq(bf_unpack(HIGH, 0xa5a5a5a6), 2);
+
+	asserteq(bf_unpack(MID, 0), 0);
+	asserteq(bf_unpack(MID, -1U), 0xff);
+	asserteq(bf_unpack(MID, 0x12345678), 0x34);
+	asserteq(bf_unpack(MID, 0x87654321), 0x65);
+	asserteq(bf_unpack(MID, 0xa5a5a5a6), 0xa5);
+
+	asserteq(bf_unpack(LOW, 0), 0);
+	asserteq(bf_unpack(LOW, -1U), 7);
+	asserteq(bf_unpack(LOW, 0x12345678), 0);
+	asserteq(bf_unpack(LOW, 0x87654321), 1);
+	asserteq(bf_unpack(LOW, 0xa5a5a5a6), 6);
+
+	asserteq(bf_unpack(HIGH_SINGLE, 0), 0);
+	asserteq(bf_unpack(HIGH_SINGLE, -1U), 1);
+	asserteq(bf_unpack(HIGH_SINGLE, 0x12345678), 0);
+	asserteq(bf_unpack(HIGH_SINGLE, 0x87654321), 1);
+	asserteq(bf_unpack(HIGH_SINGLE, 0xa5a5a5a6), 1);
+
+	asserteq(bf_unpack(MID_SINGLE, 0), 0);
+	asserteq(bf_unpack(MID_SINGLE, -1U), 1);
+	asserteq(bf_unpack(MID_SINGLE, 0x12345678), 0);
+	asserteq(bf_unpack(MID_SINGLE, 0x87654321), 1);
+	asserteq(bf_unpack(MID_SINGLE, 0xa5a5a5a6), 1);
+
+	asserteq(bf_unpack(LOW_SINGLE, 0), 0);
+	asserteq(bf_unpack(LOW_SINGLE, -1U), 1);
+	asserteq(bf_unpack(LOW_SINGLE, 0x12345678), 0);
+	asserteq(bf_unpack(LOW_SINGLE, 0x87654321), 1);
+	asserteq(bf_unpack(LOW_SINGLE, 0xa5a5a5a6), 0);
+}
+
+static void test_pack(void)
+{
+	asserteq(bf_pack(FULL, 0), 0);
+	asserteq(bf_pack(FULL, -1U), -1U);
+	asserteq(bf_pack(FULL, 0x12345678), 0x12345678);
+	asserteq(bf_pack(FULL, 0x87654321), 0x87654321);
+	asserteq(bf_pack(FULL, 0xa5a5a5a6), 0xa5a5a5a6);
+
+	asserteq(bf_pack(HIGH, 0), 0);
+	asserteq(bf_pack(HIGH, -1U), 0xc0000000);
+	asserteq(bf_pack(HIGH, 0x12345678), 0);
+	asserteq(bf_pack(HIGH, 0x87654321), 0x40000000);
+	asserteq(bf_pack(HIGH, 0xa5a5a5a6), 0x80000000);
+
+	asserteq(bf_pack(MID, 0), 0);
+	asserteq(bf_pack(MID, -1U), 0x00ff0000);
+	asserteq(bf_pack(MID, 0x12345678), 0x00780000);
+	asserteq(bf_pack(MID, 0x87654321), 0x00210000);
+	asserteq(bf_pack(MID, 0xa5a5a5a6), 0x00a60000);
+
+	asserteq(bf_pack(LOW, 0), 0);
+	asserteq(bf_pack(LOW, -1U), 7);
+	asserteq(bf_pack(LOW, 0x12345678), 0);
+	asserteq(bf_pack(LOW, 0x87654321), 1);
+	asserteq(bf_pack(LOW, 0xa5a5a5a6), 6);
+
+	asserteq(bf_pack(HIGH_SINGLE, 0), 0);
+	asserteq(bf_pack(HIGH_SINGLE, -1U), 0x80000000u);
+	asserteq(bf_pack(HIGH_SINGLE, 0x12345678), 0);
+	asserteq(bf_pack(HIGH_SINGLE, 0x87654321), 0x80000000u);
+	asserteq(bf_pack(HIGH_SINGLE, 0xa5a5a5a6), 0x00000000u);
+
+	asserteq(bf_pack(MID_SINGLE, 0), 0);
+	asserteq(bf_pack(MID_SINGLE, -1U), 0x00010000);
+	asserteq(bf_pack(MID_SINGLE, 0x12345678), 0);
+	asserteq(bf_pack(MID_SINGLE, 0x87654321), 0x00010000);
+	asserteq(bf_pack(MID_SINGLE, 0xa5a5a5a6), 0x00000000);
+
+	asserteq(bf_pack(LOW_SINGLE, 0), 0);
+	asserteq(bf_pack(LOW_SINGLE, -1U), 1);
+	asserteq(bf_pack(LOW_SINGLE, 0x12345678), 0);
+	asserteq(bf_pack(LOW_SINGLE, 0x87654321), 1);
+	asserteq(bf_pack(LOW_SINGLE, 0xa5a5a5a6), 0);
+}
+
+static void test_set(void)
+{
+}
+
+void bf_test(void)
+{
+	test_low_high();
+	test_width();
+	test_shift();
+	test_rawmask();
+	test_unpack();
+	test_pack();
+}
+
+int main(int argc, char *argv[])
+{
+	bf_test();
+	printf("%d tests run\n", test_count);
+	return 0;
+}