diff options
author | Florian Fainelli <florian@openwrt.org> | 2009-06-24 05:12:57 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2009-07-03 10:45:23 -0400 |
commit | 7ca5dc145bc7daddd8aed8bbda46b74af9cebefc (patch) | |
tree | 130b1aac6b095bdc2638124169795d6857981c31 /arch/mips/ar7 | |
parent | 7c5371c403abb29f01bc6cff6c5096abdf2dc524 (diff) |
MIPS: Add support for Texas Instruments AR7 System-on-a-Chip
This patch adds support for the Texas Instruments AR7 System-on-a-Chip.
It supports the TNETD7100, 7200 and 7300 versions of the SoC.
Signed-off-by: Matteo Croce <matteo@openwrt.org>
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Eugene Konev <ejka@openwrt.org>
Signed-off-by: Nicolas Thill <nico@openwrt.org>
Signed-off-by: Florian Fainelli <florian@openwrt.org>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/ar7')
-rw-r--r-- | arch/mips/ar7/Makefile | 10 | ||||
-rw-r--r-- | arch/mips/ar7/clock.c | 440 | ||||
-rw-r--r-- | arch/mips/ar7/gpio.c | 48 | ||||
-rw-r--r-- | arch/mips/ar7/irq.c | 176 | ||||
-rw-r--r-- | arch/mips/ar7/memory.c | 72 | ||||
-rw-r--r-- | arch/mips/ar7/platform.c | 555 | ||||
-rw-r--r-- | arch/mips/ar7/prom.c | 297 | ||||
-rw-r--r-- | arch/mips/ar7/setup.c | 94 | ||||
-rw-r--r-- | arch/mips/ar7/time.c | 30 |
9 files changed, 1722 insertions, 0 deletions
diff --git a/arch/mips/ar7/Makefile b/arch/mips/ar7/Makefile new file mode 100644 index 000000000000..7435e44b3964 --- /dev/null +++ b/arch/mips/ar7/Makefile | |||
@@ -0,0 +1,10 @@ | |||
1 | |||
2 | obj-y := \ | ||
3 | prom.o \ | ||
4 | setup.o \ | ||
5 | memory.o \ | ||
6 | irq.o \ | ||
7 | time.o \ | ||
8 | platform.o \ | ||
9 | gpio.o \ | ||
10 | clock.o | ||
diff --git a/arch/mips/ar7/clock.c b/arch/mips/ar7/clock.c new file mode 100644 index 000000000000..27dc6663f2fa --- /dev/null +++ b/arch/mips/ar7/clock.c | |||
@@ -0,0 +1,440 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org> | ||
3 | * Copyright (C) 2007 Eugene Konev <ejka@openwrt.org> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/gcd.h> | ||
26 | #include <linux/io.h> | ||
27 | |||
28 | #include <asm/addrspace.h> | ||
29 | #include <asm/mach-ar7/ar7.h> | ||
30 | |||
31 | #define BOOT_PLL_SOURCE_MASK 0x3 | ||
32 | #define CPU_PLL_SOURCE_SHIFT 16 | ||
33 | #define BUS_PLL_SOURCE_SHIFT 14 | ||
34 | #define USB_PLL_SOURCE_SHIFT 18 | ||
35 | #define DSP_PLL_SOURCE_SHIFT 22 | ||
36 | #define BOOT_PLL_SOURCE_AFE 0 | ||
37 | #define BOOT_PLL_SOURCE_BUS 0 | ||
38 | #define BOOT_PLL_SOURCE_REF 1 | ||
39 | #define BOOT_PLL_SOURCE_XTAL 2 | ||
40 | #define BOOT_PLL_SOURCE_CPU 3 | ||
41 | #define BOOT_PLL_BYPASS 0x00000020 | ||
42 | #define BOOT_PLL_ASYNC_MODE 0x02000000 | ||
43 | #define BOOT_PLL_2TO1_MODE 0x00008000 | ||
44 | |||
45 | #define TNETD7200_CLOCK_ID_CPU 0 | ||
46 | #define TNETD7200_CLOCK_ID_DSP 1 | ||
47 | #define TNETD7200_CLOCK_ID_USB 2 | ||
48 | |||
49 | #define TNETD7200_DEF_CPU_CLK 211000000 | ||
50 | #define TNETD7200_DEF_DSP_CLK 125000000 | ||
51 | #define TNETD7200_DEF_USB_CLK 48000000 | ||
52 | |||
53 | struct tnetd7300_clock { | ||
54 | u32 ctrl; | ||
55 | #define PREDIV_MASK 0x001f0000 | ||
56 | #define PREDIV_SHIFT 16 | ||
57 | #define POSTDIV_MASK 0x0000001f | ||
58 | u32 unused1[3]; | ||
59 | u32 pll; | ||
60 | #define MUL_MASK 0x0000f000 | ||
61 | #define MUL_SHIFT 12 | ||
62 | #define PLL_MODE_MASK 0x00000001 | ||
63 | #define PLL_NDIV 0x00000800 | ||
64 | #define PLL_DIV 0x00000002 | ||
65 | #define PLL_STATUS 0x00000001 | ||
66 | u32 unused2[3]; | ||
67 | }; | ||
68 | |||
69 | struct tnetd7300_clocks { | ||
70 | struct tnetd7300_clock bus; | ||
71 | struct tnetd7300_clock cpu; | ||
72 | struct tnetd7300_clock usb; | ||
73 | struct tnetd7300_clock dsp; | ||
74 | }; | ||
75 | |||
76 | struct tnetd7200_clock { | ||
77 | u32 ctrl; | ||
78 | u32 unused1[3]; | ||
79 | #define DIVISOR_ENABLE_MASK 0x00008000 | ||
80 | u32 mul; | ||
81 | u32 prediv; | ||
82 | u32 postdiv; | ||
83 | u32 postdiv2; | ||
84 | u32 unused2[6]; | ||
85 | u32 cmd; | ||
86 | u32 status; | ||
87 | u32 cmden; | ||
88 | u32 padding[15]; | ||
89 | }; | ||
90 | |||
91 | struct tnetd7200_clocks { | ||
92 | struct tnetd7200_clock cpu; | ||
93 | struct tnetd7200_clock dsp; | ||
94 | struct tnetd7200_clock usb; | ||
95 | }; | ||
96 | |||
97 | int ar7_cpu_clock = 150000000; | ||
98 | EXPORT_SYMBOL(ar7_cpu_clock); | ||
99 | int ar7_bus_clock = 125000000; | ||
100 | EXPORT_SYMBOL(ar7_bus_clock); | ||
101 | int ar7_dsp_clock; | ||
102 | EXPORT_SYMBOL(ar7_dsp_clock); | ||
103 | |||
104 | static void approximate(int base, int target, int *prediv, | ||
105 | int *postdiv, int *mul) | ||
106 | { | ||
107 | int i, j, k, freq, res = target; | ||
108 | for (i = 1; i <= 16; i++) | ||
109 | for (j = 1; j <= 32; j++) | ||
110 | for (k = 1; k <= 32; k++) { | ||
111 | freq = abs(base / j * i / k - target); | ||
112 | if (freq < res) { | ||
113 | res = freq; | ||
114 | *mul = i; | ||
115 | *prediv = j; | ||
116 | *postdiv = k; | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | |||
121 | static void calculate(int base, int target, int *prediv, int *postdiv, | ||
122 | int *mul) | ||
123 | { | ||
124 | int tmp_gcd, tmp_base, tmp_freq; | ||
125 | |||
126 | for (*prediv = 1; *prediv <= 32; (*prediv)++) { | ||
127 | tmp_base = base / *prediv; | ||
128 | tmp_gcd = gcd(target, tmp_base); | ||
129 | *mul = target / tmp_gcd; | ||
130 | *postdiv = tmp_base / tmp_gcd; | ||
131 | if ((*mul < 1) || (*mul >= 16)) | ||
132 | continue; | ||
133 | if ((*postdiv > 0) & (*postdiv <= 32)) | ||
134 | break; | ||
135 | } | ||
136 | |||
137 | if (base / *prediv * *mul / *postdiv != target) { | ||
138 | approximate(base, target, prediv, postdiv, mul); | ||
139 | tmp_freq = base / *prediv * *mul / *postdiv; | ||
140 | printk(KERN_WARNING | ||
141 | "Adjusted requested frequency %d to %d\n", | ||
142 | target, tmp_freq); | ||
143 | } | ||
144 | |||
145 | printk(KERN_DEBUG "Clocks: prediv: %d, postdiv: %d, mul: %d\n", | ||
146 | *prediv, *postdiv, *mul); | ||
147 | } | ||
148 | |||
149 | static int tnetd7300_dsp_clock(void) | ||
150 | { | ||
151 | u32 didr1, didr2; | ||
152 | u8 rev = ar7_chip_rev(); | ||
153 | didr1 = readl((void *)KSEG1ADDR(AR7_REGS_GPIO + 0x18)); | ||
154 | didr2 = readl((void *)KSEG1ADDR(AR7_REGS_GPIO + 0x1c)); | ||
155 | if (didr2 & (1 << 23)) | ||
156 | return 0; | ||
157 | if ((rev >= 0x23) && (rev != 0x57)) | ||
158 | return 250000000; | ||
159 | if ((((didr2 & 0x1fff) << 10) | ((didr1 & 0xffc00000) >> 22)) | ||
160 | > 4208000) | ||
161 | return 250000000; | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static int tnetd7300_get_clock(u32 shift, struct tnetd7300_clock *clock, | ||
166 | u32 *bootcr, u32 bus_clock) | ||
167 | { | ||
168 | int product; | ||
169 | int base_clock = AR7_REF_CLOCK; | ||
170 | u32 ctrl = readl(&clock->ctrl); | ||
171 | u32 pll = readl(&clock->pll); | ||
172 | int prediv = ((ctrl & PREDIV_MASK) >> PREDIV_SHIFT) + 1; | ||
173 | int postdiv = (ctrl & POSTDIV_MASK) + 1; | ||
174 | int divisor = prediv * postdiv; | ||
175 | int mul = ((pll & MUL_MASK) >> MUL_SHIFT) + 1; | ||
176 | |||
177 | switch ((*bootcr & (BOOT_PLL_SOURCE_MASK << shift)) >> shift) { | ||
178 | case BOOT_PLL_SOURCE_BUS: | ||
179 | base_clock = bus_clock; | ||
180 | break; | ||
181 | case BOOT_PLL_SOURCE_REF: | ||
182 | base_clock = AR7_REF_CLOCK; | ||
183 | break; | ||
184 | case BOOT_PLL_SOURCE_XTAL: | ||
185 | base_clock = AR7_XTAL_CLOCK; | ||
186 | break; | ||
187 | case BOOT_PLL_SOURCE_CPU: | ||
188 | base_clock = ar7_cpu_clock; | ||
189 | break; | ||
190 | } | ||
191 | |||
192 | if (*bootcr & BOOT_PLL_BYPASS) | ||
193 | return base_clock / divisor; | ||
194 | |||
195 | if ((pll & PLL_MODE_MASK) == 0) | ||
196 | return (base_clock >> (mul / 16 + 1)) / divisor; | ||
197 | |||
198 | if ((pll & (PLL_NDIV | PLL_DIV)) == (PLL_NDIV | PLL_DIV)) { | ||
199 | product = (mul & 1) ? | ||
200 | (base_clock * mul) >> 1 : | ||
201 | (base_clock * (mul - 1)) >> 2; | ||
202 | return product / divisor; | ||
203 | } | ||
204 | |||
205 | if (mul == 16) | ||
206 | return base_clock / divisor; | ||
207 | |||
208 | return base_clock * mul / divisor; | ||
209 | } | ||
210 | |||
211 | static void tnetd7300_set_clock(u32 shift, struct tnetd7300_clock *clock, | ||
212 | u32 *bootcr, u32 frequency) | ||
213 | { | ||
214 | int prediv, postdiv, mul; | ||
215 | int base_clock = ar7_bus_clock; | ||
216 | |||
217 | switch ((*bootcr & (BOOT_PLL_SOURCE_MASK << shift)) >> shift) { | ||
218 | case BOOT_PLL_SOURCE_BUS: | ||
219 | base_clock = ar7_bus_clock; | ||
220 | break; | ||
221 | case BOOT_PLL_SOURCE_REF: | ||
222 | base_clock = AR7_REF_CLOCK; | ||
223 | break; | ||
224 | case BOOT_PLL_SOURCE_XTAL: | ||
225 | base_clock = AR7_XTAL_CLOCK; | ||
226 | break; | ||
227 | case BOOT_PLL_SOURCE_CPU: | ||
228 | base_clock = ar7_cpu_clock; | ||
229 | break; | ||
230 | } | ||
231 | |||
232 | calculate(base_clock, frequency, &prediv, &postdiv, &mul); | ||
233 | |||
234 | writel(((prediv - 1) << PREDIV_SHIFT) | (postdiv - 1), &clock->ctrl); | ||
235 | msleep(1); | ||
236 | writel(4, &clock->pll); | ||
237 | while (readl(&clock->pll) & PLL_STATUS) | ||
238 | ; | ||
239 | writel(((mul - 1) << MUL_SHIFT) | (0xff << 3) | 0x0e, &clock->pll); | ||
240 | msleep(75); | ||
241 | } | ||
242 | |||
243 | static void __init tnetd7300_init_clocks(void) | ||
244 | { | ||
245 | u32 *bootcr = (u32 *)ioremap_nocache(AR7_REGS_DCL, 4); | ||
246 | struct tnetd7300_clocks *clocks = | ||
247 | ioremap_nocache(UR8_REGS_CLOCKS, | ||
248 | sizeof(struct tnetd7300_clocks)); | ||
249 | |||
250 | ar7_bus_clock = tnetd7300_get_clock(BUS_PLL_SOURCE_SHIFT, | ||
251 | &clocks->bus, bootcr, AR7_AFE_CLOCK); | ||
252 | |||
253 | if (*bootcr & BOOT_PLL_ASYNC_MODE) | ||
254 | ar7_cpu_clock = tnetd7300_get_clock(CPU_PLL_SOURCE_SHIFT, | ||
255 | &clocks->cpu, bootcr, AR7_AFE_CLOCK); | ||
256 | else | ||
257 | ar7_cpu_clock = ar7_bus_clock; | ||
258 | |||
259 | if (ar7_dsp_clock == 250000000) | ||
260 | tnetd7300_set_clock(DSP_PLL_SOURCE_SHIFT, &clocks->dsp, | ||
261 | bootcr, ar7_dsp_clock); | ||
262 | |||
263 | iounmap(clocks); | ||
264 | iounmap(bootcr); | ||
265 | } | ||
266 | |||
267 | static int tnetd7200_get_clock(int base, struct tnetd7200_clock *clock, | ||
268 | u32 *bootcr, u32 bus_clock) | ||
269 | { | ||
270 | int divisor = ((readl(&clock->prediv) & 0x1f) + 1) * | ||
271 | ((readl(&clock->postdiv) & 0x1f) + 1); | ||
272 | |||
273 | if (*bootcr & BOOT_PLL_BYPASS) | ||
274 | return base / divisor; | ||
275 | |||
276 | return base * ((readl(&clock->mul) & 0xf) + 1) / divisor; | ||
277 | } | ||
278 | |||
279 | |||
280 | static void tnetd7200_set_clock(int base, struct tnetd7200_clock *clock, | ||
281 | int prediv, int postdiv, int postdiv2, int mul, u32 frequency) | ||
282 | { | ||
283 | printk(KERN_INFO | ||
284 | "Clocks: base = %d, frequency = %u, prediv = %d, " | ||
285 | "postdiv = %d, postdiv2 = %d, mul = %d\n", | ||
286 | base, frequency, prediv, postdiv, postdiv2, mul); | ||
287 | |||
288 | writel(0, &clock->ctrl); | ||
289 | writel(DIVISOR_ENABLE_MASK | ((prediv - 1) & 0x1F), &clock->prediv); | ||
290 | writel((mul - 1) & 0xF, &clock->mul); | ||
291 | |||
292 | while (readl(&clock->status) & 0x1) | ||
293 | ; /* nop */ | ||
294 | |||
295 | writel(DIVISOR_ENABLE_MASK | ((postdiv - 1) & 0x1F), &clock->postdiv); | ||
296 | |||
297 | writel(readl(&clock->cmden) | 1, &clock->cmden); | ||
298 | writel(readl(&clock->cmd) | 1, &clock->cmd); | ||
299 | |||
300 | while (readl(&clock->status) & 0x1) | ||
301 | ; /* nop */ | ||
302 | |||
303 | writel(DIVISOR_ENABLE_MASK | ((postdiv2 - 1) & 0x1F), &clock->postdiv2); | ||
304 | |||
305 | writel(readl(&clock->cmden) | 1, &clock->cmden); | ||
306 | writel(readl(&clock->cmd) | 1, &clock->cmd); | ||
307 | |||
308 | while (readl(&clock->status) & 0x1) | ||
309 | ; /* nop */ | ||
310 | |||
311 | writel(readl(&clock->ctrl) | 1, &clock->ctrl); | ||
312 | } | ||
313 | |||
314 | static int tnetd7200_get_clock_base(int clock_id, u32 *bootcr) | ||
315 | { | ||
316 | if (*bootcr & BOOT_PLL_ASYNC_MODE) | ||
317 | /* Async */ | ||
318 | switch (clock_id) { | ||
319 | case TNETD7200_CLOCK_ID_DSP: | ||
320 | return AR7_REF_CLOCK; | ||
321 | default: | ||
322 | return AR7_AFE_CLOCK; | ||
323 | } | ||
324 | else | ||
325 | /* Sync */ | ||
326 | if (*bootcr & BOOT_PLL_2TO1_MODE) | ||
327 | /* 2:1 */ | ||
328 | switch (clock_id) { | ||
329 | case TNETD7200_CLOCK_ID_DSP: | ||
330 | return AR7_REF_CLOCK; | ||
331 | default: | ||
332 | return AR7_AFE_CLOCK; | ||
333 | } | ||
334 | else | ||
335 | /* 1:1 */ | ||
336 | return AR7_REF_CLOCK; | ||
337 | } | ||
338 | |||
339 | |||
340 | static void __init tnetd7200_init_clocks(void) | ||
341 | { | ||
342 | u32 *bootcr = (u32 *)ioremap_nocache(AR7_REGS_DCL, 4); | ||
343 | struct tnetd7200_clocks *clocks = | ||
344 | ioremap_nocache(AR7_REGS_CLOCKS, | ||
345 | sizeof(struct tnetd7200_clocks)); | ||
346 | int cpu_base, cpu_mul, cpu_prediv, cpu_postdiv; | ||
347 | int dsp_base, dsp_mul, dsp_prediv, dsp_postdiv; | ||
348 | int usb_base, usb_mul, usb_prediv, usb_postdiv; | ||
349 | |||
350 | cpu_base = tnetd7200_get_clock_base(TNETD7200_CLOCK_ID_CPU, bootcr); | ||
351 | dsp_base = tnetd7200_get_clock_base(TNETD7200_CLOCK_ID_DSP, bootcr); | ||
352 | |||
353 | if (*bootcr & BOOT_PLL_ASYNC_MODE) { | ||
354 | printk(KERN_INFO "Clocks: Async mode\n"); | ||
355 | |||
356 | printk(KERN_INFO "Clocks: Setting DSP clock\n"); | ||
357 | calculate(dsp_base, TNETD7200_DEF_DSP_CLK, | ||
358 | &dsp_prediv, &dsp_postdiv, &dsp_mul); | ||
359 | ar7_bus_clock = | ||
360 | ((dsp_base / dsp_prediv) * dsp_mul) / dsp_postdiv; | ||
361 | tnetd7200_set_clock(dsp_base, &clocks->dsp, | ||
362 | dsp_prediv, dsp_postdiv * 2, dsp_postdiv, dsp_mul * 2, | ||
363 | ar7_bus_clock); | ||
364 | |||
365 | printk(KERN_INFO "Clocks: Setting CPU clock\n"); | ||
366 | calculate(cpu_base, TNETD7200_DEF_CPU_CLK, &cpu_prediv, | ||
367 | &cpu_postdiv, &cpu_mul); | ||
368 | ar7_cpu_clock = | ||
369 | ((cpu_base / cpu_prediv) * cpu_mul) / cpu_postdiv; | ||
370 | tnetd7200_set_clock(cpu_base, &clocks->cpu, | ||
371 | cpu_prediv, cpu_postdiv, -1, cpu_mul, | ||
372 | ar7_cpu_clock); | ||
373 | |||
374 | } else | ||
375 | if (*bootcr & BOOT_PLL_2TO1_MODE) { | ||
376 | printk(KERN_INFO "Clocks: Sync 2:1 mode\n"); | ||
377 | |||
378 | printk(KERN_INFO "Clocks: Setting CPU clock\n"); | ||
379 | calculate(cpu_base, TNETD7200_DEF_CPU_CLK, &cpu_prediv, | ||
380 | &cpu_postdiv, &cpu_mul); | ||
381 | ar7_cpu_clock = ((cpu_base / cpu_prediv) * cpu_mul) | ||
382 | / cpu_postdiv; | ||
383 | tnetd7200_set_clock(cpu_base, &clocks->cpu, | ||
384 | cpu_prediv, cpu_postdiv, -1, cpu_mul, | ||
385 | ar7_cpu_clock); | ||
386 | |||
387 | printk(KERN_INFO "Clocks: Setting DSP clock\n"); | ||
388 | calculate(dsp_base, TNETD7200_DEF_DSP_CLK, &dsp_prediv, | ||
389 | &dsp_postdiv, &dsp_mul); | ||
390 | ar7_bus_clock = ar7_cpu_clock / 2; | ||
391 | tnetd7200_set_clock(dsp_base, &clocks->dsp, | ||
392 | dsp_prediv, dsp_postdiv * 2, dsp_postdiv, | ||
393 | dsp_mul * 2, ar7_bus_clock); | ||
394 | } else { | ||
395 | printk(KERN_INFO "Clocks: Sync 1:1 mode\n"); | ||
396 | |||
397 | printk(KERN_INFO "Clocks: Setting DSP clock\n"); | ||
398 | calculate(dsp_base, TNETD7200_DEF_DSP_CLK, &dsp_prediv, | ||
399 | &dsp_postdiv, &dsp_mul); | ||
400 | ar7_bus_clock = ((dsp_base / dsp_prediv) * dsp_mul) | ||
401 | / dsp_postdiv; | ||
402 | tnetd7200_set_clock(dsp_base, &clocks->dsp, | ||
403 | dsp_prediv, dsp_postdiv * 2, dsp_postdiv, | ||
404 | dsp_mul * 2, ar7_bus_clock); | ||
405 | |||
406 | ar7_cpu_clock = ar7_bus_clock; | ||
407 | } | ||
408 | |||
409 | printk(KERN_INFO "Clocks: Setting USB clock\n"); | ||
410 | usb_base = ar7_bus_clock; | ||
411 | calculate(usb_base, TNETD7200_DEF_USB_CLK, &usb_prediv, | ||
412 | &usb_postdiv, &usb_mul); | ||
413 | tnetd7200_set_clock(usb_base, &clocks->usb, | ||
414 | usb_prediv, usb_postdiv, -1, usb_mul, | ||
415 | TNETD7200_DEF_USB_CLK); | ||
416 | |||
417 | ar7_dsp_clock = ar7_cpu_clock; | ||
418 | |||
419 | iounmap(clocks); | ||
420 | iounmap(bootcr); | ||
421 | } | ||
422 | |||
423 | int __init ar7_init_clocks(void) | ||
424 | { | ||
425 | switch (ar7_chip_id()) { | ||
426 | case AR7_CHIP_7100: | ||
427 | case AR7_CHIP_7200: | ||
428 | tnetd7200_init_clocks(); | ||
429 | break; | ||
430 | case AR7_CHIP_7300: | ||
431 | ar7_dsp_clock = tnetd7300_dsp_clock(); | ||
432 | tnetd7300_init_clocks(); | ||
433 | break; | ||
434 | default: | ||
435 | break; | ||
436 | } | ||
437 | |||
438 | return 0; | ||
439 | } | ||
440 | arch_initcall(ar7_init_clocks); | ||
diff --git a/arch/mips/ar7/gpio.c b/arch/mips/ar7/gpio.c new file mode 100644 index 000000000000..74e14a3dbf4a --- /dev/null +++ b/arch/mips/ar7/gpio.c | |||
@@ -0,0 +1,48 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org> | ||
3 | * Copyright (C) 2007 Eugene Konev <ejka@openwrt.org> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | |||
22 | #include <asm/mach-ar7/gpio.h> | ||
23 | |||
24 | static const char *ar7_gpio_list[AR7_GPIO_MAX]; | ||
25 | |||
26 | int gpio_request(unsigned gpio, const char *label) | ||
27 | { | ||
28 | if (gpio >= AR7_GPIO_MAX) | ||
29 | return -EINVAL; | ||
30 | |||
31 | if (ar7_gpio_list[gpio]) | ||
32 | return -EBUSY; | ||
33 | |||
34 | if (label) | ||
35 | ar7_gpio_list[gpio] = label; | ||
36 | else | ||
37 | ar7_gpio_list[gpio] = "busy"; | ||
38 | |||
39 | return 0; | ||
40 | } | ||
41 | EXPORT_SYMBOL(gpio_request); | ||
42 | |||
43 | void gpio_free(unsigned gpio) | ||
44 | { | ||
45 | BUG_ON(!ar7_gpio_list[gpio]); | ||
46 | ar7_gpio_list[gpio] = NULL; | ||
47 | } | ||
48 | EXPORT_SYMBOL(gpio_free); | ||
diff --git a/arch/mips/ar7/irq.c b/arch/mips/ar7/irq.c new file mode 100644 index 000000000000..c781556c44e4 --- /dev/null +++ b/arch/mips/ar7/irq.c | |||
@@ -0,0 +1,176 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org> | ||
3 | * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/io.h> | ||
22 | |||
23 | #include <asm/irq_cpu.h> | ||
24 | #include <asm/mipsregs.h> | ||
25 | #include <asm/mach-ar7/ar7.h> | ||
26 | |||
27 | #define EXCEPT_OFFSET 0x80 | ||
28 | #define PACE_OFFSET 0xA0 | ||
29 | #define CHNLS_OFFSET 0x200 | ||
30 | |||
31 | #define REG_OFFSET(irq, reg) ((irq) / 32 * 0x4 + reg * 0x10) | ||
32 | #define SEC_REG_OFFSET(reg) (EXCEPT_OFFSET + reg * 0x8) | ||
33 | #define SEC_SR_OFFSET (SEC_REG_OFFSET(0)) /* 0x80 */ | ||
34 | #define CR_OFFSET(irq) (REG_OFFSET(irq, 1)) /* 0x10 */ | ||
35 | #define SEC_CR_OFFSET (SEC_REG_OFFSET(1)) /* 0x88 */ | ||
36 | #define ESR_OFFSET(irq) (REG_OFFSET(irq, 2)) /* 0x20 */ | ||
37 | #define SEC_ESR_OFFSET (SEC_REG_OFFSET(2)) /* 0x90 */ | ||
38 | #define ECR_OFFSET(irq) (REG_OFFSET(irq, 3)) /* 0x30 */ | ||
39 | #define SEC_ECR_OFFSET (SEC_REG_OFFSET(3)) /* 0x98 */ | ||
40 | #define PIR_OFFSET (0x40) | ||
41 | #define MSR_OFFSET (0x44) | ||
42 | #define PM_OFFSET(irq) (REG_OFFSET(irq, 5)) /* 0x50 */ | ||
43 | #define TM_OFFSET(irq) (REG_OFFSET(irq, 6)) /* 0x60 */ | ||
44 | |||
45 | #define REG(addr) ((u32 *)(KSEG1ADDR(AR7_REGS_IRQ) + addr)) | ||
46 | |||
47 | #define CHNL_OFFSET(chnl) (CHNLS_OFFSET + (chnl * 4)) | ||
48 | |||
49 | static int ar7_irq_base; | ||
50 | |||
51 | static void ar7_unmask_irq(unsigned int irq) | ||
52 | { | ||
53 | writel(1 << ((irq - ar7_irq_base) % 32), | ||
54 | REG(ESR_OFFSET(irq - ar7_irq_base))); | ||
55 | } | ||
56 | |||
57 | static void ar7_mask_irq(unsigned int irq) | ||
58 | { | ||
59 | writel(1 << ((irq - ar7_irq_base) % 32), | ||
60 | REG(ECR_OFFSET(irq - ar7_irq_base))); | ||
61 | } | ||
62 | |||
63 | static void ar7_ack_irq(unsigned int irq) | ||
64 | { | ||
65 | writel(1 << ((irq - ar7_irq_base) % 32), | ||
66 | REG(CR_OFFSET(irq - ar7_irq_base))); | ||
67 | } | ||
68 | |||
69 | static void ar7_unmask_sec_irq(unsigned int irq) | ||
70 | { | ||
71 | writel(1 << (irq - ar7_irq_base - 40), REG(SEC_ESR_OFFSET)); | ||
72 | } | ||
73 | |||
74 | static void ar7_mask_sec_irq(unsigned int irq) | ||
75 | { | ||
76 | writel(1 << (irq - ar7_irq_base - 40), REG(SEC_ECR_OFFSET)); | ||
77 | } | ||
78 | |||
79 | static void ar7_ack_sec_irq(unsigned int irq) | ||
80 | { | ||
81 | writel(1 << (irq - ar7_irq_base - 40), REG(SEC_CR_OFFSET)); | ||
82 | } | ||
83 | |||
84 | static struct irq_chip ar7_irq_type = { | ||
85 | .name = "AR7", | ||
86 | .unmask = ar7_unmask_irq, | ||
87 | .mask = ar7_mask_irq, | ||
88 | .ack = ar7_ack_irq | ||
89 | }; | ||
90 | |||
91 | static struct irq_chip ar7_sec_irq_type = { | ||
92 | .name = "AR7", | ||
93 | .unmask = ar7_unmask_sec_irq, | ||
94 | .mask = ar7_mask_sec_irq, | ||
95 | .ack = ar7_ack_sec_irq, | ||
96 | }; | ||
97 | |||
98 | static struct irqaction ar7_cascade_action = { | ||
99 | .handler = no_action, | ||
100 | .name = "AR7 cascade interrupt" | ||
101 | }; | ||
102 | |||
103 | static void __init ar7_irq_init(int base) | ||
104 | { | ||
105 | int i; | ||
106 | /* | ||
107 | * Disable interrupts and clear pending | ||
108 | */ | ||
109 | writel(0xffffffff, REG(ECR_OFFSET(0))); | ||
110 | writel(0xff, REG(ECR_OFFSET(32))); | ||
111 | writel(0xffffffff, REG(SEC_ECR_OFFSET)); | ||
112 | writel(0xffffffff, REG(CR_OFFSET(0))); | ||
113 | writel(0xff, REG(CR_OFFSET(32))); | ||
114 | writel(0xffffffff, REG(SEC_CR_OFFSET)); | ||
115 | |||
116 | ar7_irq_base = base; | ||
117 | |||
118 | for (i = 0; i < 40; i++) { | ||
119 | writel(i, REG(CHNL_OFFSET(i))); | ||
120 | /* Primary IRQ's */ | ||
121 | set_irq_chip_and_handler(base + i, &ar7_irq_type, | ||
122 | handle_level_irq); | ||
123 | /* Secondary IRQ's */ | ||
124 | if (i < 32) | ||
125 | set_irq_chip_and_handler(base + i + 40, | ||
126 | &ar7_sec_irq_type, | ||
127 | handle_level_irq); | ||
128 | } | ||
129 | |||
130 | setup_irq(2, &ar7_cascade_action); | ||
131 | setup_irq(ar7_irq_base, &ar7_cascade_action); | ||
132 | set_c0_status(IE_IRQ0); | ||
133 | } | ||
134 | |||
135 | void __init arch_init_irq(void) | ||
136 | { | ||
137 | mips_cpu_irq_init(); | ||
138 | ar7_irq_init(8); | ||
139 | } | ||
140 | |||
141 | static void ar7_cascade(void) | ||
142 | { | ||
143 | u32 status; | ||
144 | int i, irq; | ||
145 | |||
146 | /* Primary IRQ's */ | ||
147 | irq = readl(REG(PIR_OFFSET)) & 0x3f; | ||
148 | if (irq) { | ||
149 | do_IRQ(ar7_irq_base + irq); | ||
150 | return; | ||
151 | } | ||
152 | |||
153 | /* Secondary IRQ's are cascaded through primary '0' */ | ||
154 | writel(1, REG(CR_OFFSET(irq))); | ||
155 | status = readl(REG(SEC_SR_OFFSET)); | ||
156 | for (i = 0; i < 32; i++) { | ||
157 | if (status & 1) { | ||
158 | do_IRQ(ar7_irq_base + i + 40); | ||
159 | return; | ||
160 | } | ||
161 | status >>= 1; | ||
162 | } | ||
163 | |||
164 | spurious_interrupt(); | ||
165 | } | ||
166 | |||
167 | asmlinkage void plat_irq_dispatch(void) | ||
168 | { | ||
169 | unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; | ||
170 | if (pending & STATUSF_IP7) /* cpu timer */ | ||
171 | do_IRQ(7); | ||
172 | else if (pending & STATUSF_IP2) /* int0 hardware line */ | ||
173 | ar7_cascade(); | ||
174 | else | ||
175 | spurious_interrupt(); | ||
176 | } | ||
diff --git a/arch/mips/ar7/memory.c b/arch/mips/ar7/memory.c new file mode 100644 index 000000000000..46fed44825a6 --- /dev/null +++ b/arch/mips/ar7/memory.c | |||
@@ -0,0 +1,72 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Felix Fietkau <nbd@openwrt.org> | ||
3 | * Copyright (C) 2007 Eugene Konev <ejka@openwrt.org> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | #include <linux/bootmem.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/pfn.h> | ||
24 | #include <linux/proc_fs.h> | ||
25 | #include <linux/string.h> | ||
26 | #include <linux/swap.h> | ||
27 | |||
28 | #include <asm/bootinfo.h> | ||
29 | #include <asm/page.h> | ||
30 | #include <asm/sections.h> | ||
31 | |||
32 | #include <asm/mach-ar7/ar7.h> | ||
33 | #include <asm/mips-boards/prom.h> | ||
34 | |||
35 | static int __init memsize(void) | ||
36 | { | ||
37 | u32 size = (64 << 20); | ||
38 | u32 *addr = (u32 *)KSEG1ADDR(AR7_SDRAM_BASE + size - 4); | ||
39 | u32 *kernel_end = (u32 *)KSEG1ADDR(CPHYSADDR((u32)&_end)); | ||
40 | u32 *tmpaddr = addr; | ||
41 | |||
42 | while (tmpaddr > kernel_end) { | ||
43 | *tmpaddr = (u32)tmpaddr; | ||
44 | size >>= 1; | ||
45 | tmpaddr -= size >> 2; | ||
46 | } | ||
47 | |||
48 | do { | ||
49 | tmpaddr += size >> 2; | ||
50 | if (*tmpaddr != (u32)tmpaddr) | ||
51 | break; | ||
52 | size <<= 1; | ||
53 | } while (size < (64 << 20)); | ||
54 | |||
55 | writel(tmpaddr, &addr); | ||
56 | |||
57 | return size; | ||
58 | } | ||
59 | |||
60 | void __init prom_meminit(void) | ||
61 | { | ||
62 | unsigned long pages; | ||
63 | |||
64 | pages = memsize() >> PAGE_SHIFT; | ||
65 | add_memory_region(PHYS_OFFSET, pages << PAGE_SHIFT, | ||
66 | BOOT_MEM_RAM); | ||
67 | } | ||
68 | |||
69 | void __init prom_free_prom_memory(void) | ||
70 | { | ||
71 | /* Nothing to free */ | ||
72 | } | ||
diff --git a/arch/mips/ar7/platform.c b/arch/mips/ar7/platform.c new file mode 100644 index 000000000000..542244961780 --- /dev/null +++ b/arch/mips/ar7/platform.c | |||
@@ -0,0 +1,555 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org> | ||
3 | * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/init.h> | ||
21 | #include <linux/types.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/dma-mapping.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/mtd/physmap.h> | ||
27 | #include <linux/serial.h> | ||
28 | #include <linux/serial_8250.h> | ||
29 | #include <linux/ioport.h> | ||
30 | #include <linux/io.h> | ||
31 | #include <linux/version.h> | ||
32 | #include <linux/vlynq.h> | ||
33 | #include <linux/leds.h> | ||
34 | #include <linux/string.h> | ||
35 | #include <linux/etherdevice.h> | ||
36 | |||
37 | #include <asm/addrspace.h> | ||
38 | #include <asm/mach-ar7/ar7.h> | ||
39 | #include <asm/mach-ar7/gpio.h> | ||
40 | #include <asm/mach-ar7/prom.h> | ||
41 | |||
42 | struct plat_vlynq_data { | ||
43 | struct plat_vlynq_ops ops; | ||
44 | int gpio_bit; | ||
45 | int reset_bit; | ||
46 | }; | ||
47 | |||
48 | |||
49 | static int vlynq_on(struct vlynq_device *dev) | ||
50 | { | ||
51 | int result; | ||
52 | struct plat_vlynq_data *pdata = dev->dev.platform_data; | ||
53 | |||
54 | result = gpio_request(pdata->gpio_bit, "vlynq"); | ||
55 | if (result) | ||
56 | goto out; | ||
57 | |||
58 | ar7_device_reset(pdata->reset_bit); | ||
59 | |||
60 | result = ar7_gpio_disable(pdata->gpio_bit); | ||
61 | if (result) | ||
62 | goto out_enabled; | ||
63 | |||
64 | result = ar7_gpio_enable(pdata->gpio_bit); | ||
65 | if (result) | ||
66 | goto out_enabled; | ||
67 | |||
68 | result = gpio_direction_output(pdata->gpio_bit, 0); | ||
69 | if (result) | ||
70 | goto out_gpio_enabled; | ||
71 | |||
72 | msleep(50); | ||
73 | |||
74 | gpio_set_value(pdata->gpio_bit, 1); | ||
75 | msleep(50); | ||
76 | |||
77 | return 0; | ||
78 | |||
79 | out_gpio_enabled: | ||
80 | ar7_gpio_disable(pdata->gpio_bit); | ||
81 | out_enabled: | ||
82 | ar7_device_disable(pdata->reset_bit); | ||
83 | gpio_free(pdata->gpio_bit); | ||
84 | out: | ||
85 | return result; | ||
86 | } | ||
87 | |||
88 | static void vlynq_off(struct vlynq_device *dev) | ||
89 | { | ||
90 | struct plat_vlynq_data *pdata = dev->dev.platform_data; | ||
91 | ar7_gpio_disable(pdata->gpio_bit); | ||
92 | gpio_free(pdata->gpio_bit); | ||
93 | ar7_device_disable(pdata->reset_bit); | ||
94 | } | ||
95 | |||
96 | static struct resource physmap_flash_resource = { | ||
97 | .name = "mem", | ||
98 | .flags = IORESOURCE_MEM, | ||
99 | .start = 0x10000000, | ||
100 | .end = 0x107fffff, | ||
101 | }; | ||
102 | |||
103 | static struct resource cpmac_low_res[] = { | ||
104 | { | ||
105 | .name = "regs", | ||
106 | .flags = IORESOURCE_MEM, | ||
107 | .start = AR7_REGS_MAC0, | ||
108 | .end = AR7_REGS_MAC0 + 0x7ff, | ||
109 | }, | ||
110 | { | ||
111 | .name = "irq", | ||
112 | .flags = IORESOURCE_IRQ, | ||
113 | .start = 27, | ||
114 | .end = 27, | ||
115 | }, | ||
116 | }; | ||
117 | |||
118 | static struct resource cpmac_high_res[] = { | ||
119 | { | ||
120 | .name = "regs", | ||
121 | .flags = IORESOURCE_MEM, | ||
122 | .start = AR7_REGS_MAC1, | ||
123 | .end = AR7_REGS_MAC1 + 0x7ff, | ||
124 | }, | ||
125 | { | ||
126 | .name = "irq", | ||
127 | .flags = IORESOURCE_IRQ, | ||
128 | .start = 41, | ||
129 | .end = 41, | ||
130 | }, | ||
131 | }; | ||
132 | |||
133 | static struct resource vlynq_low_res[] = { | ||
134 | { | ||
135 | .name = "regs", | ||
136 | .flags = IORESOURCE_MEM, | ||
137 | .start = AR7_REGS_VLYNQ0, | ||
138 | .end = AR7_REGS_VLYNQ0 + 0xff, | ||
139 | }, | ||
140 | { | ||
141 | .name = "irq", | ||
142 | .flags = IORESOURCE_IRQ, | ||
143 | .start = 29, | ||
144 | .end = 29, | ||
145 | }, | ||
146 | { | ||
147 | .name = "mem", | ||
148 | .flags = IORESOURCE_MEM, | ||
149 | .start = 0x04000000, | ||
150 | .end = 0x04ffffff, | ||
151 | }, | ||
152 | { | ||
153 | .name = "devirq", | ||
154 | .flags = IORESOURCE_IRQ, | ||
155 | .start = 80, | ||
156 | .end = 111, | ||
157 | }, | ||
158 | }; | ||
159 | |||
160 | static struct resource vlynq_high_res[] = { | ||
161 | { | ||
162 | .name = "regs", | ||
163 | .flags = IORESOURCE_MEM, | ||
164 | .start = AR7_REGS_VLYNQ1, | ||
165 | .end = AR7_REGS_VLYNQ1 + 0xff, | ||
166 | }, | ||
167 | { | ||
168 | .name = "irq", | ||
169 | .flags = IORESOURCE_IRQ, | ||
170 | .start = 33, | ||
171 | .end = 33, | ||
172 | }, | ||
173 | { | ||
174 | .name = "mem", | ||
175 | .flags = IORESOURCE_MEM, | ||
176 | .start = 0x0c000000, | ||
177 | .end = 0x0cffffff, | ||
178 | }, | ||
179 | { | ||
180 | .name = "devirq", | ||
181 | .flags = IORESOURCE_IRQ, | ||
182 | .start = 112, | ||
183 | .end = 143, | ||
184 | }, | ||
185 | }; | ||
186 | |||
187 | static struct resource usb_res[] = { | ||
188 | { | ||
189 | .name = "regs", | ||
190 | .flags = IORESOURCE_MEM, | ||
191 | .start = AR7_REGS_USB, | ||
192 | .end = AR7_REGS_USB + 0xff, | ||
193 | }, | ||
194 | { | ||
195 | .name = "irq", | ||
196 | .flags = IORESOURCE_IRQ, | ||
197 | .start = 32, | ||
198 | .end = 32, | ||
199 | }, | ||
200 | { | ||
201 | .name = "mem", | ||
202 | .flags = IORESOURCE_MEM, | ||
203 | .start = 0x03400000, | ||
204 | .end = 0x034001fff, | ||
205 | }, | ||
206 | }; | ||
207 | |||
208 | static struct physmap_flash_data physmap_flash_data = { | ||
209 | .width = 2, | ||
210 | }; | ||
211 | |||
212 | static struct plat_cpmac_data cpmac_low_data = { | ||
213 | .reset_bit = 17, | ||
214 | .power_bit = 20, | ||
215 | .phy_mask = 0x80000000, | ||
216 | }; | ||
217 | |||
218 | static struct plat_cpmac_data cpmac_high_data = { | ||
219 | .reset_bit = 21, | ||
220 | .power_bit = 22, | ||
221 | .phy_mask = 0x7fffffff, | ||
222 | }; | ||
223 | |||
224 | static struct plat_vlynq_data vlynq_low_data = { | ||
225 | .ops.on = vlynq_on, | ||
226 | .ops.off = vlynq_off, | ||
227 | .reset_bit = 20, | ||
228 | .gpio_bit = 18, | ||
229 | }; | ||
230 | |||
231 | static struct plat_vlynq_data vlynq_high_data = { | ||
232 | .ops.on = vlynq_on, | ||
233 | .ops.off = vlynq_off, | ||
234 | .reset_bit = 16, | ||
235 | .gpio_bit = 19, | ||
236 | }; | ||
237 | |||
238 | static struct platform_device physmap_flash = { | ||
239 | .id = 0, | ||
240 | .name = "physmap-flash", | ||
241 | .dev.platform_data = &physmap_flash_data, | ||
242 | .resource = &physmap_flash_resource, | ||
243 | .num_resources = 1, | ||
244 | }; | ||
245 | |||
246 | static u64 cpmac_dma_mask = DMA_32BIT_MASK; | ||
247 | static struct platform_device cpmac_low = { | ||
248 | .id = 0, | ||
249 | .name = "cpmac", | ||
250 | .dev = { | ||
251 | .dma_mask = &cpmac_dma_mask, | ||
252 | .coherent_dma_mask = DMA_32BIT_MASK, | ||
253 | .platform_data = &cpmac_low_data, | ||
254 | }, | ||
255 | .resource = cpmac_low_res, | ||
256 | .num_resources = ARRAY_SIZE(cpmac_low_res), | ||
257 | }; | ||
258 | |||
259 | static struct platform_device cpmac_high = { | ||
260 | .id = 1, | ||
261 | .name = "cpmac", | ||
262 | .dev = { | ||
263 | .dma_mask = &cpmac_dma_mask, | ||
264 | .coherent_dma_mask = DMA_32BIT_MASK, | ||
265 | .platform_data = &cpmac_high_data, | ||
266 | }, | ||
267 | .resource = cpmac_high_res, | ||
268 | .num_resources = ARRAY_SIZE(cpmac_high_res), | ||
269 | }; | ||
270 | |||
271 | static struct platform_device vlynq_low = { | ||
272 | .id = 0, | ||
273 | .name = "vlynq", | ||
274 | .dev.platform_data = &vlynq_low_data, | ||
275 | .resource = vlynq_low_res, | ||
276 | .num_resources = ARRAY_SIZE(vlynq_low_res), | ||
277 | }; | ||
278 | |||
279 | static struct platform_device vlynq_high = { | ||
280 | .id = 1, | ||
281 | .name = "vlynq", | ||
282 | .dev.platform_data = &vlynq_high_data, | ||
283 | .resource = vlynq_high_res, | ||
284 | .num_resources = ARRAY_SIZE(vlynq_high_res), | ||
285 | }; | ||
286 | |||
287 | |||
288 | static struct gpio_led default_leds[] = { | ||
289 | { | ||
290 | .name = "status", | ||
291 | .gpio = 8, | ||
292 | .active_low = 1, | ||
293 | }, | ||
294 | }; | ||
295 | |||
296 | static struct gpio_led dsl502t_leds[] = { | ||
297 | { | ||
298 | .name = "status", | ||
299 | .gpio = 9, | ||
300 | .active_low = 1, | ||
301 | }, | ||
302 | { | ||
303 | .name = "ethernet", | ||
304 | .gpio = 7, | ||
305 | .active_low = 1, | ||
306 | }, | ||
307 | { | ||
308 | .name = "usb", | ||
309 | .gpio = 12, | ||
310 | .active_low = 1, | ||
311 | }, | ||
312 | }; | ||
313 | |||
314 | static struct gpio_led dg834g_leds[] = { | ||
315 | { | ||
316 | .name = "ppp", | ||
317 | .gpio = 6, | ||
318 | .active_low = 1, | ||
319 | }, | ||
320 | { | ||
321 | .name = "status", | ||
322 | .gpio = 7, | ||
323 | .active_low = 1, | ||
324 | }, | ||
325 | { | ||
326 | .name = "adsl", | ||
327 | .gpio = 8, | ||
328 | .active_low = 1, | ||
329 | }, | ||
330 | { | ||
331 | .name = "wifi", | ||
332 | .gpio = 12, | ||
333 | .active_low = 1, | ||
334 | }, | ||
335 | { | ||
336 | .name = "power", | ||
337 | .gpio = 14, | ||
338 | .active_low = 1, | ||
339 | .default_trigger = "default-on", | ||
340 | }, | ||
341 | }; | ||
342 | |||
343 | static struct gpio_led fb_sl_leds[] = { | ||
344 | { | ||
345 | .name = "1", | ||
346 | .gpio = 7, | ||
347 | }, | ||
348 | { | ||
349 | .name = "2", | ||
350 | .gpio = 13, | ||
351 | .active_low = 1, | ||
352 | }, | ||
353 | { | ||
354 | .name = "3", | ||
355 | .gpio = 10, | ||
356 | .active_low = 1, | ||
357 | }, | ||
358 | { | ||
359 | .name = "4", | ||
360 | .gpio = 12, | ||
361 | .active_low = 1, | ||
362 | }, | ||
363 | { | ||
364 | .name = "5", | ||
365 | .gpio = 9, | ||
366 | .active_low = 1, | ||
367 | }, | ||
368 | }; | ||
369 | |||
370 | static struct gpio_led fb_fon_leds[] = { | ||
371 | { | ||
372 | .name = "1", | ||
373 | .gpio = 8, | ||
374 | }, | ||
375 | { | ||
376 | .name = "2", | ||
377 | .gpio = 3, | ||
378 | .active_low = 1, | ||
379 | }, | ||
380 | { | ||
381 | .name = "3", | ||
382 | .gpio = 5, | ||
383 | }, | ||
384 | { | ||
385 | .name = "4", | ||
386 | .gpio = 4, | ||
387 | .active_low = 1, | ||
388 | }, | ||
389 | { | ||
390 | .name = "5", | ||
391 | .gpio = 11, | ||
392 | .active_low = 1, | ||
393 | }, | ||
394 | }; | ||
395 | |||
396 | static struct gpio_led_platform_data ar7_led_data; | ||
397 | |||
398 | static struct platform_device ar7_gpio_leds = { | ||
399 | .name = "leds-gpio", | ||
400 | .id = -1, | ||
401 | .dev = { | ||
402 | .platform_data = &ar7_led_data, | ||
403 | } | ||
404 | }; | ||
405 | |||
406 | static struct platform_device ar7_udc = { | ||
407 | .id = -1, | ||
408 | .name = "ar7_udc", | ||
409 | .resource = usb_res, | ||
410 | .num_resources = ARRAY_SIZE(usb_res), | ||
411 | }; | ||
412 | |||
413 | static inline unsigned char char2hex(char h) | ||
414 | { | ||
415 | switch (h) { | ||
416 | case '0': case '1': case '2': case '3': case '4': | ||
417 | case '5': case '6': case '7': case '8': case '9': | ||
418 | return h - '0'; | ||
419 | case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': | ||
420 | return h - 'A' + 10; | ||
421 | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': | ||
422 | return h - 'a' + 10; | ||
423 | default: | ||
424 | return 0; | ||
425 | } | ||
426 | } | ||
427 | |||
428 | static void cpmac_get_mac(int instance, unsigned char *dev_addr) | ||
429 | { | ||
430 | int i; | ||
431 | char name[5], default_mac[ETH_ALEN], *mac; | ||
432 | |||
433 | mac = NULL; | ||
434 | sprintf(name, "mac%c", 'a' + instance); | ||
435 | mac = prom_getenv(name); | ||
436 | if (!mac) { | ||
437 | sprintf(name, "mac%c", 'a'); | ||
438 | mac = prom_getenv(name); | ||
439 | } | ||
440 | if (!mac) { | ||
441 | random_ether_addr(default_mac); | ||
442 | mac = default_mac; | ||
443 | } | ||
444 | for (i = 0; i < 6; i++) | ||
445 | dev_addr[i] = (char2hex(mac[i * 3]) << 4) + | ||
446 | char2hex(mac[i * 3 + 1]); | ||
447 | } | ||
448 | |||
449 | static void __init detect_leds(void) | ||
450 | { | ||
451 | char *prid, *usb_prod; | ||
452 | |||
453 | /* Default LEDs */ | ||
454 | ar7_led_data.num_leds = ARRAY_SIZE(default_leds); | ||
455 | ar7_led_data.leds = default_leds; | ||
456 | |||
457 | /* FIXME: the whole thing is unreliable */ | ||
458 | prid = prom_getenv("ProductID"); | ||
459 | usb_prod = prom_getenv("usb_prod"); | ||
460 | |||
461 | /* If we can't get the product id from PROM, use the default LEDs */ | ||
462 | if (!prid) | ||
463 | return; | ||
464 | |||
465 | if (strstr(prid, "Fritz_Box_FON")) { | ||
466 | ar7_led_data.num_leds = ARRAY_SIZE(fb_fon_leds); | ||
467 | ar7_led_data.leds = fb_fon_leds; | ||
468 | } else if (strstr(prid, "Fritz_Box_")) { | ||
469 | ar7_led_data.num_leds = ARRAY_SIZE(fb_sl_leds); | ||
470 | ar7_led_data.leds = fb_sl_leds; | ||
471 | } else if ((!strcmp(prid, "AR7RD") || !strcmp(prid, "AR7DB")) | ||
472 | && usb_prod != NULL && strstr(usb_prod, "DSL-502T")) { | ||
473 | ar7_led_data.num_leds = ARRAY_SIZE(dsl502t_leds); | ||
474 | ar7_led_data.leds = dsl502t_leds; | ||
475 | } else if (strstr(prid, "DG834")) { | ||
476 | ar7_led_data.num_leds = ARRAY_SIZE(dg834g_leds); | ||
477 | ar7_led_data.leds = dg834g_leds; | ||
478 | } | ||
479 | } | ||
480 | |||
481 | static int __init ar7_register_devices(void) | ||
482 | { | ||
483 | int res; | ||
484 | static struct uart_port uart_port[2]; | ||
485 | |||
486 | memset(uart_port, 0, sizeof(struct uart_port) * 2); | ||
487 | |||
488 | uart_port[0].type = PORT_16550A; | ||
489 | uart_port[0].line = 0; | ||
490 | uart_port[0].irq = AR7_IRQ_UART0; | ||
491 | uart_port[0].uartclk = ar7_bus_freq() / 2; | ||
492 | uart_port[0].iotype = UPIO_MEM32; | ||
493 | uart_port[0].mapbase = AR7_REGS_UART0; | ||
494 | uart_port[0].membase = ioremap(uart_port[0].mapbase, 256); | ||
495 | uart_port[0].regshift = 2; | ||
496 | res = early_serial_setup(&uart_port[0]); | ||
497 | if (res) | ||
498 | return res; | ||
499 | |||
500 | |||
501 | /* Only TNETD73xx have a second serial port */ | ||
502 | if (ar7_has_second_uart()) { | ||
503 | uart_port[1].type = PORT_16550A; | ||
504 | uart_port[1].line = 1; | ||
505 | uart_port[1].irq = AR7_IRQ_UART1; | ||
506 | uart_port[1].uartclk = ar7_bus_freq() / 2; | ||
507 | uart_port[1].iotype = UPIO_MEM32; | ||
508 | uart_port[1].mapbase = UR8_REGS_UART1; | ||
509 | uart_port[1].membase = ioremap(uart_port[1].mapbase, 256); | ||
510 | uart_port[1].regshift = 2; | ||
511 | res = early_serial_setup(&uart_port[1]); | ||
512 | if (res) | ||
513 | return res; | ||
514 | } | ||
515 | |||
516 | res = platform_device_register(&physmap_flash); | ||
517 | if (res) | ||
518 | return res; | ||
519 | |||
520 | ar7_device_disable(vlynq_low_data.reset_bit); | ||
521 | res = platform_device_register(&vlynq_low); | ||
522 | if (res) | ||
523 | return res; | ||
524 | |||
525 | if (ar7_has_high_vlynq()) { | ||
526 | ar7_device_disable(vlynq_high_data.reset_bit); | ||
527 | res = platform_device_register(&vlynq_high); | ||
528 | if (res) | ||
529 | return res; | ||
530 | } | ||
531 | |||
532 | if (ar7_has_high_cpmac()) { | ||
533 | cpmac_get_mac(1, cpmac_high_data.dev_addr); | ||
534 | res = platform_device_register(&cpmac_high); | ||
535 | if (res) | ||
536 | return res; | ||
537 | } else { | ||
538 | cpmac_low_data.phy_mask = 0xffffffff; | ||
539 | } | ||
540 | |||
541 | cpmac_get_mac(0, cpmac_low_data.dev_addr); | ||
542 | res = platform_device_register(&cpmac_low); | ||
543 | if (res) | ||
544 | return res; | ||
545 | |||
546 | detect_leds(); | ||
547 | res = platform_device_register(&ar7_gpio_leds); | ||
548 | if (res) | ||
549 | return res; | ||
550 | |||
551 | res = platform_device_register(&ar7_udc); | ||
552 | |||
553 | return res; | ||
554 | } | ||
555 | arch_initcall(ar7_register_devices); | ||
diff --git a/arch/mips/ar7/prom.c b/arch/mips/ar7/prom.c new file mode 100644 index 000000000000..a320bceb2f9d --- /dev/null +++ b/arch/mips/ar7/prom.c | |||
@@ -0,0 +1,297 @@ | |||
1 | /* | ||
2 | * Carsten Langgaard, carstenl@mips.com | ||
3 | * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you can distribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License (Version 2) as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | * for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
17 | * | ||
18 | * Putting things on the screen/serial line using YAMONs facilities. | ||
19 | */ | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/serial_reg.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/string.h> | ||
26 | #include <linux/io.h> | ||
27 | #include <asm/bootinfo.h> | ||
28 | |||
29 | #include <asm/mach-ar7/ar7.h> | ||
30 | #include <asm/mach-ar7/prom.h> | ||
31 | |||
32 | #define MAX_ENTRY 80 | ||
33 | |||
34 | struct env_var { | ||
35 | char *name; | ||
36 | char *value; | ||
37 | }; | ||
38 | |||
39 | static struct env_var adam2_env[MAX_ENTRY]; | ||
40 | |||
41 | char *prom_getenv(const char *name) | ||
42 | { | ||
43 | int i; | ||
44 | for (i = 0; (i < MAX_ENTRY) && adam2_env[i].name; i++) | ||
45 | if (!strcmp(name, adam2_env[i].name)) | ||
46 | return adam2_env[i].value; | ||
47 | |||
48 | return NULL; | ||
49 | } | ||
50 | EXPORT_SYMBOL(prom_getenv); | ||
51 | |||
52 | char * __init prom_getcmdline(void) | ||
53 | { | ||
54 | return &(arcs_cmdline[0]); | ||
55 | } | ||
56 | |||
57 | static void __init ar7_init_cmdline(int argc, char *argv[]) | ||
58 | { | ||
59 | char *cp; | ||
60 | int actr; | ||
61 | |||
62 | actr = 1; /* Always ignore argv[0] */ | ||
63 | |||
64 | cp = &(arcs_cmdline[0]); | ||
65 | while (actr < argc) { | ||
66 | strcpy(cp, argv[actr]); | ||
67 | cp += strlen(argv[actr]); | ||
68 | *cp++ = ' '; | ||
69 | actr++; | ||
70 | } | ||
71 | if (cp != &(arcs_cmdline[0])) { | ||
72 | /* get rid of trailing space */ | ||
73 | --cp; | ||
74 | *cp = '\0'; | ||
75 | } | ||
76 | } | ||
77 | |||
78 | struct psbl_rec { | ||
79 | u32 psbl_size; | ||
80 | u32 env_base; | ||
81 | u32 env_size; | ||
82 | u32 ffs_base; | ||
83 | u32 ffs_size; | ||
84 | }; | ||
85 | |||
86 | static __initdata char psp_env_version[] = "TIENV0.8"; | ||
87 | |||
88 | struct psp_env_chunk { | ||
89 | u8 num; | ||
90 | u8 ctrl; | ||
91 | u16 csum; | ||
92 | u8 len; | ||
93 | char data[11]; | ||
94 | } __attribute__ ((packed)); | ||
95 | |||
96 | struct psp_var_map_entry { | ||
97 | u8 num; | ||
98 | char *value; | ||
99 | }; | ||
100 | |||
101 | static struct psp_var_map_entry psp_var_map[] = { | ||
102 | { 1, "cpufrequency" }, | ||
103 | { 2, "memsize" }, | ||
104 | { 3, "flashsize" }, | ||
105 | { 4, "modetty0" }, | ||
106 | { 5, "modetty1" }, | ||
107 | { 8, "maca" }, | ||
108 | { 9, "macb" }, | ||
109 | { 28, "sysfrequency" }, | ||
110 | { 38, "mipsfrequency" }, | ||
111 | }; | ||
112 | |||
113 | /* | ||
114 | |||
115 | Well-known variable (num is looked up in table above for matching variable name) | ||
116 | Example: cpufrequency=211968000 | ||
117 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- | ||
118 | | 01 |CTRL|CHECKSUM | 01 | _2 | _1 | _1 | _9 | _6 | _8 | _0 | _0 | _0 | \0 | FF | ||
119 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- | ||
120 | |||
121 | Name=Value pair in a single chunk | ||
122 | Example: NAME=VALUE | ||
123 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- | ||
124 | | 00 |CTRL|CHECKSUM | 01 | _N | _A | _M | _E | _0 | _V | _A | _L | _U | _E | \0 | ||
125 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- | ||
126 | |||
127 | Name=Value pair in 2 chunks (len is the number of chunks) | ||
128 | Example: bootloaderVersion=1.3.7.15 | ||
129 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- | ||
130 | | 00 |CTRL|CHECKSUM | 02 | _b | _o | _o | _t | _l | _o | _a | _d | _e | _r | _V | ||
131 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- | ||
132 | | _e | _r | _s | _i | _o | _n | \0 | _1 | _. | _3 | _. | _7 | _. | _1 | _5 | \0 | ||
133 | +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+--- | ||
134 | |||
135 | Data is padded with 0xFF | ||
136 | |||
137 | */ | ||
138 | |||
139 | #define PSP_ENV_SIZE 4096 | ||
140 | |||
141 | static char psp_env_data[PSP_ENV_SIZE] = { 0, }; | ||
142 | |||
143 | static char * __init lookup_psp_var_map(u8 num) | ||
144 | { | ||
145 | int i; | ||
146 | |||
147 | for (i = 0; i < sizeof(psp_var_map); i++) | ||
148 | if (psp_var_map[i].num == num) | ||
149 | return psp_var_map[i].value; | ||
150 | |||
151 | return NULL; | ||
152 | } | ||
153 | |||
154 | static void __init add_adam2_var(char *name, char *value) | ||
155 | { | ||
156 | int i; | ||
157 | for (i = 0; i < MAX_ENTRY; i++) { | ||
158 | if (!adam2_env[i].name) { | ||
159 | adam2_env[i].name = name; | ||
160 | adam2_env[i].value = value; | ||
161 | return; | ||
162 | } else if (!strcmp(adam2_env[i].name, name)) { | ||
163 | adam2_env[i].value = value; | ||
164 | return; | ||
165 | } | ||
166 | } | ||
167 | } | ||
168 | |||
169 | static int __init parse_psp_env(void *psp_env_base) | ||
170 | { | ||
171 | int i, n; | ||
172 | char *name, *value; | ||
173 | struct psp_env_chunk *chunks = (struct psp_env_chunk *)psp_env_data; | ||
174 | |||
175 | memcpy_fromio(chunks, psp_env_base, PSP_ENV_SIZE); | ||
176 | |||
177 | i = 1; | ||
178 | n = PSP_ENV_SIZE / sizeof(struct psp_env_chunk); | ||
179 | while (i < n) { | ||
180 | if ((chunks[i].num == 0xff) || ((i + chunks[i].len) > n)) | ||
181 | break; | ||
182 | value = chunks[i].data; | ||
183 | if (chunks[i].num) { | ||
184 | name = lookup_psp_var_map(chunks[i].num); | ||
185 | } else { | ||
186 | name = value; | ||
187 | value += strlen(name) + 1; | ||
188 | } | ||
189 | if (name) | ||
190 | add_adam2_var(name, value); | ||
191 | i += chunks[i].len; | ||
192 | } | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static void __init ar7_init_env(struct env_var *env) | ||
197 | { | ||
198 | int i; | ||
199 | struct psbl_rec *psbl = (struct psbl_rec *)(KSEG1ADDR(0x14000300)); | ||
200 | void *psp_env = (void *)KSEG1ADDR(psbl->env_base); | ||
201 | |||
202 | if (strcmp(psp_env, psp_env_version) == 0) { | ||
203 | parse_psp_env(psp_env); | ||
204 | } else { | ||
205 | for (i = 0; i < MAX_ENTRY; i++, env++) | ||
206 | if (env->name) | ||
207 | add_adam2_var(env->name, env->value); | ||
208 | } | ||
209 | } | ||
210 | |||
211 | static void __init console_config(void) | ||
212 | { | ||
213 | #ifdef CONFIG_SERIAL_8250_CONSOLE | ||
214 | char console_string[40]; | ||
215 | int baud = 0; | ||
216 | char parity = '\0', bits = '\0', flow = '\0'; | ||
217 | char *s, *p; | ||
218 | |||
219 | if (strstr(prom_getcmdline(), "console=")) | ||
220 | return; | ||
221 | |||
222 | #ifdef CONFIG_KGDB | ||
223 | if (!strstr(prom_getcmdline(), "nokgdb")) { | ||
224 | strcat(prom_getcmdline(), " console=kgdb"); | ||
225 | kgdb_enabled = 1; | ||
226 | return; | ||
227 | } | ||
228 | #endif | ||
229 | |||
230 | s = prom_getenv("modetty0"); | ||
231 | if (s) { | ||
232 | baud = simple_strtoul(s, &p, 10); | ||
233 | s = p; | ||
234 | if (*s == ',') | ||
235 | s++; | ||
236 | if (*s) | ||
237 | parity = *s++; | ||
238 | if (*s == ',') | ||
239 | s++; | ||
240 | if (*s) | ||
241 | bits = *s++; | ||
242 | if (*s == ',') | ||
243 | s++; | ||
244 | if (*s == 'h') | ||
245 | flow = 'r'; | ||
246 | } | ||
247 | |||
248 | if (baud == 0) | ||
249 | baud = 38400; | ||
250 | if (parity != 'n' && parity != 'o' && parity != 'e') | ||
251 | parity = 'n'; | ||
252 | if (bits != '7' && bits != '8') | ||
253 | bits = '8'; | ||
254 | |||
255 | if (flow == 'r') | ||
256 | sprintf(console_string, " console=ttyS0,%d%c%c%c", baud, | ||
257 | parity, bits, flow); | ||
258 | else | ||
259 | sprintf(console_string, " console=ttyS0,%d%c%c", baud, parity, | ||
260 | bits); | ||
261 | strcat(prom_getcmdline(), console_string); | ||
262 | #endif | ||
263 | } | ||
264 | |||
265 | void __init prom_init(void) | ||
266 | { | ||
267 | ar7_init_cmdline(fw_arg0, (char **)fw_arg1); | ||
268 | ar7_init_env((struct env_var *)fw_arg2); | ||
269 | console_config(); | ||
270 | } | ||
271 | |||
272 | #define PORT(offset) (KSEG1ADDR(AR7_REGS_UART0 + (offset * 4))) | ||
273 | static inline unsigned int serial_in(int offset) | ||
274 | { | ||
275 | return readl((void *)PORT(offset)); | ||
276 | } | ||
277 | |||
278 | static inline void serial_out(int offset, int value) | ||
279 | { | ||
280 | writel(value, (void *)PORT(offset)); | ||
281 | } | ||
282 | |||
283 | char prom_getchar(void) | ||
284 | { | ||
285 | while (!(serial_in(UART_LSR) & UART_LSR_DR)) | ||
286 | ; | ||
287 | return serial_in(UART_RX); | ||
288 | } | ||
289 | |||
290 | int prom_putchar(char c) | ||
291 | { | ||
292 | while ((serial_in(UART_LSR) & UART_LSR_TEMT) == 0) | ||
293 | ; | ||
294 | serial_out(UART_TX, c); | ||
295 | return 1; | ||
296 | } | ||
297 | |||
diff --git a/arch/mips/ar7/setup.c b/arch/mips/ar7/setup.c new file mode 100644 index 000000000000..6ebb5f16d967 --- /dev/null +++ b/arch/mips/ar7/setup.c | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * Carsten Langgaard, carstenl@mips.com | ||
3 | * Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you can distribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License (Version 2) as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | * for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
17 | */ | ||
18 | #include <linux/version.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/ioport.h> | ||
21 | #include <linux/pm.h> | ||
22 | #include <linux/time.h> | ||
23 | |||
24 | #include <asm/reboot.h> | ||
25 | #include <asm/mach-ar7/ar7.h> | ||
26 | #include <asm/mach-ar7/prom.h> | ||
27 | |||
28 | static void ar7_machine_restart(char *command) | ||
29 | { | ||
30 | u32 *softres_reg = ioremap(AR7_REGS_RESET + | ||
31 | AR7_RESET_SOFTWARE, 1); | ||
32 | writel(1, softres_reg); | ||
33 | } | ||
34 | |||
35 | static void ar7_machine_halt(void) | ||
36 | { | ||
37 | while (1) | ||
38 | ; | ||
39 | } | ||
40 | |||
41 | static void ar7_machine_power_off(void) | ||
42 | { | ||
43 | u32 *power_reg = (u32 *)ioremap(AR7_REGS_POWER, 1); | ||
44 | u32 power_state = readl(power_reg) | (3 << 30); | ||
45 | writel(power_state, power_reg); | ||
46 | ar7_machine_halt(); | ||
47 | } | ||
48 | |||
49 | const char *get_system_type(void) | ||
50 | { | ||
51 | u16 chip_id = ar7_chip_id(); | ||
52 | switch (chip_id) { | ||
53 | case AR7_CHIP_7300: | ||
54 | return "TI AR7 (TNETD7300)"; | ||
55 | case AR7_CHIP_7100: | ||
56 | return "TI AR7 (TNETD7100)"; | ||
57 | case AR7_CHIP_7200: | ||
58 | return "TI AR7 (TNETD7200)"; | ||
59 | default: | ||
60 | return "TI AR7 (Unknown)"; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | static int __init ar7_init_console(void) | ||
65 | { | ||
66 | return 0; | ||
67 | } | ||
68 | console_initcall(ar7_init_console); | ||
69 | |||
70 | /* | ||
71 | * Initializes basic routines and structures pointers, memory size (as | ||
72 | * given by the bios and saves the command line. | ||
73 | */ | ||
74 | |||
75 | void __init plat_mem_setup(void) | ||
76 | { | ||
77 | unsigned long io_base; | ||
78 | |||
79 | _machine_restart = ar7_machine_restart; | ||
80 | _machine_halt = ar7_machine_halt; | ||
81 | pm_power_off = ar7_machine_power_off; | ||
82 | panic_timeout = 3; | ||
83 | |||
84 | io_base = (unsigned long)ioremap(AR7_REGS_BASE, 0x10000); | ||
85 | if (!io_base) | ||
86 | panic("Can't remap IO base!\n"); | ||
87 | set_io_port_base(io_base); | ||
88 | |||
89 | prom_meminit(); | ||
90 | |||
91 | printk(KERN_INFO "%s, ID: 0x%04x, Revision: 0x%02x\n", | ||
92 | get_system_type(), | ||
93 | ar7_chip_id(), ar7_chip_rev()); | ||
94 | } | ||
diff --git a/arch/mips/ar7/time.c b/arch/mips/ar7/time.c new file mode 100644 index 000000000000..a1fba894daa2 --- /dev/null +++ b/arch/mips/ar7/time.c | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * Carsten Langgaard, carstenl@mips.com | ||
3 | * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you can distribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License (Version 2) as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | * for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along | ||
15 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
17 | * | ||
18 | * Setting up the clock on the MIPS boards. | ||
19 | */ | ||
20 | |||
21 | #include <linux/init.h> | ||
22 | #include <linux/time.h> | ||
23 | |||
24 | #include <asm/time.h> | ||
25 | #include <asm/mach-ar7/ar7.h> | ||
26 | |||
27 | void __init plat_time_init(void) | ||
28 | { | ||
29 | mips_hpt_frequency = ar7_cpu_freq() / 2; | ||
30 | } | ||