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