ccbda48546748cba5a9d3f0ddd8e5edb037207c8
[openwrt.git] / target / linux / atheros / files / arch / mips / atheros / board.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2003 Atheros Communications, Inc.,  All Rights Reserved.
7  * Copyright (C) 2006 FON Technology, SL.
8  * Copyright (C) 2006 Imre Kaloz <kaloz@openwrt.org>
9  * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
10  */
11
12 /*
13  * Platform devices for Atheros SoCs
14  */
15
16 #include <linux/autoconf.h>
17 #include <linux/init.h>
18 #include <linux/module.h>
19 #include <linux/types.h>
20 #include <linux/string.h>
21 #include <linux/platform_device.h>
22 #include <linux/kernel.h>
23 #include <linux/serial.h>
24 #include <linux/serial_core.h>
25 #include <asm/bootinfo.h>
26 #include <asm/irq_cpu.h>
27 #include <asm/io.h>
28 #include "ar531x.h"
29
30 char *board_config, *radio_config;
31
32 extern int early_serial_setup(struct uart_port *port);
33
34 static u8 *find_board_config(char *flash_limit)
35 {
36         char *addr;
37         int found = 0;
38
39         for (addr = (char *) (flash_limit - 0x1000);
40                 addr >= (char *) (flash_limit - 0x30000);
41                 addr -= 0x1000) {
42
43                 if ( *(int *)addr == 0x35333131) {
44                         /* config magic found */
45                         found = 1;
46                         break;
47                 }
48         }
49
50         if (!found) {
51                 printk("WARNING: No board configuration data found!\n");
52                 addr = NULL;
53         }
54         
55         return addr;
56 }
57
58 static u8 *find_radio_config(char *flash_limit, char *board_config)
59 {
60         int dataFound;
61         u32 radio_config;
62         
63         /* 
64          * Now find the start of Radio Configuration data, using heuristics:
65          * Search forward from Board Configuration data by 0x1000 bytes
66          * at a time until we find non-0xffffffff.
67          */
68         dataFound = 0;
69         for (radio_config = (u32) board_config + 0x1000;
70              (radio_config < (u32) flash_limit);
71              radio_config += 0x1000) {
72                 if (*(int *)radio_config != 0xffffffff) {
73                         dataFound = 1;
74                         break;
75                 }
76         }
77
78 #ifdef CONFIG_ATHEROS_AR5315
79         if (!dataFound) { /* AR2316 relocates radio config to new location */
80             for (radio_config = (u32) board_config + 0xf8;
81                 (radio_config < (u32) flash_limit - 0x1000 + 0xf8);
82                          radio_config += 0x1000) {
83                         if (*(int *)radio_config != 0xffffffff) {
84                                 dataFound = 1;
85                                 break;
86                         }
87             }
88         }
89 #endif
90
91         if (!dataFound) {
92                 printk("Could not find Radio Configuration data\n");
93                 radio_config = 0;
94         }
95
96         return (u8 *) radio_config;
97 }
98
99 int __init ar531x_find_config(char *flash_limit)
100 {
101         unsigned int rcfg_size;
102         char *bcfg, *rcfg;
103
104         /* Copy the board and radio data to RAM, because with the new
105          * spiflash driver, accessing the mapped memory directly is no
106          * longer safe */
107
108         bcfg = find_board_config(flash_limit);
109         if (!bcfg)
110                 return -ENODEV;
111
112         board_config = kzalloc(BOARD_CONFIG_BUFSZ, GFP_KERNEL);
113         memcpy(board_config, bcfg, 0x100);
114
115         /* Radio config starts 0x100 bytes after board config, regardless
116          * of what the physical layout on the flash chip looks like */
117
118         rcfg = find_radio_config(flash_limit, bcfg);
119         if (!rcfg)
120                 return -ENODEV;
121
122         radio_config = board_config + 0x100 + ((rcfg - bcfg) & 0xfff);
123         printk("Radio config found at offset 0x%x(0x%x)\n", rcfg - bcfg, radio_config - board_config);
124         rcfg_size = BOARD_CONFIG_BUFSZ - ((rcfg - bcfg) & (BOARD_CONFIG_BUFSZ - 1));
125         memcpy(radio_config, rcfg, rcfg_size);
126         
127         return 0;
128 }
129
130 void __init serial_setup(unsigned long mapbase, unsigned int uartclk)
131 {
132         struct uart_port s;
133         
134         memset(&s, 0, sizeof(s));
135
136         s.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST;
137         s.iotype = UPIO_MEM;
138         s.irq = AR531X_MISC_IRQ_UART0;
139         s.regshift = 2;
140         s.mapbase = mapbase;
141         s.uartclk = uartclk;
142         s.membase = (void __iomem *)s.mapbase;
143
144         early_serial_setup(&s);
145 }
146
147 void __init plat_mem_setup(void)
148 {
149         DO_AR5312(ar5312_plat_setup();)
150         DO_AR5315(ar5315_plat_setup();)
151
152         /* Disable data watchpoints */
153         write_c0_watchlo0(0);
154 }
155
156 const char *get_system_type(void)
157 {
158         switch (mips_machtype) {
159 #ifdef CONFIG_ATHEROS_AR5312
160         case MACH_ATHEROS_AR5312:
161                 return "Atheros AR5312";
162
163         case MACH_ATHEROS_AR2312:
164                 return "Atheros AR2312";
165                 
166         case MACH_ATHEROS_AR2313:
167                 return "Atheros AR2313";
168 #endif
169 #ifdef CONFIG_ATHEROS_AR5315
170         case MACH_ATHEROS_AR2315:
171                 return "Atheros AR2315";
172         case MACH_ATHEROS_AR2316:
173                 return "Atheros AR2316";
174         case MACH_ATHEROS_AR2317:
175                 return "Atheros AR2317";
176         case MACH_ATHEROS_AR2318:
177                 return "Atheros AR2318";
178 #endif
179         }
180         return "Atheros (unknown)";
181 }
182
183 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24))
184 void __init plat_timer_setup(struct irqaction *irq)
185 {
186         unsigned int count;
187
188         /* Usually irq is timer_irqaction (timer_interrupt) */
189         setup_irq(AR531X_IRQ_CPU_CLOCK, irq);
190
191         /* to generate the first CPU timer interrupt */
192         count = read_c0_count();
193         write_c0_compare(count + 1000);
194 }
195 #endif
196
197 asmlinkage void plat_irq_dispatch(void)
198 {
199         DO_AR5312(ar5312_irq_dispatch();)
200         DO_AR5315(ar5315_irq_dispatch();)
201 }
202
203 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
204 void (*board_time_init)(void);
205 void __init plat_time_init(void) {
206     board_time_init();
207 }
208 #endif
209
210 void __init arch_init_irq(void)
211 {
212         clear_c0_status(ST0_IM);
213         mips_cpu_irq_init();
214
215         /* Initialize interrupt controllers */
216         DO_AR5312(ar5312_misc_intr_init(AR531X_MISC_IRQ_BASE);)
217         DO_AR5315(ar5315_misc_intr_init(AR531X_MISC_IRQ_BASE);)
218 }