1/* MN10300 CPU cache invalidation routines, using automatic purge registers
2 *
3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public Licence
8 * as published by the Free Software Foundation; either version
9 * 2 of the Licence, or (at your option) any later version.
10 */
11#include <linux/sys.h>
12#include <linux/linkage.h>
13#include <asm/smp.h>
14#include <asm/page.h>
15#include <asm/cache.h>
16#include <asm/irqflags.h>
17#include <asm/cacheflush.h>
18#include "cache.inc"
19
20#define mn10300_local_dcache_inv_range_intr_interval \
21	+((1 << MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL) - 1)
22
23#if mn10300_local_dcache_inv_range_intr_interval > 0xff
24#error MN10300_DCACHE_INV_RANGE_INTR_LOG2_INTERVAL must be 8 or less
25#endif
26
27	.am33_2
28
29#ifndef CONFIG_SMP
30	.globl	mn10300_icache_inv
31	.globl	mn10300_icache_inv_page
32	.globl	mn10300_icache_inv_range
33	.globl	mn10300_icache_inv_range2
34	.globl	mn10300_dcache_inv
35	.globl	mn10300_dcache_inv_page
36	.globl	mn10300_dcache_inv_range
37	.globl	mn10300_dcache_inv_range2
38
39mn10300_icache_inv		= mn10300_local_icache_inv
40mn10300_icache_inv_page		= mn10300_local_icache_inv_page
41mn10300_icache_inv_range	= mn10300_local_icache_inv_range
42mn10300_icache_inv_range2	= mn10300_local_icache_inv_range2
43mn10300_dcache_inv		= mn10300_local_dcache_inv
44mn10300_dcache_inv_page		= mn10300_local_dcache_inv_page
45mn10300_dcache_inv_range	= mn10300_local_dcache_inv_range
46mn10300_dcache_inv_range2	= mn10300_local_dcache_inv_range2
47
48#endif /* !CONFIG_SMP */
49
50###############################################################################
51#
52# void mn10300_local_icache_inv(void)
53# Invalidate the entire icache
54#
55###############################################################################
56	ALIGN
57	.globl	mn10300_local_icache_inv
58        .type	mn10300_local_icache_inv,@function
59mn10300_local_icache_inv:
60	mov	CHCTR,a0
61
62	movhu	(a0),d0
63	btst	CHCTR_ICEN,d0
64	beq	mn10300_local_icache_inv_end
65
66	invalidate_icache 1
67
68mn10300_local_icache_inv_end:
69	ret	[],0
70	.size	mn10300_local_icache_inv,.-mn10300_local_icache_inv
71
72###############################################################################
73#
74# void mn10300_local_dcache_inv(void)
75# Invalidate the entire dcache
76#
77###############################################################################
78	ALIGN
79	.globl	mn10300_local_dcache_inv
80	.type	mn10300_local_dcache_inv,@function
81mn10300_local_dcache_inv:
82	mov	CHCTR,a0
83
84	movhu	(a0),d0
85	btst	CHCTR_DCEN,d0
86	beq	mn10300_local_dcache_inv_end
87
88	invalidate_dcache 1
89
90mn10300_local_dcache_inv_end:
91	ret	[],0
92	.size	mn10300_local_dcache_inv,.-mn10300_local_dcache_inv
93
94###############################################################################
95#
96# void mn10300_local_dcache_inv_range(unsigned long start, unsigned long end)
97# void mn10300_local_dcache_inv_range2(unsigned long start, unsigned long size)
98# void mn10300_local_dcache_inv_page(unsigned long start)
99# Invalidate a range of addresses on a page in the dcache
100#
101###############################################################################
102	ALIGN
103	.globl	mn10300_local_dcache_inv_page
104	.globl	mn10300_local_dcache_inv_range
105	.globl	mn10300_local_dcache_inv_range2
106	.type	mn10300_local_dcache_inv_page,@function
107	.type	mn10300_local_dcache_inv_range,@function
108	.type	mn10300_local_dcache_inv_range2,@function
109mn10300_local_dcache_inv_page:
110	and	~(PAGE_SIZE-1),d0
111	mov	PAGE_SIZE,d1
112mn10300_local_dcache_inv_range2:
113	add	d0,d1
114mn10300_local_dcache_inv_range:
115	# If we are in writeback mode we check the start and end alignments,
116	# and if they're not cacheline-aligned, we must flush any bits outside
117	# the range that share cachelines with stuff inside the range
118#ifdef CONFIG_MN10300_CACHE_WBACK
119	btst	~L1_CACHE_TAG_MASK,d0
120	bne	1f
121	btst	~L1_CACHE_TAG_MASK,d1
122	beq	2f
1231:
124	bra	mn10300_local_dcache_flush_inv_range
1252:
126#endif /* CONFIG_MN10300_CACHE_WBACK */
127
128	movm	[d2,d3,a2],(sp)
129
130	mov	CHCTR,a0
131	movhu	(a0),d2
132	btst	CHCTR_DCEN,d2
133	beq	mn10300_local_dcache_inv_range_end
134
135	# round the addresses out to be full cachelines, unless we're in
136	# writeback mode, in which case we would be in flush and invalidate by
137	# now
138#ifndef CONFIG_MN10300_CACHE_WBACK
139	and	L1_CACHE_TAG_MASK,d0	# round start addr down
140
141	mov	L1_CACHE_BYTES-1,d2
142	add	d2,d1
143	and	L1_CACHE_TAG_MASK,d1	# round end addr up
144#endif /* !CONFIG_MN10300_CACHE_WBACK */
145
146	sub	d0,d1,d2		# calculate the total size
147	mov	d0,a2			# A2 = start address
148	mov	d1,a1			# A1 = end address
149
150	LOCAL_CLI_SAVE(d3)
151
152	mov	DCPGCR,a0		# make sure the purger isn't busy
153	setlb
154	mov	(a0),d0
155	btst	DCPGCR_DCPGBSY,d0
156	lne
157
158	# skip initial address alignment calculation if address is zero
159	mov	d2,d1
160	cmp	0,a2
161	beq	1f
162
163dcivloop:
164	/* calculate alignsize
165	 *
166	 * alignsize = L1_CACHE_BYTES;
167	 * while (! start & alignsize) {
168	 *	alignsize <<=1;
169	 * }
170	 * d1 = alignsize;
171	 */
172	mov	L1_CACHE_BYTES,d1
173	lsr	1,d1
174	setlb
175	add	d1,d1
176	mov	d1,d0
177	and	a2,d0
178	leq
179
1801:
181	/* calculate invsize
182	 *
183	 * if (totalsize > alignsize) {
184	 *	invsize = alignsize;
185	 * } else {
186	 *	invsize = totalsize;
187	 *	tmp = 0x80000000;
188	 *	while (! invsize & tmp) {
189	 *		tmp >>= 1;
190	 *	}
191	 *	invsize = tmp;
192	 * }
193	 * d1 = invsize
194	 */
195	cmp	d2,d1
196	bns	2f
197	mov	d2,d1
198
199	mov	0x80000000,d0		# start from 31bit=1
200	setlb
201	lsr	1,d0
202	mov	d0,e0
203	and	d1,e0
204	leq
205	mov	d0,d1
206
2072:
208	/* set mask
209	 *
210	 * mask = ~(invsize-1);
211	 * DCPGMR = mask;
212	 */
213	mov	d1,d0
214	add	-1,d0
215	not	d0
216	mov	d0,(DCPGMR)
217
218	# invalidate area
219	mov	a2,d0
220	or	DCPGCR_DCI,d0
221	mov	d0,(a0)			# DCPGCR = (mask & start) | DCPGCR_DCI
222
223	setlb				# wait for the purge to complete
224	mov	(a0),d0
225	btst	DCPGCR_DCPGBSY,d0
226	lne
227
228	sub	d1,d2			# decrease size remaining
229	add	d1,a2			# increase next start address
230
231	/* check invalidating of end address
232	 *
233	 * a2 = a2 + invsize
234	 * if (a2 < end) {
235	 *     goto dcivloop;
236	 * } */
237	cmp	a1,a2
238	bns	dcivloop
239
240	LOCAL_IRQ_RESTORE(d3)
241
242mn10300_local_dcache_inv_range_end:
243	ret	[d2,d3,a2],12
244	.size	mn10300_local_dcache_inv_page,.-mn10300_local_dcache_inv_page
245	.size	mn10300_local_dcache_inv_range,.-mn10300_local_dcache_inv_range
246	.size	mn10300_local_dcache_inv_range2,.-mn10300_local_dcache_inv_range2
247
248###############################################################################
249#
250# void mn10300_local_icache_inv_page(unsigned long start)
251# void mn10300_local_icache_inv_range2(unsigned long start, unsigned long size)
252# void mn10300_local_icache_inv_range(unsigned long start, unsigned long end)
253# Invalidate a range of addresses on a page in the icache
254#
255###############################################################################
256	ALIGN
257	.globl	mn10300_local_icache_inv_page
258	.globl	mn10300_local_icache_inv_range
259	.globl	mn10300_local_icache_inv_range2
260	.type	mn10300_local_icache_inv_page,@function
261	.type	mn10300_local_icache_inv_range,@function
262	.type	mn10300_local_icache_inv_range2,@function
263mn10300_local_icache_inv_page:
264	and	~(PAGE_SIZE-1),d0
265	mov	PAGE_SIZE,d1
266mn10300_local_icache_inv_range2:
267	add	d0,d1
268mn10300_local_icache_inv_range:
269	movm	[d2,d3,a2],(sp)
270
271	mov	CHCTR,a0
272	movhu	(a0),d2
273	btst	CHCTR_ICEN,d2
274	beq	mn10300_local_icache_inv_range_reg_end
275
276	/* calculate alignsize
277	 *
278	 * alignsize = L1_CACHE_BYTES;
279	 * for (i = (end - start - 1) / L1_CACHE_BYTES ;  i > 0; i >>= 1) {
280	 *     alignsize <<= 1;
281	 * }
282	 * d2 = alignsize;
283	 */
284	mov	L1_CACHE_BYTES,d2
285	sub	d0,d1,d3
286	add	-1,d3
287	lsr	L1_CACHE_SHIFT,d3
288	beq	2f
2891:
290	add	d2,d2
291	lsr	1,d3
292	bne	1b
2932:
294
295	/* a1 = end */
296	mov	d1,a1
297
298	LOCAL_CLI_SAVE(d3)
299
300	mov	ICIVCR,a0
301	/* wait for busy bit of area invalidation */
302	setlb
303	mov	(a0),d1
304	btst	ICIVCR_ICIVBSY,d1
305	lne
306
307	/* set mask
308	 *
309	 * mask = ~(alignsize-1);
310	 * ICIVMR = mask;
311	 */
312	mov	d2,d1
313	add	-1,d1
314	not	d1
315	mov	d1,(ICIVMR)
316	/* a2 = mask & start */
317	and	d1,d0,a2
318
319icivloop:
320	/* area invalidate
321	 *
322	 * ICIVCR = (mask & start) | ICIVCR_ICI
323	 */
324	mov	a2,d0
325	or	ICIVCR_ICI,d0
326	mov	d0,(a0)
327
328	/* wait for busy bit of area invalidation */
329	setlb
330	mov	(a0),d1
331	btst	ICIVCR_ICIVBSY,d1
332	lne
333
334	/* check invalidating of end address
335	 *
336	 * a2 = a2 + alignsize
337	 * if (a2 < end) {
338	 *     goto icivloop;
339	 * } */
340	add	d2,a2
341	cmp	a1,a2
342	bns	icivloop
343
344	LOCAL_IRQ_RESTORE(d3)
345
346mn10300_local_icache_inv_range_reg_end:
347	ret	[d2,d3,a2],12
348	.size	mn10300_local_icache_inv_page,.-mn10300_local_icache_inv_page
349	.size	mn10300_local_icache_inv_range,.-mn10300_local_icache_inv_range
350	.size	mn10300_local_icache_inv_range2,.-mn10300_local_icache_inv_range2
351