summaryrefslogtreecommitdiff
path: root/src/arch/armv7/lib
diff options
context:
space:
mode:
authorStefan Reinauer <stefan.reinauer@coreboot.org>2012-12-07 17:15:04 -0800
committerRonald G. Minnich <rminnich@gmail.com>2012-12-08 06:53:19 +0100
commit52db0b984523047da19ca3b41558b9dbf45abad7 (patch)
tree5ed389bb233d5b007593ede56040ccf268e37bbe /src/arch/armv7/lib
parent509f77277cfccdae897f0d369672ce0818ecdf88 (diff)
WIP: Initial ARMv7 architecture implementation in coreboot
The first ARMv7 CPU we're going to support is the Exynos 5250 used in the Google Snow ChromeBook. Change-Id: I4de8433bbc6202eb8fef2556a11186a3376d411b Signed-off-by: David Hendricks <dhendrix@chromium.org> Signed-off-by: Stefan Reinauer <reinauer@google.com> Signed-off-by: Ronald G. Minnich <rminnich@gmail.com> Reviewed-on: http://review.coreboot.org/2004 Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/arch/armv7/lib')
-rw-r--r--src/arch/armv7/lib/Makefile.inc26
-rw-r--r--src/arch/armv7/lib/Makefile.uboot95
-rw-r--r--src/arch/armv7/lib/_divsi3.S142
-rw-r--r--src/arch/armv7/lib/_udivsi3.S93
-rw-r--r--src/arch/armv7/lib/_uldivmod.S157
-rw-r--r--src/arch/armv7/lib/bootm.c405
-rw-r--r--src/arch/armv7/lib/c_start.S9
-rw-r--r--src/arch/armv7/lib/cache-cp15.c304
-rw-r--r--src/arch/armv7/lib/cache_v7.c427
-rw-r--r--src/arch/armv7/lib/div.c5
-rw-r--r--src/arch/armv7/lib/div0.c32
-rw-r--r--src/arch/armv7/lib/div64.S208
-rw-r--r--src/arch/armv7/lib/eabi_compat.c33
-rw-r--r--src/arch/armv7/lib/hang_spl.c16
-rw-r--r--src/arch/armv7/lib/id.inc18
-rw-r--r--src/arch/armv7/lib/id.lds6
-rw-r--r--src/arch/armv7/lib/interrupts.c198
-rw-r--r--src/arch/armv7/lib/memcpy.S243
-rw-r--r--src/arch/armv7/lib/memset.S126
-rw-r--r--src/arch/armv7/lib/reset.c53
-rw-r--r--src/arch/armv7/lib/romstage_console.c75
-rw-r--r--src/arch/armv7/lib/syslib.c70
22 files changed, 2741 insertions, 0 deletions
diff --git a/src/arch/armv7/lib/Makefile.inc b/src/arch/armv7/lib/Makefile.inc
new file mode 100644
index 0000000000..bdf6a64ffb
--- /dev/null
+++ b/src/arch/armv7/lib/Makefile.inc
@@ -0,0 +1,26 @@
+romstage-y += cache_v7.c
+romstage-y += cache-cp15.c
+romstage-y += div0.c
+romstage-y += div64.S
+romstage-y += hang_spl.c
+romstage-y += romstage_console.c
+romstage-y += syslib.c
+
+#ramstage-y += printk_init.c
+#romstage-y += walkcbfs.S
+
+ramstage-y += c_start.S
+
+#ramstage-y += div.c
+ramstage-y += div0.c
+ramstage-y += div64.S
+ramstage-y += hang_spl.c
+#ramstage-y += interrupts.c
+#ramstage-y += memcpy.S
+#ramstage-y += memset.S
+#ramstage-y += reset.c
+ramstage-y += syslib.c
+
+#FIXME(dhendrix): should this be a config option?
+romstage-y += eabi_compat.c
+ramstage-y += eabi_compat.c
diff --git a/src/arch/armv7/lib/Makefile.uboot b/src/arch/armv7/lib/Makefile.uboot
new file mode 100644
index 0000000000..d33d08b14d
--- /dev/null
+++ b/src/arch/armv7/lib/Makefile.uboot
@@ -0,0 +1,95 @@
+#
+# (C) Copyright 2002-2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# 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
+#
+
+include $(TOPDIR)/config.mk
+
+LIB = $(obj)lib$(ARCH).o
+LIBGCC = $(obj)libgcc.o
+
+ifndef CONFIG_SPL_BUILD
+GLSOBJS += _ashldi3.o
+GLSOBJS += _ashrdi3.o
+GLSOBJS += _divsi3.o
+GLSOBJS += _lshrdi3.o
+GLSOBJS += _modsi3.o
+GLSOBJS += _udivsi3.o
+GLSOBJS += _umodsi3.o
+ifdef CONFIG_CHROMEOS
+GLSOBJS += _uldivmod.o
+endif
+
+GLCOBJS += div0.o
+
+ifeq ($(CONFIG_SYS_GENERIC_BOARD),)
+COBJS-y += board.o
+endif
+COBJS-y += bootm.o
+COBJS-y += cache.o
+COBJS-y += cache-cp15.o
+COBJS-$(CONFIG_SYS_L2_PL310) += cache-pl310.o
+SOBJS-$(CONFIG_USE_ARCH_MEMSET) += memset.o
+SOBJS-$(CONFIG_USE_ARCH_MEMCPY) += memcpy.o
+else # CONFIG_SPL_BUILD
+ifeq ($(CONFIG_SYS_GENERIC_BOARD),)
+COBJS-y += hang_spl.o
+endif
+endif # CONFIG_SPL_BUILD
+
+COBJS-y += interrupts.o
+COBJS-y += reset.o
+
+SRCS := $(GLSOBJS:.o=.S) $(GLCOBJS:.o=.c) \
+ $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c)
+OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y))
+LGOBJS := $(addprefix $(obj),$(GLSOBJS)) \
+ $(addprefix $(obj),$(GLCOBJS))
+
+# Always build libarm.o
+TARGETS := $(LIB)
+
+# Build private libgcc only when asked for
+ifdef USE_PRIVATE_LIBGCC
+TARGETS += $(LIBGCC)
+endif
+
+# For EABI conformant tool chains, provide eabi_compat()
+ifneq (,$(findstring -mabi=aapcs-linux,$(PLATFORM_CPPFLAGS)))
+TARGETS += $(obj)eabi_compat.o
+endif
+
+all: $(TARGETS)
+
+$(LIB): $(obj).depend $(OBJS)
+ $(call cmd_link_o_target, $(OBJS))
+
+$(LIBGCC): $(obj).depend $(LGOBJS)
+ $(call cmd_link_o_target, $(LGOBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/src/arch/armv7/lib/_divsi3.S b/src/arch/armv7/lib/_divsi3.S
new file mode 100644
index 0000000000..cfbadb2ab9
--- /dev/null
+++ b/src/arch/armv7/lib/_divsi3.S
@@ -0,0 +1,142 @@
+
+.macro ARM_DIV_BODY dividend, divisor, result, curbit
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz \curbit, \divisor
+ clz \result, \dividend
+ sub \result, \curbit, \result
+ mov \curbit, #1
+ mov \divisor, \divisor, lsl \result
+ mov \curbit, \curbit, lsl \result
+ mov \result, #0
+
+#else
+
+ @ Initially shift the divisor left 3 bits if possible,
+ @ set curbit accordingly. This allows for curbit to be located
+ @ at the left end of each 4 bit nibbles in the division loop
+ @ to save one loop in most cases.
+ tst \divisor, #0xe0000000
+ moveq \divisor, \divisor, lsl #3
+ moveq \curbit, #8
+ movne \curbit, #1
+
+ @ Unless the divisor is very big, shift it up in multiples of
+ @ four bits, since this is the amount of unwinding in the main
+ @ division loop. Continue shifting until the divisor is
+ @ larger than the dividend.
+1: cmp \divisor, #0x10000000
+ cmplo \divisor, \dividend
+ movlo \divisor, \divisor, lsl #4
+ movlo \curbit, \curbit, lsl #4
+ blo 1b
+
+ @ For very big divisors, we must shift it a bit at a time, or
+ @ we will be in danger of overflowing.
+1: cmp \divisor, #0x80000000
+ cmplo \divisor, \dividend
+ movlo \divisor, \divisor, lsl #1
+ movlo \curbit, \curbit, lsl #1
+ blo 1b
+
+ mov \result, #0
+
+#endif
+
+ @ Division loop
+1: cmp \dividend, \divisor
+ subhs \dividend, \dividend, \divisor
+ orrhs \result, \result, \curbit
+ cmp \dividend, \divisor, lsr #1
+ subhs \dividend, \dividend, \divisor, lsr #1
+ orrhs \result, \result, \curbit, lsr #1
+ cmp \dividend, \divisor, lsr #2
+ subhs \dividend, \dividend, \divisor, lsr #2
+ orrhs \result, \result, \curbit, lsr #2
+ cmp \dividend, \divisor, lsr #3
+ subhs \dividend, \dividend, \divisor, lsr #3
+ orrhs \result, \result, \curbit, lsr #3
+ cmp \dividend, #0 @ Early termination?
+ movnes \curbit, \curbit, lsr #4 @ No, any more bits to do?
+ movne \divisor, \divisor, lsr #4
+ bne 1b
+
+.endm
+
+.macro ARM_DIV2_ORDER divisor, order
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz \order, \divisor
+ rsb \order, \order, #31
+
+#else
+
+ cmp \divisor, #(1 << 16)
+ movhs \divisor, \divisor, lsr #16
+ movhs \order, #16
+ movlo \order, #0
+
+ cmp \divisor, #(1 << 8)
+ movhs \divisor, \divisor, lsr #8
+ addhs \order, \order, #8
+
+ cmp \divisor, #(1 << 4)
+ movhs \divisor, \divisor, lsr #4
+ addhs \order, \order, #4
+
+ cmp \divisor, #(1 << 2)
+ addhi \order, \order, #3
+ addls \order, \order, \divisor, lsr #1
+
+#endif
+
+.endm
+
+ .align 5
+.globl __divsi3
+.globl __aeabi_idiv
+__divsi3:
+__aeabi_idiv:
+ cmp r1, #0
+ eor ip, r0, r1 @ save the sign of the result.
+ beq Ldiv0
+ rsbmi r1, r1, #0 @ loops below use unsigned.
+ subs r2, r1, #1 @ division by 1 or -1 ?
+ beq 10f
+ movs r3, r0
+ rsbmi r3, r0, #0 @ positive dividend value
+ cmp r3, r1
+ bls 11f
+ tst r1, r2 @ divisor is power of 2 ?
+ beq 12f
+
+ ARM_DIV_BODY r3, r1, r0, r2
+
+ cmp ip, #0
+ rsbmi r0, r0, #0
+ mov pc, lr
+
+10: teq ip, r0 @ same sign ?
+ rsbmi r0, r0, #0
+ mov pc, lr
+
+11: movlo r0, #0
+ moveq r0, ip, asr #31
+ orreq r0, r0, #1
+ mov pc, lr
+
+12: ARM_DIV2_ORDER r1, r2
+
+ cmp ip, #0
+ mov r0, r3, lsr r2
+ rsbmi r0, r0, #0
+ mov pc, lr
+
+Ldiv0:
+
+ str lr, [sp, #-4]!
+ bl __div0
+ mov r0, #0 @ About as wrong as it could be.
+ ldr pc, [sp], #4
diff --git a/src/arch/armv7/lib/_udivsi3.S b/src/arch/armv7/lib/_udivsi3.S
new file mode 100644
index 0000000000..1309802610
--- /dev/null
+++ b/src/arch/armv7/lib/_udivsi3.S
@@ -0,0 +1,93 @@
+/* # 1 "libgcc1.S" */
+@ libgcc1 routines for ARM cpu.
+@ Division routines, written by Richard Earnshaw, (rearnsha@armltd.co.uk)
+dividend .req r0
+divisor .req r1
+result .req r2
+curbit .req r3
+/* ip .req r12 */
+/* sp .req r13 */
+/* lr .req r14 */
+/* pc .req r15 */
+ .text
+ .globl __udivsi3
+ .type __udivsi3 ,function
+ .globl __aeabi_uidiv
+ .type __aeabi_uidiv ,function
+ .align 0
+ __udivsi3:
+ __aeabi_uidiv:
+ cmp divisor, #0
+ beq Ldiv0
+ mov curbit, #1
+ mov result, #0
+ cmp dividend, divisor
+ bcc Lgot_result
+Loop1:
+ @ Unless the divisor is very big, shift it up in multiples of
+ @ four bits, since this is the amount of unwinding in the main
+ @ division loop. Continue shifting until the divisor is
+ @ larger than the dividend.
+ cmp divisor, #0x10000000
+ cmpcc divisor, dividend
+ movcc divisor, divisor, lsl #4
+ movcc curbit, curbit, lsl #4
+ bcc Loop1
+Lbignum:
+ @ For very big divisors, we must shift it a bit at a time, or
+ @ we will be in danger of overflowing.
+ cmp divisor, #0x80000000
+ cmpcc divisor, dividend
+ movcc divisor, divisor, lsl #1
+ movcc curbit, curbit, lsl #1
+ bcc Lbignum
+Loop3:
+ @ Test for possible subtractions, and note which bits
+ @ are done in the result. On the final pass, this may subtract
+ @ too much from the dividend, but the result will be ok, since the
+ @ "bit" will have been shifted out at the bottom.
+ cmp dividend, divisor
+ subcs dividend, dividend, divisor
+ orrcs result, result, curbit
+ cmp dividend, divisor, lsr #1
+ subcs dividend, dividend, divisor, lsr #1
+ orrcs result, result, curbit, lsr #1
+ cmp dividend, divisor, lsr #2
+ subcs dividend, dividend, divisor, lsr #2
+ orrcs result, result, curbit, lsr #2
+ cmp dividend, divisor, lsr #3
+ subcs dividend, dividend, divisor, lsr #3
+ orrcs result, result, curbit, lsr #3
+ cmp dividend, #0 @ Early termination?
+ movnes curbit, curbit, lsr #4 @ No, any more bits to do?
+ movne divisor, divisor, lsr #4
+ bne Loop3
+Lgot_result:
+ mov r0, result
+ mov pc, lr
+Ldiv0:
+ str lr, [sp, #-4]!
+ bl __div0 (PLT)
+ mov r0, #0 @ about as wrong as it could be
+ ldmia sp!, {pc}
+ .size __udivsi3 , . - __udivsi3
+
+.globl __aeabi_uidivmod
+__aeabi_uidivmod:
+
+ stmfd sp!, {r0, r1, ip, lr}
+ bl __aeabi_uidiv
+ ldmfd sp!, {r1, r2, ip, lr}
+ mul r3, r0, r2
+ sub r1, r1, r3
+ mov pc, lr
+
+.globl __aeabi_idivmod
+__aeabi_idivmod:
+
+ stmfd sp!, {r0, r1, ip, lr}
+ bl __aeabi_idiv
+ ldmfd sp!, {r1, r2, ip, lr}
+ mul r3, r0, r2
+ sub r1, r1, r3
+ mov pc, lr
diff --git a/src/arch/armv7/lib/_uldivmod.S b/src/arch/armv7/lib/_uldivmod.S
new file mode 100644
index 0000000000..65e4fa8441
--- /dev/null
+++ b/src/arch/armv7/lib/_uldivmod.S
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ */
+
+/*
+ * A, Q = r0 + (r1 << 32)
+ * B, R = r2 + (r3 << 32)
+ * A / B = Q ... R
+ */
+
+ .text
+ .global __aeabi_uldivmod
+ .type __aeabi_uldivmod, function
+ .align 0
+
+A_0 .req r0
+A_1 .req r1
+B_0 .req r2
+B_1 .req r3
+C_0 .req r4
+C_1 .req r5
+D_0 .req r6
+D_1 .req r7
+
+Q_0 .req r0
+Q_1 .req r1
+R_0 .req r2
+R_1 .req r3
+
+__aeabi_uldivmod:
+ stmfd sp!, {r4, r5, r6, r7, lr}
+ @ Test if B == 0
+ orrs ip, B_0, B_1 @ Z set -> B == 0
+ beq L_div_by_0
+ @ Test if B is power of 2: (B & (B - 1)) == 0
+ subs C_0, B_0, #1
+ sbc C_1, B_1, #0
+ tst C_0, B_0
+ tsteq B_1, C_1
+ beq L_pow2
+ @ Test if A_1 == B_1 == 0
+ orrs ip, A_1, B_1
+ beq L_div_32_32
+
+L_div_64_64:
+ mov C_0, #1
+ mov C_1, #0
+ @ D_0 = clz A
+ teq A_1, #0
+ clz D_0, A_1
+ clzeq ip, A_0
+ addeq D_0, D_0, ip
+ @ D_1 = clz B
+ teq B_1, #0
+ clz D_1, B_1
+ clzeq ip, B_0
+ addeq D_1, D_1, ip
+ @ if clz B - clz A > 0
+ subs D_0, D_1, D_0
+ bls L_done_shift
+ @ B <<= (clz B - clz A)
+ subs D_1, D_0, #32
+ rsb ip, D_0, #32
+ movmi B_1, B_1, lsl D_0
+ orrmi B_1, B_1, B_0, lsr ip
+ movpl B_1, B_0, lsl D_1
+ mov B_0, B_0, lsl D_0
+ @ C = 1 << (clz B - clz A)
+ movmi C_1, C_1, lsl D_0
+ orrmi C_1, C_1, C_0, lsr ip
+ movpl C_1, C_0, lsl D_1
+ mov C_0, C_0, lsl D_0
+L_done_shift:
+ mov D_0, #0
+ mov D_1, #0
+ @ C: current bit; D: result
+L_subtract:
+ @ if A >= B
+ cmp A_1, B_1
+ cmpeq A_0, B_0
+ bcc L_update
+ @ A -= B
+ subs A_0, A_0, B_0
+ sbc A_1, A_1, B_1
+ @ D |= C
+ orr D_0, D_0, C_0
+ orr D_1, D_1, C_1
+L_update:
+ @ if A == 0: break
+ orrs ip, A_1, A_0
+ beq L_exit
+ @ C >>= 1
+ movs C_1, C_1, lsr #1
+ movs C_0, C_0, rrx
+ @ if C == 0: break
+ orrs ip, C_1, C_0
+ beq L_exit
+ @ B >>= 1
+ movs B_1, B_1, lsr #1
+ mov B_0, B_0, rrx
+ b L_subtract
+L_exit:
+ @ Note: A, B & Q, R are aliases
+ mov R_0, A_0
+ mov R_1, A_1
+ mov Q_0, D_0
+ mov Q_1, D_1
+ ldmfd sp!, {r4, r5, r6, r7, pc}
+
+L_div_32_32:
+ @ Note: A_0 & r0 are aliases
+ @ Q_1 r1
+ mov r1, B_0
+ bl __aeabi_uidivmod
+ mov R_0, r1
+ mov R_1, #0
+ mov Q_1, #0
+ ldmfd sp!, {r4, r5, r6, r7, pc}
+
+L_pow2:
+ @ Note: A, B and Q, R are aliases
+ @ R = A & (B - 1)
+ and C_0, A_0, C_0
+ and C_1, A_1, C_1
+ @ Q = A >> log2(B)
+ @ Note: B must not be 0 here!
+ clz D_0, B_0
+ add D_1, D_0, #1
+ rsbs D_0, D_0, #31
+ bpl L_1
+ clz D_0, B_1
+ rsb D_0, D_0, #31
+ mov A_0, A_1, lsr D_0
+ add D_0, D_0, #32
+L_1:
+ movpl A_0, A_0, lsr D_0
+ orrpl A_0, A_0, A_1, lsl D_1
+ mov A_1, A_1, lsr D_0
+ @ Mov back C to R
+ mov R_0, C_0
+ mov R_1, C_1
+ ldmfd sp!, {r4, r5, r6, r7, pc}
+
+L_div_by_0:
+ bl __div0
+ @ As wrong as it could be
+ mov Q_0, #0
+ mov Q_1, #0
+ mov R_0, #0
+ mov R_1, #0
+ ldmfd sp!, {r4, r5, r6, r7, pc}
diff --git a/src/arch/armv7/lib/bootm.c b/src/arch/armv7/lib/bootm.c
new file mode 100644
index 0000000000..afe1b70814
--- /dev/null
+++ b/src/arch/armv7/lib/bootm.c
@@ -0,0 +1,405 @@
+/*
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
+ *
+ * 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
+ *
+ */
+
+#include <common.h>
+#include <command.h>
+#include <image.h>
+#include <u-boot/zlib.h>
+#include <arch/byteorder.h>
+#include <fdt.h>
+#include <libfdt.h>
+#include <fdt_support.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
+ defined (CONFIG_CMDLINE_TAG) || \
+ defined (CONFIG_INITRD_TAG) || \
+ defined (CONFIG_SERIAL_TAG) || \
+ defined (CONFIG_REVISION_TAG)
+static void setup_start_tag (bd_t *bd);
+
+# ifdef CONFIG_SETUP_MEMORY_TAGS
+static void setup_memory_tags (bd_t *bd);
+# endif
+static void setup_commandline_tag (bd_t *bd, char *commandline);
+
+# ifdef CONFIG_INITRD_TAG
+static void setup_initrd_tag (bd_t *bd, ulong initrd_start,
+ ulong initrd_end);
+# endif
+static void setup_end_tag (bd_t *bd);
+
+# if defined (CONFIG_VFD) || defined (CONFIG_LCD)
+# if !defined(CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED)
+static void setup_videolfb_tag (gd_t *gd);
+# endif
+# endif
+
+static struct tag *params;
+#endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */
+
+static ulong get_sp(void);
+#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL)
+static int bootm_linux_fdt(int machid, bootm_headers_t *images);
+#endif
+
+void arch_lmb_reserve(struct lmb *lmb)
+{
+ ulong sp;
+
+ /*
+ * Booting a (Linux) kernel image
+ *
+ * Allocate space for command line and board info - the
+ * address should be as high as possible within the reach of
+ * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused
+ * memory, which means far enough below the current stack
+ * pointer.
+ */
+ sp = get_sp();
+ debug("## Current stack ends at 0x%08lx ", sp);
+
+ /* adjust sp by 1K to be safe */
+ sp -= 1024;
+ lmb_reserve(lmb, sp,
+ gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp);
+}
+
+static void announce_and_cleanup(void)
+{
+ printf("\nStarting kernel ...\n\n");
+ bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
+
+#ifdef CONFIG_USB_DEVICE
+ {
+ extern void udc_disconnect(void);
+ udc_disconnect();
+ }
+#endif
+ cleanup_before_linux();
+}
+
+int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
+{
+ bd_t *bd = gd->bd;
+ char *s;
+ int machid = bd->bi_arch_number;
+ void (*kernel_entry)(int zero, int arch, uint params);
+
+#ifdef CONFIG_CMDLINE_TAG
+ char *commandline = getenv ("bootargs");
+#endif
+
+ if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
+ return 1;
+
+ s = getenv ("machid");
+ if (s) {
+ machid = simple_strtoul (s, NULL, 16);
+ printf ("Using machid 0x%x from environment\n", machid);
+ }
+
+ bootstage_mark(BOOTSTAGE_ID_RUN_OS);
+
+#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL)
+ if (images->ft_len)
+ return bootm_linux_fdt(machid, images);
+#endif
+
+ kernel_entry = (void (*)(int, int, uint))images->ep;
+
+ debug ("## Transferring control to Linux (at address %08lx) ...\n",
+ (ulong) kernel_entry);
+
+#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
+ defined (CONFIG_CMDLINE_TAG) || \
+ defined (CONFIG_INITRD_TAG) || \
+ defined (CONFIG_SERIAL_TAG) || \
+ defined (CONFIG_REVISION_TAG)
+ setup_start_tag (bd);
+#ifdef CONFIG_SERIAL_TAG
+ setup_serial_tag (&params);
+#endif
+#ifdef CONFIG_REVISION_TAG
+ setup_revision_tag (&params);
+#endif
+#ifdef CONFIG_SETUP_MEMORY_TAGS
+ setup_memory_tags (bd);
+#endif
+#ifdef CONFIG_CMDLINE_TAG
+ setup_commandline_tag (bd, commandline);
+#endif
+#ifdef CONFIG_INITRD_TAG
+ if (images->rd_start && images->rd_end)
+ setup_initrd_tag (bd, images->rd_start, images->rd_end);
+#endif
+#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
+#if !defined(CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED)
+ setup_videolfb_tag ((gd_t *) gd);
+#endif
+#endif
+ setup_end_tag (bd);
+#endif
+
+ announce_and_cleanup();
+
+ kernel_entry(0, machid, bd->bi_boot_params);
+ /* does not return */
+
+ return 1;
+}
+
+#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL)
+static int fixup_memory_node(void *blob)
+{
+ bd_t *bd = gd->bd;
+ int bank;
+ u64 start[CONFIG_NR_DRAM_BANKS];
+ u64 size[CONFIG_NR_DRAM_BANKS];
+
+ for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
+ start[bank] = bd->bi_dram[bank].start;
+ size[bank] = bd->bi_dram[bank].size;
+ }
+
+ return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS);
+}
+
+static int bootm_linux_fdt(int machid, bootm_headers_t *images)
+{
+ ulong rd_len;
+ void (*kernel_entry)(int zero, int dt_machid, void *dtblob);
+ ulong of_size = images->ft_len;
+ char **of_flat_tree = &images->ft_addr;
+ ulong *initrd_start = &images->initrd_start;
+ ulong *initrd_end = &images->initrd_end;
+ struct lmb *lmb = &images->lmb;
+ int ret;
+
+ kernel_entry = (void (*)(int, int, void *))images->ep;
+
+ boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree);
+
+ rd_len = images->rd_end - images->rd_start;
+ ret = boot_ramdisk_high(lmb, images->rd_start, rd_len,
+ initrd_start, initrd_end);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_OF_BOARD_SETUP
+ /* Try to reserve 1024 bytes for board fixups */
+ if (!fdt_open_into(*of_flat_tree, *of_flat_tree, of_size + 1024))
+ of_size += 1024;
+ /* Call the board-specific fixup routine */
+ ft_board_setup(*of_flat_tree, gd->bd);
+#endif
+#ifdef CONFIG_OF_UPDATE_FDT_BEFORE_BOOT
+ /* this must be earlier than boot_relocate_fdt */
+ ret = fit_update_fdt_before_boot(*of_flat_tree, &of_size);
+ if (ret)
+ return ret;
+#endif
+
+ ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size);
+ if (ret)
+ return ret;
+
+ debug("## Transferring control to Linux (at address %08lx) ...\n",
+ (ulong) kernel_entry);
+
+ fdt_chosen(*of_flat_tree, 1);
+
+ fixup_memory_node(*of_flat_tree);
+
+ fdt_fixup_ethernet(*of_flat_tree);
+
+ fdt_initrd(*of_flat_tree, *initrd_start, *initrd_end, 1);
+
+ announce_and_cleanup();
+
+ kernel_entry(0, machid, *of_flat_tree);
+ /* does not return */
+
+ return 1;
+}
+#endif
+
+#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
+ defined (CONFIG_CMDLINE_TAG) || \
+ defined (CONFIG_INITRD_TAG) || \
+ defined (CONFIG_SERIAL_TAG) || \
+ defined (CONFIG_REVISION_TAG)
+static void setup_start_tag (bd_t *bd)
+{
+ params = (struct tag *) bd->bi_boot_params;
+
+ params->hdr.tag = ATAG_CORE;
+ params->hdr.size = tag_size (tag_core);
+
+#if defined (ATAG_CORE_FLAGS) && \
+ defined (ATAG_PAGE_SIZE) && \
+ defined (ATAG_CORE_RDEV)
+ params->u.core.flags = ATAG_CORE_FLAGS;
+ params->u.core.pagesize = ATAG_PAGE_SIZE;
+ params->u.core.rootdev = ATAG_CORE_RDEV;
+#else
+ params->u.core.flags = 0;
+ params->u.core.pagesize = 0;
+ params->u.core.rootdev = 0;
+#endif
+
+ params = tag_next (params);
+}
+
+
+#ifdef CONFIG_SETUP_MEMORY_TAGS
+static void setup_memory_tags (bd_t *bd)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+ params->hdr.tag = ATAG_MEM;
+ params->hdr.size = tag_size (tag_mem32);
+
+ params->u.mem.start = bd->bi_dram[i].start;
+ params->u.mem.size = bd->bi_dram[i].size;
+
+ params = tag_next (params);
+ }
+}
+#endif /* CONFIG_SETUP_MEMORY_TAGS */
+
+
+static void setup_commandline_tag (bd_t *bd, char *commandline)
+{
+ char *p;
+
+ if (!commandline)
+ return;
+
+ /* eat leading white space */
+ for (p = commandline; *p == ' '; p++);
+
+ /* skip non-existent command lines so the kernel will still
+ * use its default command line.
+ */
+ if (*p == '\0')
+ return;
+
+ params->hdr.tag = ATAG_CMDLINE;
+ params->hdr.size =
+ (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
+
+ strcpy (params->u.cmdline.cmdline, p);
+
+ params = tag_next (params);
+}
+
+
+#ifdef CONFIG_INITRD_TAG
+static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulong initrd_end)
+{
+ /* an ATAG_INITRD node tells the kernel where the compressed
+ * ramdisk can be found. ATAG_RDIMG is a better name, actually.
+ */
+ params->hdr.tag = ATAG_INITRD2;
+ params->hdr.size = tag_size (tag_initrd);
+
+ params->u.initrd.start = initrd_start;
+ params->u.initrd.size = initrd_end - initrd_start;
+
+ params = tag_next (params);
+}
+#endif /* CONFIG_INITRD_TAG */
+
+#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
+#if !defined(CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED)
+extern ulong calc_fbsize (void);
+static void setup_videolfb_tag (gd_t *gd)
+{
+ /* An ATAG_VIDEOLFB node tells the kernel where and how large
+ * the framebuffer for video was allocated (among other things).
+ * Note that a _physical_ address is passed !
+ *
+ * We only use it to pass the address and size, the other entries
+ * in the tag_videolfb are not of interest.
+ */
+ params->hdr.tag = ATAG_VIDEOLFB;
+ params->hdr.size = tag_size (tag_videolfb);
+
+ params->u.videolfb.lfb_base = (u32) gd->fb_base;
+ /* Fb size is calculated according to parameters for our panel
+ */
+ params->u.videolfb.lfb_size = calc_fbsize();
+
+ params = tag_next (params);
+}
+#endif /* CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED */
+#endif /* CONFIG_VFD || CONFIG_LCD */
+
+#ifdef CONFIG_SERIAL_TAG
+void setup_serial_tag (struct tag **tmp)
+{
+ struct tag *params = *tmp;
+ struct tag_serialnr serialnr;
+ void get_board_serial(struct tag_serialnr *serialnr);
+
+ get_board_serial(&serialnr);
+ params->hdr.tag = ATAG_SERIAL;
+ params->hdr.size = tag_size (tag_serialnr);
+ params->u.serialnr.low = serialnr.low;
+ params->u.serialnr.high= serialnr.high;
+ params = tag_next (params);
+ *tmp = params;
+}
+#endif
+
+#ifdef CONFIG_REVISION_TAG
+void setup_revision_tag(struct tag **in_params)
+{
+ u32 rev = 0;
+ u32 get_board_rev(void);
+
+ rev = get_board_rev();
+ params->hdr.tag = ATAG_REVISION;
+ params->hdr.size = tag_size (tag_revision);
+ params->u.revision.rev = rev;
+ params = tag_next (params);
+}
+#endif /* CONFIG_REVISION_TAG */
+
+static void setup_end_tag (bd_t *bd)
+{
+ params->hdr.tag = ATAG_NONE;
+ params->hdr.size = 0;
+}
+#endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */
+
+static ulong get_sp(void)
+{
+ ulong ret;
+
+ asm("mov %0, sp" : "=r"(ret) : );
+ return ret;
+}
diff --git a/src/arch/armv7/lib/c_start.S b/src/arch/armv7/lib/c_start.S
new file mode 100644
index 0000000000..b6e746299d
--- /dev/null
+++ b/src/arch/armv7/lib/c_start.S
@@ -0,0 +1,9 @@
+.section ".text"
+.globl _start
+_start:
+ bl _hardwaremain
+
+_hardwaremain: .word hardwaremain
+@ .word hardwaremain
+
+
diff --git a/src/arch/armv7/lib/cache-cp15.c b/src/arch/armv7/lib/cache-cp15.c
new file mode 100644
index 0000000000..1786725148
--- /dev/null
+++ b/src/arch/armv7/lib/cache-cp15.c
@@ -0,0 +1,304 @@
+/*
+ * (C) Copyright 2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * 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
+ */
+
+/* FIXME(dhendrix): clean-up weak symbols if it looks unlikely we'll
+ want to override them with anything other than what's in cache_v7. */
+#include <common.h>
+#include <stdlib.h>
+#include <system.h>
+#include <global_data.h>
+
+#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if 0
+void __arm_init_before_mmu(void)
+{
+}
+void arm_init_before_mmu(void)
+ __attribute__((weak, alias("__arm_init_before_mmu")));
+#endif
+
+static void cp_delay (void)
+{
+ volatile int i;
+
+ /* copro seems to need some delay between reading and writing */
+ for (i = 0; i < 100; i++)
+ nop();
+ asm volatile("" : : : "memory");
+}
+
+static void set_section_dcache(int section, enum dcache_option option)
+{
+ u32 value = section << MMU_SECTION_SHIFT | (3 << 10);
+// u32 *page_table = (u32 *)gd->tlb_addr;
+ u32 *page_table;
+ unsigned int tlb_addr;
+ unsigned int tlb_size = 4096 * 4;
+
+ /*
+ * FIXME(dhendrix): This calculation is from arch/arm/lib/board.c
+ * in u-boot. We may need to subtract more due to logging.
+ */
+ tlb_addr = (CONFIG_SYS_SDRAM_BASE + (CONFIG_DRAM_SIZE_MB << 20UL));
+ tlb_addr -= tlb_size;
+ /* round down to next 64KB limit */
+ tlb_addr &= ~(0x10000 - 1);
+ page_table = (u32 *)tlb_addr;
+
+ switch (option) {
+ case DCACHE_WRITETHROUGH:
+ value |= 0x1a;
+ break;
+
+ case DCACHE_WRITEBACK:
+ value |= 0x1e;
+ break;
+
+ case DCACHE_OFF:
+ value |= 0x12;
+ break;
+ }
+
+ page_table[section] = value;
+}
+
+#if 0
+void __mmu_page_table_flush(unsigned long start, unsigned long stop)
+{
+ debug("%s: Warning: not implemented\n", __func__);
+}
+#endif
+
+#if 0
+void mmu_page_table_flush(unsigned long start, unsigned long stop)
+ __attribute__((weak, alias("__mmu_page_table_flush")));
+#endif
+
+void mmu_set_region_dcache(unsigned long start, int size, enum dcache_option option)
+{
+ u32 *page_table = (u32 *)gd->tlb_addr;
+ u32 upto, end;
+
+ end = ALIGN(start + size, MMU_SECTION_SIZE) >> MMU_SECTION_SHIFT;
+ start = start >> MMU_SECTION_SHIFT;
+ debug("mmu_set_region_dcache start=%x, size=%x, option=%d\n",
+ start, size, option);
+ for (upto = start; upto < end; upto++)
+ set_section_dcache(upto, option);
+ mmu_page_table_flush((u32)&page_table[start], (u32)&page_table[end]);
+}
+
+#if 0
+static inline void dram_bank_mmu_setup(int bank)
+{
+// bd_t *bd = gd->bd;
+ int i;
+
+ debug("%s: bank: %d\n", __func__, bank);
+ for (i = bd->bi_dram[bank].start >> 20;
+ i < (bd->bi_dram[bank].start + bd->bi_dram[bank].size) >> 20;
+ i++) {
+#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
+ set_section_dcache(i, DCACHE_WRITETHROUGH);
+#else
+ set_section_dcache(i, DCACHE_WRITEBACK);
+#endif
+ }
+}
+#endif
+
+/* FIXME(dhendrix): modified to take arguments from the caller (mainboard's
+ romstage.c) so it doesn't rely on global data struct */
+/**
+ * dram_bank_mmu_set - set up the data cache policy for a given dram bank
+ *
+ * @start: virtual address start of bank
+ * @size: size of bank (in bytes)
+ */
+inline void dram_bank_mmu_setup(unsigned long start, unsigned long size)
+{
+ int i;
+
+ debug("%s: bank: %d\n", __func__, bank);
+ for (i = start >> 20; i < (start + size) >> 20; i++) {
+#if defined(CONFIG_ARM_DCACHE_POLICY_WRITEBACK)
+ set_section_dcache(i, DCACHE_WRITEBACK);
+#elif defined(CONFIG_ARM_DCACHE_POLICY_WRITETHROUGH)
+ set_section_dcache(i, DCACHE_WRITETHROUGH);
+#else
+#error "Must define dcache policy."
+#endif
+ }
+}
+
+/* to activate the MMU we need to set up virtual memory: use 1M areas */
+static inline void mmu_setup(void)
+{
+ int i;
+ u32 reg;
+
+ arm_init_before_mmu();
+ /* Set up an identity-mapping for all 4GB, rw for everyone */
+ for (i = 0; i < 4096; i++)
+ set_section_dcache(i, DCACHE_OFF);
+
+ /* FIXME(dhendrix): u-boot's global data struct was used here... */
+#if 0
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
+ dram_bank_mmu_setup(i);
+ }
+#endif
+#if 0
+ /* comes from board's romstage.c, since we need to know which
+ ranges to setup */
+ mainboard_setup_mmu();
+#endif
+ dram_bank_mmu_setup(CONFIG_SYS_SDRAM_BASE, CONFIG_DRAM_SIZE_MB << 20);
+
+ /* Copy the page table address to cp15 */
+ asm volatile("mcr p15, 0, %0, c2, c0, 0"
+ : : "r" (gd->tlb_addr) : "memory");
+ /* Set the access control to all-supervisor */
+ asm volatile("mcr p15, 0, %0, c3, c0, 0"
+ : : "r" (~0));
+ /* and enable the mmu */
+ reg = get_cr(); /* get control reg. */
+ cp_delay();
+ set_cr(reg | CR_M);
+}
+
+static int mmu_enabled(void)
+{
+ return get_cr() & CR_M;
+}
+
+/* cache_bit must be either CR_I or CR_C */
+static void cache_enable(uint32_t cache_bit)
+{
+ uint32_t reg;
+
+ /* The data cache is not active unless the mmu is enabled too */
+ if ((cache_bit == CR_C) && !mmu_enabled())
+ mmu_setup();
+ reg = get_cr(); /* get control reg. */
+ cp_delay();
+ set_cr(reg | cache_bit);
+}
+
+/*
+ * Big hack warning!
+ *
+ * Devs like to compile with -O0 to get a nice debugging illusion. But this
+ * function does not survive that since -O0 causes the compiler to read the
+ * PC back from the stack after the dcache flush. Might it be possible to fix
+ * this by flushing the write buffer?
+ */
+static void cache_disable(uint32_t cache_bit) __attribute__ ((optimize(2)));
+
+/* cache_bit must be either CR_I or CR_C */
+static void cache_disable(uint32_t cache_bit)
+{
+ uint32_t reg;
+
+ if (cache_bit == CR_C) {
+ /* if cache isn;t enabled no need to disable */
+ reg = get_cr();
+ if ((reg & CR_C) != CR_C)
+ return;
+ /* if disabling data cache, disable mmu too */
+ cache_bit |= CR_M;
+ }
+ reg = get_cr();
+ cp_delay();
+ if (cache_bit == (CR_C | CR_M))
+ flush_dcache_all();
+ set_cr(reg & ~cache_bit);
+}
+#endif
+
+#ifdef CONFIG_SYS_ICACHE_OFF
+void icache_enable (void)
+{
+ return;
+}
+
+void icache_disable (void)
+{
+ return;
+}
+
+int icache_status (void)
+{
+ return 0; /* always off */
+}
+#else
+void icache_enable(void)
+{
+ cache_enable(CR_I);
+}
+
+void icache_disable(void)
+{
+ cache_disable(CR_I);
+}
+
+int icache_status(void)
+{
+ return (get_cr() & CR_I) != 0;
+}
+#endif
+
+#ifdef CONFIG_SYS_DCACHE_OFF
+void dcache_enable (void)
+{
+ return;
+}
+
+void dcache_disable (void)
+{
+ return;
+}
+
+int dcache_status (void)
+{
+ return 0; /* always off */
+}
+#else
+void dcache_enable(void)
+{
+ cache_enable(CR_C);
+}
+
+void dcache_disable(void)
+{
+ cache_disable(CR_C);
+}
+
+int dcache_status(void)
+{
+ return (get_cr() & CR_C) != 0;
+}
+#endif
diff --git a/src/arch/armv7/lib/cache_v7.c b/src/arch/armv7/lib/cache_v7.c
new file mode 100644
index 0000000000..13bb59f025
--- /dev/null
+++ b/src/arch/armv7/lib/cache_v7.c
@@ -0,0 +1,427 @@
+/*
+ * (C) Copyright 2010
+ * Texas Instruments, <www.ti.com>
+ * Aneesh V <aneesh@ti.com>
+ *
+ * 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
+ */
+#include <armv7.h>
+#include <common.h>
+#include <system.h>
+#include <utils.h>
+#include <console/console.h>
+
+#define ARMV7_DCACHE_INVAL_ALL 1
+#define ARMV7_DCACHE_CLEAN_INVAL_ALL 2
+#define ARMV7_DCACHE_INVAL_RANGE 3
+#define ARMV7_DCACHE_CLEAN_INVAL_RANGE 4
+
+#ifndef CONFIG_SYS_DCACHE_OFF
+/*
+ * Write the level and type you want to Cache Size Selection Register(CSSELR)
+ * to get size details from Current Cache Size ID Register(CCSIDR)
+ */
+static void set_csselr(u32 level, u32 type)
+{ u32 csselr = level << 1 | type;
+
+ /* Write to Cache Size Selection Register(CSSELR) */
+ asm volatile ("mcr p15, 2, %0, c0, c0, 0" : : "r" (csselr));
+}
+
+static u32 get_ccsidr(void)
+{
+ u32 ccsidr;
+
+ /* Read current CP15 Cache Size ID Register */
+ asm volatile ("mrc p15, 1, %0, c0, c0, 0" : "=r" (ccsidr));
+ return ccsidr;
+}
+
+static u32 get_clidr(void)
+{
+ u32 clidr;
+
+ /* Read current CP15 Cache Level ID Register */
+ asm volatile ("mrc p15,1,%0,c0,c0,1" : "=r" (clidr));
+ return clidr;
+}
+
+static void v7_inval_dcache_level_setway(u32 level, u32 num_sets,
+ u32 num_ways, u32 way_shift,
+ u32 log2_line_len)
+{
+ int way, set, setway;
+
+ /*
+ * For optimal assembly code:
+ * a. count down
+ * b. have bigger loop inside
+ */
+ for (way = num_ways - 1; way >= 0 ; way--) {
+ for (set = num_sets - 1; set >= 0; set--) {
+ setway = (level << 1) | (set << log2_line_len) |
+ (way << way_shift);
+ /* Invalidate data/unified cache line by set/way */
+ asm volatile (" mcr p15, 0, %0, c7, c6, 2"
+ : : "r" (setway));
+ }
+ }
+ /* DSB to make sure the operation is complete */
+ CP15DSB;
+}
+
+static void v7_clean_inval_dcache_level_setway(u32 level, u32 num_sets,
+ u32 num_ways, u32 way_shift,
+ u32 log2_line_len)
+{
+ int way, set, setway;
+
+ /*
+ * For optimal assembly code:
+ * a. count down
+ * b. have bigger loop inside
+ */
+ for (way = num_ways - 1; way >= 0 ; way--) {
+ for (set = num_sets - 1; set >= 0; set--) {
+ setway = (level << 1) | (set << log2_line_len) |
+ (way << way_shift);
+ /*
+ * Clean & Invalidate data/unified
+ * cache line by set/way
+ */
+ asm volatile (" mcr p15, 0, %0, c7, c14, 2"
+ : : "r" (setway));
+ }
+ }
+ /* DSB to make sure the operation is complete */
+ CP15DSB;
+}
+
+static void v7_maint_dcache_level_setway(u32 level, u32 operation)
+{
+ u32 ccsidr;
+ u32 num_sets, num_ways, log2_line_len, log2_num_ways;
+ u32 way_shift;
+
+ set_csselr(level, ARMV7_CSSELR_IND_DATA_UNIFIED);
+
+ ccsidr = get_ccsidr();
+
+ log2_line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >>
+ CCSIDR_LINE_SIZE_OFFSET) + 2;
+ /* Converting from words to bytes */
+ log2_line_len += 2;
+
+ num_ways = ((ccsidr & CCSIDR_ASSOCIATIVITY_MASK) >>
+ CCSIDR_ASSOCIATIVITY_OFFSET) + 1;
+ num_sets = ((ccsidr & CCSIDR_NUM_SETS_MASK) >>
+ CCSIDR_NUM_SETS_OFFSET) + 1;
+ /*
+ * According to ARMv7 ARM number of sets and number of ways need
+ * not be a power of 2
+ */
+ log2_num_ways = log_2_n_round_up(num_ways);
+
+ way_shift = (32 - log2_num_ways);
+ if (operation == ARMV7_DCACHE_INVAL_ALL) {
+ v7_inval_dcache_level_setway(level, num_sets, num_ways,
+ way_shift, log2_line_len);
+ } else if (operation == ARMV7_DCACHE_CLEAN_INVAL_ALL) {
+ v7_clean_inval_dcache_level_setway(level, num_sets, num_ways,
+ way_shift, log2_line_len);
+ }
+}
+
+static void v7_maint_dcache_all(u32 operation)
+{
+ u32 level, cache_type, level_start_bit = 0;
+
+ u32 clidr = get_clidr();
+
+ for (level = 0; level < 7; level++) {
+ cache_type = (clidr >> level_start_bit) & 0x7;
+ if ((cache_type == ARMV7_CLIDR_CTYPE_DATA_ONLY) ||
+ (cache_type == ARMV7_CLIDR_CTYPE_INSTRUCTION_DATA) ||
+ (cache_type == ARMV7_CLIDR_CTYPE_UNIFIED))
+ v7_maint_dcache_level_setway(level, operation);
+ level_start_bit += 3;
+ }
+}
+
+static void v7_dcache_clean_inval_range(u32 start,
+ u32 stop, u32 line_len)
+{
+ u32 mva;
+
+ /* Align start to cache line boundary */
+ start &= ~(line_len - 1);
+ for (mva = start; mva < stop; mva = mva + line_len) {
+ /* DCCIMVAC - Clean & Invalidate data cache by MVA to PoC */
+ asm volatile ("mcr p15, 0, %0, c7, c14, 1" : : "r" (mva));
+ }
+}
+
+static void v7_dcache_inval_range(u32 start, u32 stop, u32 line_len)
+{
+ u32 mva;
+
+ /*
+ * If start address is not aligned to cache-line do not
+ * invalidate the first cache-line
+ */
+ if (start & (line_len - 1)) {
+ printk(BIOS_ERR, "%s - start address is not aligned - 0x%08x\n",
+ __func__, start);
+ /* move to next cache line */
+ start = (start + line_len - 1) & ~(line_len - 1);
+ }
+
+ /*
+ * If stop address is not aligned to cache-line do not
+ * invalidate the last cache-line
+ */
+ if (stop & (line_len - 1)) {
+ printk(BIOS_ERR, "%s - stop address is not aligned - 0x%08x\n",
+ __func__, stop);
+ /* align to the beginning of this cache line */
+ stop &= ~(line_len - 1);
+ }
+
+ for (mva = start; mva < stop; mva = mva + line_len) {
+ /* DCIMVAC - Invalidate data cache by MVA to PoC */
+ asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (mva));
+ }
+}
+
+static void v7_dcache_maint_range(u32 start, u32 stop, u32 range_op)
+{
+ u32 line_len = dcache_get_line_size();
+
+ switch (range_op) {
+ case ARMV7_DCACHE_CLEAN_INVAL_RANGE:
+ v7_dcache_clean_inval_range(start, stop, line_len);
+ break;
+ case ARMV7_DCACHE_INVAL_RANGE:
+ v7_dcache_inval_range(start, stop, line_len);
+ break;
+ }
+
+ /* DSB to make sure the operation is complete */
+ CP15DSB;
+}
+
+/* Invalidate TLB */
+static void v7_inval_tlb(void)
+{
+ /* Invalidate entire unified TLB */
+ asm volatile ("mcr p15, 0, %0, c8, c7, 0" : : "r" (0));
+ /* Invalidate entire data TLB */
+ asm volatile ("mcr p15, 0, %0, c8, c6, 0" : : "r" (0));
+ /* Invalidate entire instruction TLB */
+ asm volatile ("mcr p15, 0, %0, c8, c5, 0" : : "r" (0));
+ /* Full system DSB - make sure that the invalidation is complete */
+ CP15DSB;
+ /* Full system ISB - make sure the instruction stream sees it */
+ CP15ISB;
+}
+
+ulong dcache_get_line_size(void)
+{
+ u32 line_len, ccsidr;
+
+ ccsidr = get_ccsidr();
+ line_len = ((ccsidr & CCSIDR_LINE_SIZE_MASK) >>
+ CCSIDR_LINE_SIZE_OFFSET) + 2;
+ /* Converting from words to bytes */
+ line_len += 2;
+ /* converting from log2(linelen) to linelen */
+ line_len = 1 << line_len;
+
+ return line_len;
+}
+
+void invalidate_dcache_all(void)
+{
+ v7_maint_dcache_all(ARMV7_DCACHE_INVAL_ALL);
+
+ v7_outer_cache_inval_all();
+}
+
+/*
+ * Performs a clean & invalidation of the entire data cache
+ * at all levels
+ */
+void flush_dcache_all(void)
+{
+ v7_maint_dcache_all(ARMV7_DCACHE_CLEAN_INVAL_ALL);
+
+ v7_outer_cache_flush_all();
+}
+
+/*
+ * Invalidates range in all levels of D-cache/unified cache used:
+ * Affects the range [start, stop - 1]
+ */
+void invalidate_dcache_range(unsigned long start, unsigned long stop)
+{
+
+ v7_dcache_maint_range(start, stop, ARMV7_DCACHE_INVAL_RANGE);
+
+ v7_outer_cache_inval_range(start, stop);
+}
+
+/*
+ * Flush range(clean & invalidate) from all levels of D-cache/unified
+ * cache used:
+ * Affects the range [start, stop - 1]
+ */
+void flush_dcache_range(unsigned long start, unsigned long stop)
+{
+ v7_dcache_maint_range(start, stop, ARMV7_DCACHE_CLEAN_INVAL_RANGE);
+
+ v7_outer_cache_flush_range(start, stop);
+}
+
+void arm_init_before_mmu(void)
+{
+ v7_outer_cache_enable();
+ invalidate_dcache_all();
+ v7_inval_tlb();
+}
+
+void mmu_page_table_flush(unsigned long start, unsigned long stop)
+{
+ flush_dcache_range(start, stop);
+ v7_inval_tlb();
+}
+
+/*
+ * Flush range from all levels of d-cache/unified-cache used:
+ * Affects the range [start, start + size - 1]
+ */
+void flush_cache(unsigned long start, unsigned long size)
+{
+ flush_dcache_range(start, start + size);
+}
+#else /* #ifndef CONFIG_SYS_DCACHE_OFF */
+ulong dcache_get_line_size(void)
+{
+ return 0;
+}
+
+void invalidate_dcache_all(void)
+{
+}
+
+void flush_dcache_all(void)
+{
+}
+
+void invalidate_dcache_range(unsigned long start, unsigned long stop)
+{
+}
+
+void flush_dcache_range(unsigned long start, unsigned long stop)
+{
+}
+
+void arm_init_before_mmu(void)
+{
+}
+
+void flush_cache(unsigned long start, unsigned long size)
+{
+}
+
+void mmu_page_table_flush(unsigned long start, unsigned long stop)
+{
+}
+
+#endif /* #ifndef CONFIG_SYS_DCACHE_OFF */
+
+#ifndef CONFIG_SYS_ICACHE_OFF
+/* Invalidate entire I-cache and branch predictor array */
+void invalidate_icache_all(void)
+{
+ /*
+ * Invalidate all instruction caches to PoU.
+ * Also flushes branch target cache.
+ */
+ asm volatile ("mcr p15, 0, %0, c7, c5, 0" : : "r" (0));
+
+ /* Invalidate entire branch predictor array */
+ asm volatile ("mcr p15, 0, %0, c7, c5, 6" : : "r" (0));
+
+ /* Full system DSB - make sure that the invalidation is complete */
+ CP15DSB;
+
+ /* ISB - make sure the instruction stream sees it */
+ CP15ISB;
+}
+#else
+void invalidate_icache_all(void)
+{
+}
+#endif
+
+/*
+ * FIXME(dhendrix): had unexplainable compilation failure of weak symbols
+ * (in spite of having prototypes and whatnot)... ron's advice is "death
+ * to weak symbols!"
+ */
+#if 0
+/*
+ * Stub implementations for outer cache operations
+ */
+void __v7_outer_cache_enable(void)
+{
+}
+void v7_outer_cache_enable(void)
+ __attribute__((weak, alias("__v7_outer_cache_enable")));
+
+void __v7_outer_cache_disable(void)
+{
+}
+void v7_outer_cache_disable(void)
+ __attribute__((weak, alias("__v7_outer_cache_disable")));
+
+void __v7_outer_cache_flush_all(void)
+{
+}
+void v7_outer_cache_flush_all(void)
+ __attribute__((weak, alias("__v7_outer_cache_flush_all")));
+
+void __v7_outer_cache_inval_all(void)
+{
+}
+void v7_outer_cache_inval_all(void)
+ __attribute__((weak, alias("__v7_outer_cache_inval_all")));
+
+void __v7_outer_cache_flush_range(u32 start, u32 end)
+{
+}
+void v7_outer_cache_flush_range(u32 start, u32 end)
+ __attribute__((weak, alias("__v7_outer_cache_flush_range")));
+
+void __v7_outer_cache_inval_range(u32 start, u32 end)
+{
+}
+void v7_outer_cache_inval_range(u32 start, u32 end)
+ __attribute__((weak, alias("__v7_outer_cache_inval_range")));
+#endif
diff --git a/src/arch/armv7/lib/div.c b/src/arch/armv7/lib/div.c
new file mode 100644
index 0000000000..03352dc7a5
--- /dev/null
+++ b/src/arch/armv7/lib/div.c
@@ -0,0 +1,5 @@
+void __div0(void); // called from asm so no need for a prototype in a header
+void __div0(void)
+{
+ // div by zero
+}
diff --git a/src/arch/armv7/lib/div0.c b/src/arch/armv7/lib/div0.c
new file mode 100644
index 0000000000..632c247b5c
--- /dev/null
+++ b/src/arch/armv7/lib/div0.c
@@ -0,0 +1,32 @@
+/*
+ * (C) Copyright 2002
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * 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
+ */
+
+void __div0(void); // called from asm so no need for a prototype in a header
+
+/* Replacement (=dummy) for GNU/Linux division-by zero handler */
+void __div0 (void)
+{
+ extern void hang (void);
+
+ hang();
+}
diff --git a/src/arch/armv7/lib/div64.S b/src/arch/armv7/lib/div64.S
new file mode 100644
index 0000000000..44edf480ca
--- /dev/null
+++ b/src/arch/armv7/lib/div64.S
@@ -0,0 +1,208 @@
+/*
+ * linux/arch/arm/lib/div64.S
+ *
+ * Optimized computation of 64-bit dividend / 32-bit divisor
+ *
+ * Author: Nicolas Pitre
+ * Created: Oct 5, 2003
+ * Copyright: Monta Vista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+// FIXME
+//#include <linux/linkage.h>
+#define __LINUX_ARM_ARCH__ 7
+
+#ifdef __ARMEB__
+#define xh r0
+#define xl r1
+#define yh r2
+#define yl r3
+#else
+#define xl r0
+#define xh r1
+#define yl r2
+#define yh r3
+#endif
+
+/*
+ * __do_div64: perform a division with 64-bit dividend and 32-bit divisor.
+ *
+ * Note: Calling convention is totally non standard for optimal code.
+ * This is meant to be used by do_div() from include/asm/div64.h only.
+ *
+ * Input parameters:
+ * xh-xl = dividend (clobbered)
+ * r4 = divisor (preserved)
+ *
+ * Output values:
+ * yh-yl = result
+ * xh = remainder
+ *
+ * Clobbered regs: xl, ip
+ */
+
+
+.globl __do_div64;
+.align 4,0x90
+__do_div64:
+
+ @ Test for easy paths first.
+ subs ip, r4, #1
+ bls 9f @ divisor is 0 or 1
+ tst ip, r4
+ beq 8f @ divisor is power of 2
+
+ @ See if we need to handle upper 32-bit result.
+ cmp xh, r4
+ mov yh, #0
+ blo 3f
+
+ @ Align divisor with upper part of dividend.
+ @ The aligned divisor is stored in yl preserving the original.
+ @ The bit position is stored in ip.
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz yl, r4
+ clz ip, xh
+ sub yl, yl, ip
+ mov ip, #1
+ mov ip, ip, lsl yl
+ mov yl, r4, lsl yl
+
+#else
+
+ mov yl, r4
+ mov ip, #1
+1: cmp yl, #0x80000000
+ cmpcc yl, xh
+ movcc yl, yl, lsl #1
+ movcc ip, ip, lsl #1
+ bcc 1b
+
+#endif
+
+ @ The division loop for needed upper bit positions.
+ @ Break out early if dividend reaches 0.
+2: cmp xh, yl
+ orrcs yh, yh, ip
+ subcss xh, xh, yl
+ movnes ip, ip, lsr #1
+ mov yl, yl, lsr #1
+ bne 2b
+
+ @ See if we need to handle lower 32-bit result.
+3: cmp xh, #0
+ mov yl, #0
+ cmpeq xl, r4
+ movlo xh, xl
+ movlo pc, lr
+
+ @ The division loop for lower bit positions.
+ @ Here we shift remainer bits leftwards rather than moving the
+ @ divisor for comparisons, considering the carry-out bit as well.
+ mov ip, #0x80000000
+4: movs xl, xl, lsl #1
+ adcs xh, xh, xh
+ beq 6f
+ cmpcc xh, r4
+5: orrcs yl, yl, ip
+ subcs xh, xh, r4
+ movs ip, ip, lsr #1
+ bne 4b
+ mov pc, lr
+
+ @ The top part of remainder became zero. If carry is set
+ @ (the 33th bit) this is a false positive so resume the loop.
+ @ Otherwise, if lower part is also null then we are done.
+6: bcs 5b
+ cmp xl, #0
+ moveq pc, lr
+
+ @ We still have remainer bits in the low part. Bring them up.
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz xh, xl @ we know xh is zero here so...
+ add xh, xh, #1
+ mov xl, xl, lsl xh
+ mov ip, ip, lsr xh
+
+#else
+
+7: movs xl, xl, lsl #1
+ mov ip, ip, lsr #1
+ bcc 7b
+
+#endif
+
+ @ Current remainder is now 1. It is worthless to compare with
+ @ divisor at this point since divisor can not be smaller than 3 here.
+ @ If possible, branch for another shift in the division loop.
+ @ If no bit position left then we are done.
+ movs ip, ip, lsr #1
+ mov xh, #1
+ bne 4b
+ mov pc, lr
+
+8: @ Division by a power of 2: determine what that divisor order is
+ @ then simply shift values around
+
+#if __LINUX_ARM_ARCH__ >= 5
+
+ clz ip, r4
+ rsb ip, ip, #31
+
+#else
+
+ mov yl, r4
+ cmp r4, #(1 << 16)
+ mov ip, #0
+ movhs yl, yl, lsr #16
+ movhs ip, #16
+
+ cmp yl, #(1 << 8)
+ movhs yl, yl, lsr #8
+ addhs ip, ip, #8
+
+ cmp yl, #(1 << 4)
+ movhs yl, yl, lsr #4
+ addhs ip, ip, #4
+
+ cmp yl, #(1 << 2)
+ addhi ip, ip, #3
+ addls ip, ip, yl, lsr #1
+
+#endif
+
+ mov yh, xh, lsr ip
+ mov yl, xl, lsr ip
+ rsb ip, ip, #32
+ orr yl, yl, xh, lsl ip
+ mov xh, xl, lsl ip
+ mov xh, xh, lsr ip
+ mov pc, lr
+
+ @ eq -> division by 1: obvious enough...
+9: moveq yl, xl
+ moveq yh, xh
+ moveq xh, #0
+ moveq pc, lr
+
+ @ Division by 0:
+ str lr, [sp, #-8]!
+ bl __div0
+
+ @ as wrong as it could be...
+ mov yl, #0
+ mov yh, #0
+ mov xh, #0
+ ldr pc, [sp], #8
+
+@.type __do_div64, @function;
+@.size __do_div64, .-__do_div64,
+
diff --git a/src/arch/armv7/lib/eabi_compat.c b/src/arch/armv7/lib/eabi_compat.c
new file mode 100644
index 0000000000..5f59892432
--- /dev/null
+++ b/src/arch/armv7/lib/eabi_compat.c
@@ -0,0 +1,33 @@
+/*
+ * Utility functions needed for (some) EABI conformant tool chains.
+ *
+ * (C) Copyright 2009 Wolfgang Denk <wd@denx.de>
+ *
+ * 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.
+ */
+
+#include <common.h>
+
+/* FIXME(dhendrix): prototypes added for assembler */
+int raise (int signum);
+int raise (int signum)
+{
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
+ printf("raise: Signal # %d caught\n", signum);
+#endif
+ return 0;
+}
+
+/* Dummy function to avoid linker complaints */
+void __aeabi_unwind_cpp_pr0(void);
+void __aeabi_unwind_cpp_pr0(void)
+{
+};
+
+void __aeabi_unwind_cpp_pr1(void);
+void __aeabi_unwind_cpp_pr1(void)
+{
+};
diff --git a/src/arch/armv7/lib/hang_spl.c b/src/arch/armv7/lib/hang_spl.c
new file mode 100644
index 0000000000..630a57af47
--- /dev/null
+++ b/src/arch/armv7/lib/hang_spl.c
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ */
+
+#include <hang.h>
+
+void hang(void)
+{
+ for (;;);
+}
diff --git a/src/arch/armv7/lib/id.inc b/src/arch/armv7/lib/id.inc
new file mode 100644
index 0000000000..4da7024233
--- /dev/null
+++ b/src/arch/armv7/lib/id.inc
@@ -0,0 +1,18 @@
+ .section ".id", "a", %progbits
+
+ .globl __id_start
+__id_start:
+ver:
+ .asciz COREBOOT_VERSION
+vendor:
+ .asciz CONFIG_MAINBOARD_VENDOR
+part:
+ .asciz CONFIG_MAINBOARD_PART_NUMBER
+.long __id_end + CONFIG_ID_SECTION_OFFSET - ver /* Reverse offset to the vendor id */
+.long __id_end + CONFIG_ID_SECTION_OFFSET - vendor /* Reverse offset to the vendor id */
+.long __id_end + CONFIG_ID_SECTION_OFFSET - part /* Reverse offset to the part number */
+.long CONFIG_ROM_SIZE /* Size of this romimage */
+ .globl __id_end
+
+__id_end:
+.previous
diff --git a/src/arch/armv7/lib/id.lds b/src/arch/armv7/lib/id.lds
new file mode 100644
index 0000000000..9e31ee615c
--- /dev/null
+++ b/src/arch/armv7/lib/id.lds
@@ -0,0 +1,6 @@
+SECTIONS {
+ . = (0x100000000 - CONFIG_ID_SECTION_OFFSET) - (__id_end - __id_start);
+ .id (.): {
+ *(.id)
+ }
+}
diff --git a/src/arch/armv7/lib/interrupts.c b/src/arch/armv7/lib/interrupts.c
new file mode 100644
index 0000000000..6ee3f267ef
--- /dev/null
+++ b/src/arch/armv7/lib/interrupts.c
@@ -0,0 +1,198 @@
+/*
+ * (C) Copyright 2003
+ * Texas Instruments <www.ti.com>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Alex Zuepke <azu@sysgo.de>
+ *
+ * (C) Copyright 2002-2004
+ * Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
+ *
+ * (C) Copyright 2004
+ * Philippe Robin, ARM Ltd. <philippe.robin@arm.com>
+ *
+ * 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
+ */
+
+#include <common.h>
+//#include <asm/proc-armv/ptrace.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#ifdef CONFIG_USE_IRQ
+int interrupt_init (void)
+{
+ /*
+ * setup up stacks if necessary
+ */
+ IRQ_STACK_START = gd->irq_sp - 4;
+ IRQ_STACK_START_IN = gd->irq_sp + 8;
+ FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
+
+ return arch_interrupt_init();
+}
+
+/* enable IRQ interrupts */
+void enable_interrupts (void)
+{
+ unsigned long temp;
+ __asm__ __volatile__("mrs %0, cpsr\n"
+ "bic %0, %0, #0x80\n"
+ "msr cpsr_c, %0"
+ : "=r" (temp)
+ :
+ : "memory");
+}
+
+
+/*
+ * disable IRQ/FIQ interrupts
+ * returns true if interrupts had been enabled before we disabled them
+ */
+int disable_interrupts (void)
+{
+ unsigned long old,temp;
+ __asm__ __volatile__("mrs %0, cpsr\n"
+ "orr %1, %0, #0xc0\n"
+ "msr cpsr_c, %1"
+ : "=r" (old), "=r" (temp)
+ :
+ : "memory");
+ return (old & 0x80) == 0;
+}
+#else
+int interrupt_init (void)
+{
+ /*
+ * setup up stacks if necessary
+ */
+ IRQ_STACK_START_IN = gd->irq_sp + 8;
+
+ return 0;
+}
+
+void enable_interrupts (void)
+{
+ return;
+}
+int disable_interrupts (void)
+{
+ return 0;
+}
+#endif
+
+
+void bad_mode (void)
+{
+ panic ("Resetting CPU ...\n");
+ reset_cpu (0);
+}
+
+void show_regs (struct pt_regs *regs)
+{
+ unsigned long flags;
+ const char *processor_modes[] = {
+ "USER_26", "FIQ_26", "IRQ_26", "SVC_26",
+ "UK4_26", "UK5_26", "UK6_26", "UK7_26",
+ "UK8_26", "UK9_26", "UK10_26", "UK11_26",
+ "UK12_26", "UK13_26", "UK14_26", "UK15_26",
+ "USER_32", "FIQ_32", "IRQ_32", "SVC_32",
+ "UK4_32", "UK5_32", "UK6_32", "ABT_32",
+ "UK8_32", "UK9_32", "UK10_32", "UND_32",
+ "UK12_32", "UK13_32", "UK14_32", "SYS_32",
+ };
+
+ flags = condition_codes (regs);
+
+ printf ("pc : [<%08lx>] lr : [<%08lx>]\n"
+ "sp : %08lx ip : %08lx fp : %08lx\n",
+ instruction_pointer (regs),
+ regs->ARM_lr, regs->ARM_sp, regs->ARM_ip, regs->ARM_fp);
+ printf ("r10: %08lx r9 : %08lx r8 : %08lx\n",
+ regs->ARM_r10, regs->ARM_r9, regs->ARM_r8);
+ printf ("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
+ regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4);
+ printf ("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
+ regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0);
+ printf ("Flags: %c%c%c%c",
+ flags & CC_N_BIT ? 'N' : 'n',
+ flags & CC_Z_BIT ? 'Z' : 'z',
+ flags & CC_C_BIT ? 'C' : 'c', flags & CC_V_BIT ? 'V' : 'v');
+ printf (" IRQs %s FIQs %s Mode %s%s\n",
+ interrupts_enabled (regs) ? "on" : "off",
+ fast_interrupts_enabled (regs) ? "on" : "off",
+ processor_modes[processor_mode (regs)],
+ thumb_mode (regs) ? " (T)" : "");
+}
+
+void do_undefined_instruction (struct pt_regs *pt_regs)
+{
+ printf ("undefined instruction\n");
+ show_regs (pt_regs);
+ bad_mode ();
+}
+
+void do_software_interrupt (struct pt_regs *pt_regs)
+{
+ printf ("software interrupt\n");
+ show_regs (pt_regs);
+ bad_mode ();
+}
+
+void do_prefetch_abort (struct pt_regs *pt_regs)
+{
+ printf ("prefetch abort\n");
+ show_regs (pt_regs);
+ bad_mode ();
+}
+
+void do_data_abort (struct pt_regs *pt_regs)
+{
+ printf ("data abort\n");
+ show_regs (pt_regs);
+ bad_mode ();
+}
+
+void do_not_used (struct pt_regs *pt_regs)
+{
+ printf ("not used\n");
+ show_regs (pt_regs);
+ bad_mode ();
+}
+
+void do_fiq (struct pt_regs *pt_regs)
+{
+ printf ("fast interrupt request\n");
+ show_regs (pt_regs);
+ bad_mode ();
+}
+
+#ifndef CONFIG_USE_IRQ
+void do_irq (struct pt_regs *pt_regs)
+{
+ printf ("interrupt request\n");
+ show_regs (pt_regs);
+ bad_mode ();
+}
+#endif
diff --git a/src/arch/armv7/lib/memcpy.S b/src/arch/armv7/lib/memcpy.S
new file mode 100644
index 0000000000..f655256b5d
--- /dev/null
+++ b/src/arch/armv7/lib/memcpy.S
@@ -0,0 +1,243 @@
+/*
+ * linux/arch/arm/lib/memcpy.S
+ *
+ * Author: Nicolas Pitre
+ * Created: Sep 28, 2005
+ * Copyright: MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/assembler.h>
+
+#define W(instr) instr
+
+#define LDR1W_SHIFT 0
+#define STR1W_SHIFT 0
+
+ .macro ldr1w ptr reg abort
+ W(ldr) \reg, [\ptr], #4
+ .endm
+
+ .macro ldr4w ptr reg1 reg2 reg3 reg4 abort
+ ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4}
+ .endm
+
+ .macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
+ ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8}
+ .endm
+
+ .macro ldr1b ptr reg cond=al abort
+ ldr\cond\()b \reg, [\ptr], #1
+ .endm
+
+ .macro str1w ptr reg abort
+ W(str) \reg, [\ptr], #4
+ .endm
+
+ .macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
+ stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8}
+ .endm
+
+ .macro str1b ptr reg cond=al abort
+ str\cond\()b \reg, [\ptr], #1
+ .endm
+
+ .macro enter reg1 reg2
+ stmdb sp!, {r0, \reg1, \reg2}
+ .endm
+
+ .macro exit reg1 reg2
+ ldmfd sp!, {r0, \reg1, \reg2}
+ .endm
+
+ .text
+
+/* Prototype: void *memcpy(void *dest, const void *src, size_t n); */
+
+.globl memcpy
+memcpy:
+
+ cmp r0, r1
+ moveq pc, lr
+
+ enter r4, lr
+
+ subs r2, r2, #4
+ blt 8f
+ ands ip, r0, #3
+ PLD( pld [r1, #0] )
+ bne 9f
+ ands ip, r1, #3
+ bne 10f
+
+1: subs r2, r2, #(28)
+ stmfd sp!, {r5 - r8}
+ blt 5f
+
+ CALGN( ands ip, r0, #31 )
+ CALGN( rsb r3, ip, #32 )
+ CALGN( sbcnes r4, r3, r2 ) @ C is always set here
+ CALGN( bcs 2f )
+ CALGN( adr r4, 6f )
+ CALGN( subs r2, r2, r3 ) @ C gets set
+ CALGN( add pc, r4, ip )
+
+ PLD( pld [r1, #0] )
+2: PLD( subs r2, r2, #96 )
+ PLD( pld [r1, #28] )
+ PLD( blt 4f )
+ PLD( pld [r1, #60] )
+ PLD( pld [r1, #92] )
+
+3: PLD( pld [r1, #124] )
+4: ldr8w r1, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f
+ subs r2, r2, #32
+ str8w r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f
+ bge 3b
+ PLD( cmn r2, #96 )
+ PLD( bge 4b )
+
+5: ands ip, r2, #28
+ rsb ip, ip, #32
+#if LDR1W_SHIFT > 0
+ lsl ip, ip, #LDR1W_SHIFT
+#endif
+ addne pc, pc, ip @ C is always clear here
+ b 7f
+6:
+ .rept (1 << LDR1W_SHIFT)
+ W(nop)
+ .endr
+ ldr1w r1, r3, abort=20f
+ ldr1w r1, r4, abort=20f
+ ldr1w r1, r5, abort=20f
+ ldr1w r1, r6, abort=20f
+ ldr1w r1, r7, abort=20f
+ ldr1w r1, r8, abort=20f
+ ldr1w r1, lr, abort=20f
+
+#if LDR1W_SHIFT < STR1W_SHIFT
+ lsl ip, ip, #STR1W_SHIFT - LDR1W_SHIFT
+#elif LDR1W_SHIFT > STR1W_SHIFT
+ lsr ip, ip, #LDR1W_SHIFT - STR1W_SHIFT
+#endif
+ add pc, pc, ip
+ nop
+ .rept (1 << STR1W_SHIFT)
+ W(nop)
+ .endr
+ str1w r0, r3, abort=20f
+ str1w r0, r4, abort=20f
+ str1w r0, r5, abort=20f
+ str1w r0, r6, abort=20f
+ str1w r0, r7, abort=20f
+ str1w r0, r8, abort=20f
+ str1w r0, lr, abort=20f
+
+ CALGN( bcs 2b )
+
+7: ldmfd sp!, {r5 - r8}
+
+8: movs r2, r2, lsl #31
+ ldr1b r1, r3, ne, abort=21f
+ ldr1b r1, r4, cs, abort=21f
+ ldr1b r1, ip, cs, abort=21f
+ str1b r0, r3, ne, abort=21f
+ str1b r0, r4, cs, abort=21f
+ str1b r0, ip, cs, abort=21f
+
+ exit r4, pc
+
+9: rsb ip, ip, #4
+ cmp ip, #2
+ ldr1b r1, r3, gt, abort=21f
+ ldr1b r1, r4, ge, abort=21f
+ ldr1b r1, lr, abort=21f
+ str1b r0, r3, gt, abort=21f
+ str1b r0, r4, ge, abort=21f
+ subs r2, r2, ip
+ str1b r0, lr, abort=21f
+ blt 8b
+ ands ip, r1, #3
+ beq 1b
+
+10: bic r1, r1, #3
+ cmp ip, #2
+ ldr1w r1, lr, abort=21f
+ beq 17f
+ bgt 18f
+
+
+ .macro forward_copy_shift pull push
+
+ subs r2, r2, #28
+ blt 14f
+
+ CALGN( ands ip, r0, #31 )
+ CALGN( rsb ip, ip, #32 )
+ CALGN( sbcnes r4, ip, r2 ) @ C is always set here
+ CALGN( subcc r2, r2, ip )
+ CALGN( bcc 15f )
+
+11: stmfd sp!, {r5 - r9}
+
+ PLD( pld [r1, #0] )
+ PLD( subs r2, r2, #96 )
+ PLD( pld [r1, #28] )
+ PLD( blt 13f )
+ PLD( pld [r1, #60] )
+ PLD( pld [r1, #92] )
+
+12: PLD( pld [r1, #124] )
+13: ldr4w r1, r4, r5, r6, r7, abort=19f
+ mov r3, lr, pull #\pull
+ subs r2, r2, #32
+ ldr4w r1, r8, r9, ip, lr, abort=19f
+ orr r3, r3, r4, push #\push
+ mov r4, r4, pull #\pull
+ orr r4, r4, r5, push #\push
+ mov r5, r5, pull #\pull
+ orr r5, r5, r6, push #\push
+ mov r6, r6, pull #\pull
+ orr r6, r6, r7, push #\push
+ mov r7, r7, pull #\pull
+ orr r7, r7, r8, push #\push
+ mov r8, r8, pull #\pull
+ orr r8, r8, r9, push #\push
+ mov r9, r9, pull #\pull
+ orr r9, r9, ip, push #\push
+ mov ip, ip, pull #\pull
+ orr ip, ip, lr, push #\push
+ str8w r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f
+ bge 12b
+ PLD( cmn r2, #96 )
+ PLD( bge 13b )
+
+ ldmfd sp!, {r5 - r9}
+
+14: ands ip, r2, #28
+ beq 16f
+
+15: mov r3, lr, pull #\pull
+ ldr1w r1, lr, abort=21f
+ subs ip, ip, #4
+ orr r3, r3, lr, push #\push
+ str1w r0, r3, abort=21f
+ bgt 15b
+ CALGN( cmp r2, #0 )
+ CALGN( bge 11b )
+
+16: sub r1, r1, #(\push / 8)
+ b 8b
+
+ .endm
+
+
+ forward_copy_shift pull=8 push=24
+
+17: forward_copy_shift pull=16 push=16
+
+18: forward_copy_shift pull=24 push=8
diff --git a/src/arch/armv7/lib/memset.S b/src/arch/armv7/lib/memset.S
new file mode 100644
index 0000000000..0cdf89535a
--- /dev/null
+++ b/src/arch/armv7/lib/memset.S
@@ -0,0 +1,126 @@
+/*
+ * linux/arch/arm/lib/memset.S
+ *
+ * Copyright (C) 1995-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ASM optimised string functions
+ */
+#include <asm/assembler.h>
+
+ .text
+ .align 5
+ .word 0
+
+1: subs r2, r2, #4 @ 1 do we have enough
+ blt 5f @ 1 bytes to align with?
+ cmp r3, #2 @ 1
+ strltb r1, [r0], #1 @ 1
+ strleb r1, [r0], #1 @ 1
+ strb r1, [r0], #1 @ 1
+ add r2, r2, r3 @ 1 (r2 = r2 - (4 - r3))
+/*
+ * The pointer is now aligned and the length is adjusted. Try doing the
+ * memset again.
+ */
+
+.globl memset
+memset:
+ ands r3, r0, #3 @ 1 unaligned?
+ bne 1b @ 1
+/*
+ * we know that the pointer in r0 is aligned to a word boundary.
+ */
+ orr r1, r1, r1, lsl #8
+ orr r1, r1, r1, lsl #16
+ mov r3, r1
+ cmp r2, #16
+ blt 4f
+
+#if ! CALGN(1)+0
+
+/*
+ * We need an extra register for this loop - save the return address and
+ * use the LR
+ */
+ str lr, [sp, #-4]!
+ mov ip, r1
+ mov lr, r1
+
+2: subs r2, r2, #64
+ stmgeia r0!, {r1, r3, ip, lr} @ 64 bytes at a time.
+ stmgeia r0!, {r1, r3, ip, lr}
+ stmgeia r0!, {r1, r3, ip, lr}
+ stmgeia r0!, {r1, r3, ip, lr}
+ bgt 2b
+ ldmeqfd sp!, {pc} @ Now <64 bytes to go.
+/*
+ * No need to correct the count; we're only testing bits from now on
+ */
+ tst r2, #32
+ stmneia r0!, {r1, r3, ip, lr}
+ stmneia r0!, {r1, r3, ip, lr}
+ tst r2, #16
+ stmneia r0!, {r1, r3, ip, lr}
+ ldr lr, [sp], #4
+
+#else
+
+/*
+ * This version aligns the destination pointer in order to write
+ * whole cache lines at once.
+ */
+
+ stmfd sp!, {r4-r7, lr}
+ mov r4, r1
+ mov r5, r1
+ mov r6, r1
+ mov r7, r1
+ mov ip, r1
+ mov lr, r1
+
+ cmp r2, #96
+ tstgt r0, #31
+ ble 3f
+
+ and ip, r0, #31
+ rsb ip, ip, #32
+ sub r2, r2, ip
+ movs ip, ip, lsl #(32 - 4)
+ stmcsia r0!, {r4, r5, r6, r7}
+ stmmiia r0!, {r4, r5}
+ tst ip, #(1 << 30)
+ mov ip, r1
+ strne r1, [r0], #4
+
+3: subs r2, r2, #64
+ stmgeia r0!, {r1, r3-r7, ip, lr}
+ stmgeia r0!, {r1, r3-r7, ip, lr}
+ bgt 3b
+ ldmeqfd sp!, {r4-r7, pc}
+
+ tst r2, #32
+ stmneia r0!, {r1, r3-r7, ip, lr}
+ tst r2, #16
+ stmneia r0!, {r4-r7}
+ ldmfd sp!, {r4-r7, lr}
+
+#endif
+
+4: tst r2, #8
+ stmneia r0!, {r1, r3}
+ tst r2, #4
+ strne r1, [r0], #4
+/*
+ * When we get here, we've got less than 4 bytes to zero. We
+ * may have an unaligned pointer as well.
+ */
+5: tst r2, #2
+ strneb r1, [r0], #1
+ strneb r1, [r0], #1
+ tst r2, #1
+ strneb r1, [r0], #1
+ mov pc, lr
diff --git a/src/arch/armv7/lib/reset.c b/src/arch/armv7/lib/reset.c
new file mode 100644
index 0000000000..08e6acb261
--- /dev/null
+++ b/src/arch/armv7/lib/reset.c
@@ -0,0 +1,53 @@
+/*
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Alex Zuepke <azu@sysgo.de>
+ *
+ * (C) Copyright 2002
+ * Gary Jennejohn, DENX Software Engineering, <garyj@denx.de>
+ *
+ * (C) Copyright 2004
+ * DAVE Srl
+ * http://www.dave-tech.it
+ * http://www.wawnet.biz
+ * mailto:info@wawnet.biz
+ *
+ * (C) Copyright 2004 Texas Insturments
+ *
+ * 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
+ */
+
+#include <common.h>
+
+int do_reset(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ puts ("resetting ...\n");
+
+ udelay (50000); /* wait 50 ms */
+
+ disable_interrupts();
+ reset_cpu(0);
+
+ /*NOTREACHED*/
+ return 0;
+}
diff --git a/src/arch/armv7/lib/romstage_console.c b/src/arch/armv7/lib/romstage_console.c
new file mode 100644
index 0000000000..ead26f3521
--- /dev/null
+++ b/src/arch/armv7/lib/romstage_console.c
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the coreboot 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; version 2 of
+ * the License.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <console/console.h>
+#include <console/vtxprintf.h>
+#if CONFIG_SERIAL_CONSOLE
+#include <uart.h>
+#endif
+#if CONFIG_USBDEBUG
+#include <usbdebug.h>
+#endif
+
+/* FIXME: need to make console driver more generic */
+void console_tx_byte(unsigned char byte)
+{
+ if (byte == '\n')
+ console_tx_byte('\r');
+
+#if CONFIG_SERIAL_CONSOLE
+ uart_tx_byte(byte);
+#endif
+#if CONFIG_USBDEBUG
+ usbdebug_tx_byte(0, byte);
+#endif
+#if CONFIG_CONSOLE_CBMEM
+ cbmemc_tx_byte(byte);
+#endif
+}
+
+/* FIXME(dhendrix): add this back in */
+#if 0
+static void console_tx_flush(void)
+{
+#if CONFIG_CONSOLE_SERIAL
+ uart_tx_flush(CONFIG_CONSOLE_SERIAL_UART_ADDRESS);
+#endif
+#if CONFIG_USBDEBUG
+ usbdebug_tx_flush(0);
+#endif
+}
+#endif
+
+int do_printk(int msg_level, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ if (msg_level > console_loglevel) {
+ return 0;
+ }
+
+ va_start(args, fmt);
+ i = vtxprintf(console_tx_byte, fmt, args);
+ va_end(args);
+
+// console_tx_flush();
+
+ return i;
+}
diff --git a/src/arch/armv7/lib/syslib.c b/src/arch/armv7/lib/syslib.c
new file mode 100644
index 0000000000..6f38bf9679
--- /dev/null
+++ b/src/arch/armv7/lib/syslib.c
@@ -0,0 +1,70 @@
+/*
+ * (C) Copyright 2008
+ * Texas Instruments, <www.ti.com>
+ *
+ * Richard Woodruff <r-woodruff2@ti.com>
+ * Syed Mohammed Khasim <khasim@ti.com>
+ *
+ * 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
+ */
+
+//#include <common.h>
+#include <arch/io.h>
+#include <system.h> /* FIXME: dumping ground for prototypes */
+
+/************************************************************
+ * sdelay() - simple spin loop. Will be constant time as
+ * its generally used in bypass conditions only. This
+ * is necessary until timers are accessible.
+ *
+ * not inline to increase chances its in cache when called
+ *************************************************************/
+void sdelay(unsigned long loops)
+{
+ __asm__ volatile ("1:\n" "subs %0, %1, #1\n"
+ "bne 1b":"=r" (loops):"0"(loops));
+}
+
+/*****************************************************************
+ * sr32 - clear & set a value in a bit range for a 32 bit address
+ *****************************************************************/
+void sr32(void *addr, u32 start_bit, u32 num_bits, u32 value)
+{
+ u32 tmp, msk = 0;
+ msk = 1 << num_bits;
+ --msk;
+ tmp = readl((u32)addr) & ~(msk << start_bit);
+ tmp |= value << start_bit;
+ writel(tmp, (u32)addr);
+}
+
+/*********************************************************************
+ * wait_on_value() - common routine to allow waiting for changes in
+ * volatile regs.
+ *********************************************************************/
+u32 wait_on_value(u32 read_bit_mask, u32 match_value, void *read_addr,
+ u32 bound)
+{
+ u32 i = 0, val;
+ do {
+ ++i;
+ val = readl((u32)read_addr) & read_bit_mask;
+ if (val == match_value)
+ return 1;
+ if (i == bound)
+ return 0;
+ } while (1);
+}