cache-sh4.c 9.7 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4
/*
 * arch/sh/mm/cache-sh4.c
 *
 * Copyright (C) 1999, 2000, 2002  Niibe Yutaka
5
 * Copyright (C) 2001 - 2009  Paul Mundt
L
Linus Torvalds 已提交
6
 * Copyright (C) 2003  Richard Curnow
7
 * Copyright (c) 2007 STMicroelectronics (R&D) Ltd.
L
Linus Torvalds 已提交
8 9 10 11 12 13 14
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */
#include <linux/init.h>
#include <linux/mm.h>
P
Paul Mundt 已提交
15 16
#include <linux/io.h>
#include <linux/mutex.h>
17
#include <linux/fs.h>
18 19
#include <linux/highmem.h>
#include <asm/pgtable.h>
L
Linus Torvalds 已提交
20
#include <asm/mmu_context.h>
21
#include <asm/cache_insns.h>
L
Linus Torvalds 已提交
22 23
#include <asm/cacheflush.h>

24 25 26 27 28
/*
 * The maximum number of pages we support up to when doing ranged dcache
 * flushing. Anything exceeding this will simply flush the dcache in its
 * entirety.
 */
29
#define MAX_ICACHE_PAGES	32
30

31
static void __flush_cache_one(unsigned long addr, unsigned long phys,
32
			       unsigned long exec_offset);
33

L
Linus Torvalds 已提交
34 35 36
/*
 * Write back the range of D-cache, and purge the I-cache.
 *
37 38
 * Called from kernel/module.c:sys_init_module and routine for a.out format,
 * signal handler code and kprobes code
L
Linus Torvalds 已提交
39
 */
40
static void sh4_flush_icache_range(void *args)
L
Linus Torvalds 已提交
41
{
P
Paul Mundt 已提交
42 43
	struct flusher_data *data = args;
	unsigned long start, end;
44
	unsigned long flags, v;
L
Linus Torvalds 已提交
45 46
	int i;

P
Paul Mundt 已提交
47 48 49
	start = data->addr1;
	end = data->addr2;

50 51 52 53 54 55 56 57 58 59 60 61 62
	/* If there are too many pages then just blow away the caches */
	if (((end - start) >> PAGE_SHIFT) >= MAX_ICACHE_PAGES) {
		local_flush_cache_all(NULL);
		return;
	}

	/*
	 * Selectively flush d-cache then invalidate the i-cache.
	 * This is inefficient, so only use this for small ranges.
	 */
	start &= ~(L1_CACHE_BYTES-1);
	end += L1_CACHE_BYTES-1;
	end &= ~(L1_CACHE_BYTES-1);
63

64 65
	local_irq_save(flags);
	jump_to_uncached();
66

67 68
	for (v = start; v < end; v += L1_CACHE_BYTES) {
		unsigned long icacheaddr;
69
		int j, n;
70

71
		__ocbwb(v);
72

73 74
		icacheaddr = CACHE_IC_ADDRESS_ARRAY | (v &
				cpu_data->icache.entry_mask);
75

76
		/* Clear i-cache line valid-bit */
77
		n = boot_cpu_data.icache.n_aliases;
78
		for (i = 0; i < cpu_data->icache.ways; i++) {
79 80
			for (j = 0; j < n; j++)
				__raw_writel(0, icacheaddr + (j * PAGE_SIZE));
81 82
			icacheaddr += cpu_data->icache.way_incr;
		}
83
	}
84 85 86

	back_to_cached();
	local_irq_restore(flags);
L
Linus Torvalds 已提交
87 88
}

