Merge branch 'for-2.6.40' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/percpu
[pandora-kernel.git] / arch / mips / kernel / spram.c
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 }