summaryrefslogtreecommitdiffstats
path: root/arch/arm/cpu/cpu.c
blob: 154f8cc46d3d2819b88418ce744d15b776cd2457 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * cpu.c - A few helper functions for ARM
 *
 * Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
 *
 * 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 version 2
 * as published by the Free Software Foundation.
 *
 * 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
 */

/**
 * @file
 * @brief A few helper functions for ARM
 */

#include <common.h>
#include <command.h>
#include <asm/mmu.h>

/**
 * Read special processor register
 * @return co-processor 15, register #1 (control register)
 */
static unsigned long read_p15_c1 (void)
{
	unsigned long value;

	__asm__ __volatile__(
		"mrc	p15, 0, %0, c1, c0, 0   @ read control reg\n"
		: "=r" (value)
		:
		: "memory");

#ifdef MMU_DEBUG
	printf ("p15/c1 is = %08lx\n", value);
#endif
	return value;
}

/**
 *
 * Write special processor register
 * @param[in] value to write
 * @return to co-processor 15, register #1 (control register)
 */
static void write_p15_c1 (unsigned long value)
{
#ifdef MMU_DEBUG
	printf ("write %08lx to p15/c1\n", value);
#endif
	__asm__ __volatile__(
		"mcr	p15, 0, %0, c1, c0, 0   @ write it back\n"
		:
		: "r" (value)
		: "memory");

	read_p15_c1 ();
}

/**
 * Wait for co prozessor (waste time)
 * Co processor seems to need some delay between accesses
 */
static void cp_delay (void)
{
	volatile int i;

	for (i = 0; i < 100; i++)	/* FIXME does it work as expected?? */
		;
}

/** mmu off/on */
#define C1_MMU		(1<<0)
/** alignment faults off/on */
#define C1_ALIGN	(1<<1)
/** dcache off/on */
#define C1_DC		(1<<2)
/** big endian off/on */
#define C1_BIG_ENDIAN	(1<<7)
/** system protection */
#define C1_SYS_PROT	(1<<8)
/** ROM protection */
#define C1_ROM_PROT	(1<<9)
/** icache off/on */
#define C1_IC		(1<<12)
/** location of vectors: low/high addresses */
#define C1_HIGH_VECTORS (1<<13)

/**
 * Enable processor's instruction cache
 */
void icache_enable (void)
{
	ulong reg;

	reg = read_p15_c1 ();		/* get control reg. */
	cp_delay ();
	write_p15_c1 (reg | C1_IC);
}

/**
 * Disable processor's instruction cache
 */
void icache_disable (void)
{
	ulong reg;

	reg = read_p15_c1 ();
	cp_delay ();
	write_p15_c1 (reg & ~C1_IC);
}

/**
 * Detect processor's current instruction cache status
 * @return 0=disabled, 1=enabled
 */
int icache_status (void)
{
	return (read_p15_c1 () & C1_IC) != 0;
}

/**
 * Prepare a "clean" CPU for Linux to run
 * @return 0 (always)
 *
 * This function is called by the generic U-Boot part just before we call
 * Linux. It prepares the processor for Linux.
 */
int cleanup_before_linux (void)
{
	int i;

	shutdown_uboot();

#ifdef CONFIG_MMU
	mmu_disable();
#endif

	/* flush I/D-cache */
	i = 0;
	asm ("mcr p15, 0, %0, c7, c7, 0": :"r" (i));
	return 0;
}
/**
 * @page arm_boot_preparation Linux Preparation on ARM
 *
 * For ARM we never enable data cache so we do not need to disable it again.
 * Linux can be called with instruction cache enabled. As this is the
 * default setting we are running in U-Boot, there's no special preparation
 * required.
 */

static int do_icache(cmd_tbl_t *cmdtp, int argc, char *argv[])
{
	if (argc == 1) {
		printf("icache is %sabled\n", icache_status() ? "en" : "dis");
		return 0;
	}

	if (simple_strtoul(argv[1], NULL, 0) > 0)
		icache_enable();
	else
		icache_disable();

	return 0;
}

static const __maybe_unused char cmd_icache_help[] =
"Usage: icache [0|1]\n";

U_BOOT_CMD_START(icache)
	.cmd		= do_icache,
	.usage		= "show/change icache status",
	U_BOOT_CMD_HELP(cmd_icache_help)
U_BOOT_CMD_END