89
static inline void flush_cache_one(unsigned long start, unsigned long phys)
L
Linus Torvalds 已提交
90
{
91
	unsigned long flags, exec_offset = 0;
92

L
Linus Torvalds 已提交
93
	/*
M
Matt Fleming 已提交
94 95
	 * All types of SH-4 require PC to be uncached to operate on the I-cache.
	 * Some types of SH-4 require PC to be uncached to operate on the D-cache.
L
Linus Torvalds 已提交
96
	 */
97
	if ((boot_cpu_data.flags & CPU_HAS_P2_FLUSH_BUG) ||
98
	    (start < CACHE_OC_ADDRESS_ARRAY))
M
Matt Fleming 已提交
99
		exec_offset = cached_to_uncached;
100

101
	local_irq_save(flags);
102
	__flush_cache_one(start, phys, exec_offset);
103
	local_irq_restore(flags);
L
Linus Torvalds 已提交
104 105 106 107 108 109
}

/*
 * Write back & invalidate the D-cache of the page.
 * (To avoid "alias" issues)
 */
110
static void sh4_flush_dcache_page(void *arg)
L
Linus Torvalds 已提交
111
{
112
	struct page *page = arg;
113
	unsigned long addr = (unsigned long)page_address(page);
P
Paul Mundt 已提交
114
#ifndef CONFIG_SMP
115 116 117
	struct address_space *mapping = page_mapping(page);

	if (mapping && !mapping_mapped(mapping))
118
		clear_bit(PG_dcache_clean, &page->flags);
119 120
	else
#endif
121 122
		flush_cache_one(CACHE_OC_ADDRESS_ARRAY |
				(addr & shm_align_mask), page_to_phys(page));
123 124

	wmb();
L
Linus Torvalds 已提交
125 126
}

127
/* TODO: Selective icache invalidation through IC address array.. */
128
static void flush_icache_all(void)
L
Linus Torvalds 已提交
129
{
130
	unsigned long flags, ccr;
L
Linus Torvalds 已提交
131

132
	local_irq_save(flags);
133
	jump_to_uncached();
L
Linus Torvalds 已提交
134 135

	/* Flush I-cache */
136
	ccr = __raw_readl(SH_CCR);
L
Linus Torvalds 已提交
137
	ccr |= CCR_CACHE_ICI;
138
	__raw_writel(ccr, SH_CCR);
L
Linus Torvalds 已提交
139

P
Paul Mundt 已提交
140
	/*
141
	 * back_to_cached() will take care of the barrier for us, don't add
P
Paul Mundt 已提交
142 143
	 * another one!
	 */
144

145
	back_to_cached();
146
	local_irq_restore(flags);
L
Linus Torvalds 已提交
147 148
}

149
static void flush_dcache_all(void)
L
Linus Torvalds 已提交
150
{
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
	unsigned long addr, end_addr, entry_offset;

	end_addr = CACHE_OC_ADDRESS_ARRAY +
		(current_cpu_data.dcache.sets <<
		 current_cpu_data.dcache.entry_shift) *
			current_cpu_data.dcache.ways;

	entry_offset = 1 << current_cpu_data.dcache.entry_shift;

	for (addr = CACHE_OC_ADDRESS_ARRAY; addr < end_addr; ) {
		__raw_writel(0, addr); addr += entry_offset;
		__raw_writel(0, addr); addr += entry_offset;
		__raw_writel(0, addr); addr += entry_offset;
		__raw_writel(0, addr); addr += entry_offset;
		__raw_writel(0, addr); addr += entry_offset;
		__raw_writel(0, addr); addr += entry_offset;
		__raw_writel(0, addr); addr += entry_offset;
		__raw_writel(0, addr); addr += entry_offset;
	}
170 171
}

P
Paul Mundt 已提交
172
static void sh4_flush_cache_all(void *unused)
173 174
{
	flush_dcache_all();
L
Linus Torvalds 已提交
175 176 177
	flush_icache_all();
}

178 179 180 181 182 183
/*
 * Note : (RPC) since the caches are physically tagged, the only point
 * of flush_cache_mm for SH-4 is to get rid of aliases from the
 * D-cache.  The assumption elsewhere, e.g. flush_cache_range, is that
 * lines can stay resident so long as the virtual address they were
 * accessed with (hence cache set) is in accord with the physical
184
 * address (i.e. tag).  It's no different here.
185 186 187
 *
 * Caller takes mm->mmap_sem.
 */
