1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * MIPS SPRAM support |
4 | * |
5 | * Copyright (C) 2007, 2008 MIPS Technologies, Inc. |
6 | */ |
7 | #include <linux/kernel.h> |
8 | #include <linux/ptrace.h> |
9 | #include <linux/stddef.h> |
10 | |
11 | #include <asm/fpu.h> |
12 | #include <asm/mipsregs.h> |
13 | #include <asm/r4kcache.h> |
14 | #include <asm/hazards.h> |
15 | #include <asm/spram.h> |
16 | |
17 | /* |
18 | * These definitions are correct for the 24K/34K/74K SPRAM sample |
19 | * implementation. The 4KS interpreted the tags differently... |
20 | */ |
21 | #define SPRAM_TAG0_ENABLE 0x00000080 |
22 | #define SPRAM_TAG0_PA_MASK 0xfffff000 |
23 | #define SPRAM_TAG1_SIZE_MASK 0xfffff000 |
24 | |
25 | #define SPRAM_TAG_STRIDE 8 |
26 | |
27 | #define ERRCTL_SPRAM (1 << 28) |
28 | |
29 | /* errctl access */ |
30 | #define read_c0_errctl(x) read_c0_ecc(x) |
31 | #define write_c0_errctl(x) write_c0_ecc(x) |
32 | |
33 | /* |
34 | * Different semantics to the set_c0_* function built by __BUILD_SET_C0 |
35 | */ |
36 | static unsigned int bis_c0_errctl(unsigned int set) |
37 | { |
38 | unsigned int res; |
39 | res = read_c0_errctl(); |
40 | write_c0_errctl(res | set); |
41 | return res; |
42 | } |
43 | |
44 | static void ispram_store_tag(unsigned int offset, unsigned int data) |
45 | { |
46 | unsigned int errctl; |
47 | |
48 | /* enable SPRAM tag access */ |
49 | errctl = bis_c0_errctl(ERRCTL_SPRAM); |
50 | ehb(); |
51 | |
52 | write_c0_taglo(data); |
53 | ehb(); |
54 | |
55 | cache_op(Index_Store_Tag_I, CKSEG0|offset); |
56 | ehb(); |
57 | |
58 | write_c0_errctl(errctl); |
59 | ehb(); |
60 | } |
61 | |
62 | |
63 | static unsigned int ispram_load_tag(unsigned int offset) |
64 | { |
65 | unsigned int data; |
66 | unsigned int errctl; |
67 | |
68 | /* enable SPRAM tag access */ |
69 | errctl = bis_c0_errctl(ERRCTL_SPRAM); |
70 | ehb(); |
71 | cache_op(Index_Load_Tag_I, CKSEG0 | offset); |
72 | ehb(); |
73 | data = read_c0_taglo(); |
74 | ehb(); |
75 | write_c0_errctl(errctl); |
76 | ehb(); |
77 | |
78 | return data; |
79 | } |
80 | |
81 | static void dspram_store_tag(unsigned int offset, unsigned int data) |
82 | { |
83 | unsigned int errctl; |
84 | |
85 | /* enable SPRAM tag access */ |
86 | errctl = bis_c0_errctl(ERRCTL_SPRAM); |
87 | ehb(); |
88 | write_c0_dtaglo(data); |
89 | ehb(); |
90 | cache_op(Index_Store_Tag_D, CKSEG0 | offset); |
91 | ehb(); |
92 | write_c0_errctl(errctl); |
93 | ehb(); |
94 | } |
95 | |
96 | |
97 | static unsigned int dspram_load_tag(unsigned int offset) |
98 | { |
99 | unsigned int data; |
100 | unsigned int errctl; |
101 | |
102 | errctl = bis_c0_errctl(ERRCTL_SPRAM); |
103 | ehb(); |
104 | cache_op(Index_Load_Tag_D, CKSEG0 | offset); |
105 | ehb(); |
106 | data = read_c0_dtaglo(); |
107 | ehb(); |
108 | write_c0_errctl(errctl); |
109 | ehb(); |
110 | |
111 | return data; |
112 | } |
113 | |
114 | static void probe_spram(char *type, |
115 | unsigned int base, |
116 | unsigned int (*read)(unsigned int), |
117 | void (*write)(unsigned int, unsigned int)) |
118 | { |
119 | unsigned int firstsize = 0, lastsize = 0; |
120 | unsigned int firstpa = 0, lastpa = 0, pa = 0; |
121 | unsigned int offset = 0; |
122 | unsigned int size, tag0, tag1; |
123 | unsigned int enabled; |
124 | int i; |
125 | |
126 | /* |
127 | * The limit is arbitrary but avoids the loop running away if |
128 | * the SPRAM tags are implemented differently |
129 | */ |
130 | |
131 | for (i = 0; i < 8; i++) { |
132 | tag0 = read(offset); |
133 | tag1 = read(offset+SPRAM_TAG_STRIDE); |
134 | pr_debug("DBG %s%d: tag0=%08x tag1=%08x\n" , |
135 | type, i, tag0, tag1); |
136 | |
137 | size = tag1 & SPRAM_TAG1_SIZE_MASK; |
138 | |
139 | if (size == 0) |
140 | break; |
141 | |
142 | if (i != 0) { |
143 | /* tags may repeat... */ |
144 | if ((pa == firstpa && size == firstsize) || |
145 | (pa == lastpa && size == lastsize)) |
146 | break; |
147 | } |
148 | |
149 | /* Align base with size */ |
150 | base = (base + size - 1) & ~(size-1); |
151 | |
152 | /* reprogram the base address base address and enable */ |
153 | tag0 = (base & SPRAM_TAG0_PA_MASK) | SPRAM_TAG0_ENABLE; |
154 | write(offset, tag0); |
155 | |
156 | base += size; |
157 | |
158 | /* reread the tag */ |
159 | tag0 = read(offset); |
160 | pa = tag0 & SPRAM_TAG0_PA_MASK; |
161 | enabled = tag0 & SPRAM_TAG0_ENABLE; |
162 | |
163 | if (i == 0) { |
164 | firstpa = pa; |
165 | firstsize = size; |
166 | } |
167 | |
168 | lastpa = pa; |
169 | lastsize = size; |
170 | |
171 | if (strcmp(type, "DSPRAM" ) == 0) { |
172 | unsigned int *vp = (unsigned int *)(CKSEG1 | pa); |
173 | unsigned int v; |
174 | #define TDAT 0x5a5aa5a5 |
175 | vp[0] = TDAT; |
176 | vp[1] = ~TDAT; |
177 | |
178 | mb(); |
179 | |
180 | v = vp[0]; |
181 | if (v != TDAT) |
182 | printk(KERN_ERR "vp=%p wrote=%08x got=%08x\n" , |
183 | vp, TDAT, v); |
184 | v = vp[1]; |
185 | if (v != ~TDAT) |
186 | printk(KERN_ERR "vp=%p wrote=%08x got=%08x\n" , |
187 | vp+1, ~TDAT, v); |
188 | } |
189 | |
190 | pr_info("%s%d: PA=%08x,Size=%08x%s\n" , |
191 | type, i, pa, size, enabled ? ",enabled" : "" ); |
192 | offset += 2 * SPRAM_TAG_STRIDE; |
193 | } |
194 | } |
195 | void spram_config(void) |
196 | { |
197 | unsigned int config0; |
198 | |
199 | switch (current_cpu_type()) { |
200 | case CPU_24K: |
201 | case CPU_34K: |
202 | case CPU_74K: |
203 | case CPU_1004K: |
204 | case CPU_1074K: |
205 | case CPU_INTERAPTIV: |
206 | case CPU_PROAPTIV: |
207 | case CPU_P5600: |
208 | case CPU_QEMU_GENERIC: |
209 | case CPU_I6400: |
210 | case CPU_P6600: |
211 | config0 = read_c0_config(); |
212 | /* FIXME: addresses are Malta specific */ |
213 | if (config0 & MIPS_CONF_ISP) { |
214 | probe_spram(type: "ISPRAM" , base: 0x1c000000, |
215 | read: &ispram_load_tag, write: &ispram_store_tag); |
216 | } |
217 | if (config0 & MIPS_CONF_DSP) |
218 | probe_spram(type: "DSPRAM" , base: 0x1c100000, |
219 | read: &dspram_load_tag, write: &dspram_store_tag); |
220 | } |
221 | } |
222 | |