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