P
Paul Mundt 已提交
188
static void sh4_flush_cache_mm(void *arg)
L
Linus Torvalds 已提交
189
{
P
Paul Mundt 已提交
190 191
	struct mm_struct *mm = arg;

192 193 194
	if (cpu_context(smp_processor_id(), mm) == NO_CONTEXT)
		return;

195
	flush_dcache_all();
L
Linus Torvalds 已提交
196 197 198 199 200 201 202 203
}

/*
 * Write back and invalidate I/D-caches for the page.
 *
 * ADDR: Virtual Address (U0 address)
 * PFN: Physical page number
 */
P
Paul Mundt 已提交
204
static void sh4_flush_cache_page(void *args)
L
Linus Torvalds 已提交
205
{
P
Paul Mundt 已提交
206 207
	struct flusher_data *data = args;
	struct vm_area_struct *vma;
208
	struct page *page;
P
Paul Mundt 已提交
209
	unsigned long address, pfn, phys;
210 211 212 213 214 215
	int map_coherent = 0;
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;
	void *vaddr;
216

P
Paul Mundt 已提交
217
	vma = data->vma;
P
Paul Mundt 已提交
218
	address = data->addr1 & PAGE_MASK;
P
Paul Mundt 已提交
219 220
	pfn = data->addr2;
	phys = pfn << PAGE_SHIFT;
221
	page = pfn_to_page(pfn);
P
Paul Mundt 已提交
222

223 224 225
	if (cpu_context(smp_processor_id(), vma->vm_mm) == NO_CONTEXT)
		return;

226 227 228 229 230 231 232 233
	pgd = pgd_offset(vma->vm_mm, address);
	pud = pud_offset(pgd, address);
	pmd = pmd_offset(pud, address);
	pte = pte_offset_kernel(pmd, address);

	/* If the page isn't present, there is nothing to do here. */
	if (!(pte_val(*pte) & _PAGE_PRESENT))
		return;
L
Linus Torvalds 已提交
234

235 236 237
	if ((vma->vm_mm == current->active_mm))
		vaddr = NULL;
	else {
238
		/*
239 240
		 * Use kmap_coherent or kmap_atomic to do flushes for
		 * another ASID than the current one.
241
		 */
242
		map_coherent = (current_cpu_data.dcache.n_aliases &&
243
			test_bit(PG_dcache_clean, &page->flags) &&
244
			page_mapcount(page));
245 246 247
		if (map_coherent)
			vaddr = kmap_coherent(page, address);
		else
248
			vaddr = kmap_atomic(page);
249 250 251 252

		address = (unsigned long)vaddr;
	}

253
	flush_cache_one(CACHE_OC_ADDRESS_ARRAY |
254 255 256 257 258 259 260 261 262
			(address & shm_align_mask), phys);

	if (vma->vm_flags & VM_EXEC)
		flush_icache_all();

	if (vaddr) {
		if (map_coherent)
			kunmap_coherent(vaddr);
		else
263
			kunmap_atomic(vaddr);
264
	}
L
Linus Torvalds 已提交
265 266 267 268 269 270 271 272 273 274 275
}

/*
 * Write back and invalidate D-caches.
 *
 * START, END: Virtual Address (U0 address)
 *
 * NOTE: We need to flush the _physical_ page entry.
 * Flushing the cache lines for U0 only isn't enough.
 * We need to flush for P1 too, which may contain aliases.
 */
