• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/mips/mm/
1/*
2 * sc-rm7k.c: RM7000 cache management functions.
3 *
4 * Copyright (C) 1997, 2001, 2003, 2004 Ralf Baechle (ralf@linux-mips.org)
5 */
6
7#undef DEBUG
8
9#include <linux/init.h>
10#include <linux/kernel.h>
11#include <linux/mm.h>
12#include <linux/bitops.h>
13
14#include <asm/addrspace.h>
15#include <asm/bcache.h>
16#include <asm/cacheops.h>
17#include <asm/mipsregs.h>
18#include <asm/processor.h>
19#include <asm/sections.h>
20#include <asm/cacheflush.h> /* for run_uncached() */
21
22/* Primary cache parameters. */
23#define sc_lsize	32
24#define tc_pagesize	(32*128)
25
26/* Secondary cache parameters. */
27#define scache_size	(256*1024)	/* Fixed to 256KiB on RM7000 */
28
29/* Tertiary cache parameters */
30#define tc_lsize	32
31
32extern unsigned long icache_way_size, dcache_way_size;
33static unsigned long tcache_size;
34
35#include <asm/r4kcache.h>
36
37static int rm7k_tcache_init;
38
39static void rm7k_sc_wback_inv(unsigned long addr, unsigned long size)
40{
41	unsigned long end, a;
42
43	pr_debug("rm7k_sc_wback_inv[%08lx,%08lx]", addr, size);
44
45	/* Catch bad driver code */
46	BUG_ON(size == 0);
47
48	blast_scache_range(addr, addr + size);
49
50	if (!rm7k_tcache_init)
51		return;
52
53	a = addr & ~(tc_pagesize - 1);
54	end = (addr + size - 1) & ~(tc_pagesize - 1);
55	while(1) {
56		invalidate_tcache_page(a);	/* Page_Invalidate_T */
57		if (a == end)
58			break;
59		a += tc_pagesize;
60	}
61}
62
63static void rm7k_sc_inv(unsigned long addr, unsigned long size)
64{
65	unsigned long end, a;
66
67	pr_debug("rm7k_sc_inv[%08lx,%08lx]", addr, size);
68
69	/* Catch bad driver code */
70	BUG_ON(size == 0);
71
72	blast_inv_scache_range(addr, addr + size);
73
74	if (!rm7k_tcache_init)
75		return;
76
77	a = addr & ~(tc_pagesize - 1);
78	end = (addr + size - 1) & ~(tc_pagesize - 1);
79	while(1) {
80		invalidate_tcache_page(a);	/* Page_Invalidate_T */
81		if (a == end)
82			break;
83		a += tc_pagesize;
84	}
85}
86
87static void blast_rm7k_tcache(void)
88{
89	unsigned long start = CKSEG0ADDR(0);
90	unsigned long end = start + tcache_size;
91
92	write_c0_taglo(0);
93
94	while (start < end) {
95		cache_op(Page_Invalidate_T, start);
96		start += tc_pagesize;
97	}
98}
99
100/*
101 * This function is executed in uncached address space.
102 */
103static __cpuinit void __rm7k_tc_enable(void)
104{
105	int i;
106
107	set_c0_config(RM7K_CONF_TE);
108
109	write_c0_taglo(0);
110	write_c0_taghi(0);
111
112	for (i = 0; i < tcache_size; i += tc_lsize)
113		cache_op(Index_Store_Tag_T, CKSEG0ADDR(i));
114}
115
116static __cpuinit void rm7k_tc_enable(void)
117{
118	if (read_c0_config() & RM7K_CONF_TE)
119		return;
120
121	BUG_ON(tcache_size == 0);
122
123	run_uncached(__rm7k_tc_enable);
124}
125
126/*
127 * This function is executed in uncached address space.
128 */
129static __cpuinit void __rm7k_sc_enable(void)
130{
131	int i;
132
133	set_c0_config(RM7K_CONF_SE);
134
135	write_c0_taglo(0);
136	write_c0_taghi(0);
137
138	for (i = 0; i < scache_size; i += sc_lsize)
139		cache_op(Index_Store_Tag_SD, CKSEG0ADDR(i));
140}
141
142static __cpuinit void rm7k_sc_enable(void)
143{
144	if (read_c0_config() & RM7K_CONF_SE)
145		return;
146
147	pr_info("Enabling secondary cache...\n");
148	run_uncached(__rm7k_sc_enable);
149
150	if (rm7k_tcache_init)
151		rm7k_tc_enable();
152}
153
154static void rm7k_tc_disable(void)
155{
156	unsigned long flags;
157
158	local_irq_save(flags);
159	blast_rm7k_tcache();
160	clear_c0_config(RM7K_CONF_TE);
161	local_irq_save(flags);
162}
163
164static void rm7k_sc_disable(void)
165{
166	clear_c0_config(RM7K_CONF_SE);
167
168	if (rm7k_tcache_init)
169		rm7k_tc_disable();
170}
171
172static struct bcache_ops rm7k_sc_ops = {
173	.bc_enable = rm7k_sc_enable,
174	.bc_disable = rm7k_sc_disable,
175	.bc_wback_inv = rm7k_sc_wback_inv,
176	.bc_inv = rm7k_sc_inv
177};
178
179/*
180 * This is a probing function like the one found in c-r4k.c, we look for the
181 * wrap around point with different addresses.
182 */
183static __cpuinit void __probe_tcache(void)
184{
185	unsigned long flags, addr, begin, end, pow2;
186
187	begin = (unsigned long) &_stext;
188	begin  &= ~((8 * 1024 * 1024) - 1);
189	end = begin + (8 * 1024 * 1024);
190
191	local_irq_save(flags);
192
193	set_c0_config(RM7K_CONF_TE);
194
195	/* Fill size-multiple lines with a valid tag */
196	pow2 = (256 * 1024);
197	for (addr = begin; addr <= end; addr = (begin + pow2)) {
198		unsigned long *p = (unsigned long *) addr;
199		__asm__ __volatile__("nop" : : "r" (*p));
200		pow2 <<= 1;
201	}
202
203	/* Load first line with a 0 tag, to check after */
204	write_c0_taglo(0);
205	write_c0_taghi(0);
206	cache_op(Index_Store_Tag_T, begin);
207
208	/* Look for the wrap-around */
209	pow2 = (512 * 1024);
210	for (addr = begin + (512 * 1024); addr <= end; addr = begin + pow2) {
211		cache_op(Index_Load_Tag_T, addr);
212		if (!read_c0_taglo())
213			break;
214		pow2 <<= 1;
215	}
216
217	addr -= begin;
218	tcache_size = addr;
219
220	clear_c0_config(RM7K_CONF_TE);
221
222	local_irq_restore(flags);
223}
224
225void __cpuinit rm7k_sc_init(void)
226{
227	struct cpuinfo_mips *c = &current_cpu_data;
228	unsigned int config = read_c0_config();
229
230	if ((config & RM7K_CONF_SC))
231		return;
232
233	c->scache.linesz = sc_lsize;
234	c->scache.ways = 4;
235	c->scache.waybit= __ffs(scache_size / c->scache.ways);
236	c->scache.waysize = scache_size / c->scache.ways;
237	c->scache.sets = scache_size / (c->scache.linesz * c->scache.ways);
238	printk(KERN_INFO "Secondary cache size %dK, linesize %d bytes.\n",
239	       (scache_size >> 10), sc_lsize);
240
241	if (!(config & RM7K_CONF_SE))
242		rm7k_sc_enable();
243
244	bcops = &rm7k_sc_ops;
245
246	/*
247	 * While we're at it let's deal with the tertiary cache.
248	 */
249
250	rm7k_tcache_init = 0;
251	tcache_size = 0;
252
253	if (config & RM7K_CONF_TC)
254		return;
255
256	/*
257	 * No efficient way to ask the hardware for the size of the tcache,
258	 * so must probe for it.
259	 */
260	run_uncached(__probe_tcache);
261	rm7k_tc_enable();
262	rm7k_tcache_init = 1;
263	c->tcache.linesz = tc_lsize;
264	c->tcache.ways = 1;
265	pr_info("Tertiary cache size %ldK.\n", (tcache_size >> 10));
266}
267