1 /*
2  * Copyright (C) 2012-2014 Panasonic Corporation
3  * Copyright (C) 2015-2016 Socionext Inc.
4  *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5  *
6  * SPDX-License-Identifier:	GPL-2.0+
7  */
8 
9 #include <common.h>
10 #include <linux/io.h>
11 #include <asm/armv7.h>
12 
13 #include "ssc-regs.h"
14 
15 #ifdef CONFIG_UNIPHIER_L2CACHE_ON
16 static void uniphier_cache_sync(void)
17 {
18 	/* drain internal buffers */
19 	writel(UNIPHIER_SSCOPE_CM_SYNC, UNIPHIER_SSCOPE);
20 	/* need a read back to confirm */
21 	readl(UNIPHIER_SSCOPE);
22 }
23 
24 static void uniphier_cache_maint_all(u32 operation)
25 {
26 	/* clear the complete notification flag */
27 	writel(UNIPHIER_SSCOLPQS_EF, UNIPHIER_SSCOLPQS);
28 
29 	/* try until the command is successfully set */
30 	do {
31 		writel(UNIPHIER_SSCOQM_S_ALL | UNIPHIER_SSCOQM_CE | operation,
32 		       UNIPHIER_SSCOQM);
33 	} while (readl(UNIPHIER_SSCOPPQSEF) &
34 		 (UNIPHIER_SSCOPPQSEF_FE | UNIPHIER_SSCOPPQSEF_OE));
35 
36 	/* wait until the operation is completed */
37 	while (readl(UNIPHIER_SSCOLPQS) != UNIPHIER_SSCOLPQS_EF)
38 		;
39 
40 	uniphier_cache_sync();
41 }
42 
43 void v7_outer_cache_flush_all(void)
44 {
45 	uniphier_cache_maint_all(UNIPHIER_SSCOQM_CM_FLUSH);
46 }
47 
48 void v7_outer_cache_inval_all(void)
49 {
50 	uniphier_cache_maint_all(UNIPHIER_SSCOQM_CM_INV);
51 }
52 
53 static void __uniphier_cache_maint_range(u32 start, u32 size, u32 operation)
54 {
55 	/* clear the complete notification flag */
56 	writel(UNIPHIER_SSCOLPQS_EF, UNIPHIER_SSCOLPQS);
57 
58 	/* try until the command is successfully set */
59 	do {
60 		writel(UNIPHIER_SSCOQM_S_RANGE | UNIPHIER_SSCOQM_CE | operation,
61 		       UNIPHIER_SSCOQM);
62 		writel(start, UNIPHIER_SSCOQAD);
63 		writel(size, UNIPHIER_SSCOQSZ);
64 
65 	} while (readl(UNIPHIER_SSCOPPQSEF) &
66 		 (UNIPHIER_SSCOPPQSEF_FE | UNIPHIER_SSCOPPQSEF_OE));
67 
68 	/* wait until the operation is completed */
69 	while (readl(UNIPHIER_SSCOLPQS) != UNIPHIER_SSCOLPQS_EF)
70 		;
71 }
72 
73 static void uniphier_cache_maint_range(u32 start, u32 end, u32 operation)
74 {
75 	u32 size;
76 
77 	/*
78 	 * If start address is not aligned to cache-line,
79 	 * do cache operation for the first cache-line
80 	 */
81 	start = start & ~(UNIPHIER_SSC_LINE_SIZE - 1);
82 
83 	size = end - start;
84 
85 	if (unlikely(size >= (u32)(-UNIPHIER_SSC_LINE_SIZE))) {
86 		/* this means cache operation for all range */
87 		uniphier_cache_maint_all(operation);
88 		return;
89 	}
90 
91 	/*
92 	 * If end address is not aligned to cache-line,
93 	 * do cache operation for the last cache-line
94 	 */
95 	size = ALIGN(size, UNIPHIER_SSC_LINE_SIZE);
96 
97 	while (size) {
98 		u32 chunk_size = size > UNIPHIER_SSC_RANGE_OP_MAX_SIZE ?
99 					UNIPHIER_SSC_RANGE_OP_MAX_SIZE : size;
100 		__uniphier_cache_maint_range(start, chunk_size, operation);
101 
102 		start += chunk_size;
103 		size -= chunk_size;
104 	}
105 
106 	uniphier_cache_sync();
107 }
108 
109 void v7_outer_cache_flush_range(u32 start, u32 end)
110 {
111 	uniphier_cache_maint_range(start, end, UNIPHIER_SSCOQM_CM_FLUSH);
112 }
113 
114 void v7_outer_cache_inval_range(u32 start, u32 end)
115 {
116 	if (start & (UNIPHIER_SSC_LINE_SIZE - 1)) {
117 		start &= ~(UNIPHIER_SSC_LINE_SIZE - 1);
118 		__uniphier_cache_maint_range(start, UNIPHIER_SSC_LINE_SIZE,
119 					     UNIPHIER_SSCOQM_CM_FLUSH);
120 		start += UNIPHIER_SSC_LINE_SIZE;
121 	}
122 
123 	if (start >= end) {
124 		uniphier_cache_sync();
125 		return;
126 	}
127 
128 	if (end & (UNIPHIER_SSC_LINE_SIZE - 1)) {
129 		end &= ~(UNIPHIER_SSC_LINE_SIZE - 1);
130 		__uniphier_cache_maint_range(end, UNIPHIER_SSC_LINE_SIZE,
131 					     UNIPHIER_SSCOQM_CM_FLUSH);
132 	}
133 
134 	if (start >= end) {
135 		uniphier_cache_sync();
136 		return;
137 	}
138 
139 	uniphier_cache_maint_range(start, end, UNIPHIER_SSCOQM_CM_INV);
140 }
141 
142 void v7_outer_cache_enable(void)
143 {
144 	u32 tmp;
145 
146 	writel(U32_MAX, UNIPHIER_SSCLPDAWCR);	/* activate all ways */
147 	tmp = readl(UNIPHIER_SSCC);
148 	tmp |= UNIPHIER_SSCC_ON;
149 	writel(tmp, UNIPHIER_SSCC);
150 }
151 #endif
152 
153 void v7_outer_cache_disable(void)
154 {
155 	u32 tmp;
156 
157 	tmp = readl(UNIPHIER_SSCC);
158 	tmp &= ~UNIPHIER_SSCC_ON;
159 	writel(tmp, UNIPHIER_SSCC);
160 }
161 
162 void enable_caches(void)
163 {
164 	dcache_enable();
165 }
166