P
Paul Mundt 已提交
276
static void sh4_flush_cache_range(void *args)
L
Linus Torvalds 已提交
277
{
P
Paul Mundt 已提交
278 279 280 281 282 283 284 285
	struct flusher_data *data = args;
	struct vm_area_struct *vma;
	unsigned long start, end;

	vma = data->vma;
	start = data->addr1;
	end = data->addr2;

286 287 288
	if (cpu_context(smp_processor_id(), vma->vm_mm) == NO_CONTEXT)
		return;

289 290 291 292
	/*
	 * If cache is only 4k-per-way, there are never any 'aliases'.  Since
	 * the cache is physically tagged, the data can just be left in there.
	 */
293
	if (boot_cpu_data.dcache.n_aliases == 0)
294 295
		return;

296
	flush_dcache_all();
297

298
	if (vma->vm_flags & VM_EXEC)
L
Linus Torvalds 已提交
299 300 301
		flush_icache_all();
}

302
/**
303
 * __flush_cache_one
304 305 306 307 308 309 310 311 312 313 314 315
 *
 * @addr:  address in memory mapped cache array
 * @phys:  P1 address to flush (has to match tags if addr has 'A' bit
 *         set i.e. associative write)
 * @exec_offset: set to 0x20000000 if flush has to be executed from P2
 *               region else 0x0
 *
 * The offset into the cache array implied by 'addr' selects the
 * 'colour' of the virtual address range that will be flushed.  The
 * operation (purge/write-back) is selected by the lower 2 bits of
 * 'phys'.
 */
316
static void __flush_cache_one(unsigned long addr, unsigned long phys,
317 318 319 320 321 322 323 324 325
			       unsigned long exec_offset)
{
	int way_count;
	unsigned long base_addr = addr;
	struct cache_info *dcache;
	unsigned long way_incr;
	unsigned long a, ea, p;
	unsigned long temp_pc;

326
	dcache = &boot_cpu_data.dcache;
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
	/* Write this way for better assembly. */
	way_count = dcache->ways;
	way_incr = dcache->way_incr;

	/*
	 * Apply exec_offset (i.e. branch to P2 if required.).
	 *
	 * FIXME:
	 *
	 *	If I write "=r" for the (temp_pc), it puts this in r6 hence
	 *	trashing exec_offset before it's been added on - why?  Hence
	 *	"=&r" as a 'workaround'
	 */
	asm volatile("mov.l 1f, %0\n\t"
		     "add   %1, %0\n\t"
		     "jmp   @%0\n\t"
		     "nop\n\t"
		     ".balign 4\n\t"
		     "1:  .long 2f\n\t"
		     "2:\n" : "=&r" (temp_pc) : "r" (exec_offset));

	/*
	 * We know there will be >=1 iteration, so write as do-while to avoid
	 * pointless nead-of-loop check for 0 iterations.
	 */
	do {
		ea = base_addr + PAGE_SIZE;
		a = base_addr;
		p = phys;

		do {
			*(volatile unsigned long *)a = p;
			/*
			 * Next line: intentionally not p+32, saves an add, p
			 * will do since only the cache tag bits need to
			 * match.
			 */
			*(volatile unsigned long *)(a+32) = p;
			a += 64;
			p += 64;
		} while (a < ea);

		base_addr += way_incr;
	} while (--way_count != 0);
}

373 374 375 376 377 378 379 380
extern void __weak sh4__flush_region_init(void);

/*
 * SH-4 has virtually indexed and physically tagged cache.
 */
void __init sh4_cache_init(void)
{
	printk("PVR=%08x CVR=%08x PRR=%08x\n",
381 382 383
		__raw_readl(CCN_PVR),
		__raw_readl(CCN_CVR),
		__raw_readl(CCN_PRR));
384

P
Paul Mundt 已提交
385 386 387 388 389 390 391
	local_flush_icache_range	= sh4_flush_icache_range;
	local_flush_dcache_page		= sh4_flush_dcache_page;
	local_flush_cache_all		= sh4_flush_cache_all;
	local_flush_cache_mm		= sh4_flush_cache_mm;
	local_flush_cache_dup_mm	= sh4_flush_cache_mm;
	local_flush_cache_page		= sh4_flush_cache_page;
	local_flush_cache_range		= sh4_flush_cache_range;
392 393 394

	sh4__flush_region_init();
}