Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * tc-init: We assume the TURBOchannel to be up and running so | |
3 | * just probe for Modules and fill in the global data structure | |
4 | * tc_bus. | |
5 | * | |
6 | * This file is subject to the terms and conditions of the GNU General Public | |
7 | * License. See the file "COPYING" in the main directory of this archive | |
8 | * for more details. | |
9 | * | |
10 | * Copyright (c) Harald Koerfgen, 1998 | |
778220f7 | 11 | * Copyright (c) 2001, 2003, 2005 Maciej W. Rozycki |
1da177e4 | 12 | */ |
1da177e4 | 13 | #include <linux/init.h> |
1da177e4 LT |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> | |
a5fc9c0b MR |
16 | #include <linux/string.h> |
17 | #include <linux/types.h> | |
1da177e4 LT |
18 | |
19 | #include <asm/addrspace.h> | |
a5fc9c0b | 20 | #include <asm/bug.h> |
1da177e4 | 21 | #include <asm/errno.h> |
a5fc9c0b MR |
22 | #include <asm/io.h> |
23 | #include <asm/paccess.h> | |
24 | ||
1da177e4 LT |
25 | #include <asm/dec/machtype.h> |
26 | #include <asm/dec/prom.h> | |
27 | #include <asm/dec/tcinfo.h> | |
28 | #include <asm/dec/tcmodule.h> | |
29 | #include <asm/dec/interrupts.h> | |
1da177e4 LT |
30 | |
31 | MODULE_LICENSE("GPL"); | |
32 | slot_info tc_bus[MAX_SLOT]; | |
33 | static int num_tcslots; | |
34 | static tcinfo *info; | |
35 | ||
1da177e4 LT |
36 | /* |
37 | * Interface to the world. Read comment in include/asm-mips/tc.h. | |
38 | */ | |
39 | ||
40 | int search_tc_card(const char *name) | |
41 | { | |
42 | int slot; | |
43 | slot_info *sip; | |
44 | ||
45 | for (slot = 0; slot < num_tcslots; slot++) { | |
46 | sip = &tc_bus[slot]; | |
47 | if ((sip->flags & FREE) && | |
48 | (strncmp(sip->name, name, strlen(name)) == 0)) { | |
49 | return slot; | |
50 | } | |
51 | } | |
52 | ||
53 | return -ENODEV; | |
54 | } | |
55 | ||
56 | void claim_tc_card(int slot) | |
57 | { | |
58 | if (tc_bus[slot].flags & IN_USE) { | |
59 | printk("claim_tc_card: attempting to claim a card already in use\n"); | |
60 | return; | |
61 | } | |
62 | tc_bus[slot].flags &= ~FREE; | |
63 | tc_bus[slot].flags |= IN_USE; | |
64 | } | |
65 | ||
66 | void release_tc_card(int slot) | |
67 | { | |
68 | if (tc_bus[slot].flags & FREE) { | |
69 | printk("release_tc_card: " | |
70 | "attempting to release a card already free\n"); | |
71 | return; | |
72 | } | |
73 | tc_bus[slot].flags &= ~IN_USE; | |
74 | tc_bus[slot].flags |= FREE; | |
75 | } | |
76 | ||
77 | unsigned long get_tc_base_addr(int slot) | |
78 | { | |
79 | return tc_bus[slot].base_addr; | |
80 | } | |
81 | ||
82 | unsigned long get_tc_irq_nr(int slot) | |
83 | { | |
84 | return tc_bus[slot].interrupt; | |
85 | } | |
86 | ||
87 | unsigned long get_tc_speed(void) | |
88 | { | |
89 | return 100000 * (10000 / (unsigned long)info->clk_period); | |
90 | } | |
91 | ||
92 | /* | |
93 | * Probing for TURBOchannel modules | |
94 | */ | |
95 | static void __init tc_probe(unsigned long startaddr, unsigned long size, | |
96 | int slots) | |
97 | { | |
a5fc9c0b | 98 | unsigned long slotaddr; |
1da177e4 LT |
99 | int i, slot, err; |
100 | long offset; | |
a5fc9c0b MR |
101 | u8 pattern[4]; |
102 | volatile u8 *module; | |
1da177e4 LT |
103 | |
104 | for (slot = 0; slot < slots; slot++) { | |
a5fc9c0b MR |
105 | slotaddr = startaddr + slot * size; |
106 | module = ioremap_nocache(slotaddr, size); | |
107 | BUG_ON(!module); | |
1da177e4 LT |
108 | |
109 | offset = OLDCARD; | |
110 | ||
111 | err = 0; | |
112 | err |= get_dbe(pattern[0], module + OLDCARD + TC_PATTERN0); | |
113 | err |= get_dbe(pattern[1], module + OLDCARD + TC_PATTERN1); | |
114 | err |= get_dbe(pattern[2], module + OLDCARD + TC_PATTERN2); | |
115 | err |= get_dbe(pattern[3], module + OLDCARD + TC_PATTERN3); | |
a5fc9c0b MR |
116 | if (err) { |
117 | iounmap(module); | |
1da177e4 | 118 | continue; |
a5fc9c0b | 119 | } |
1da177e4 LT |
120 | |
121 | if (pattern[0] != 0x55 || pattern[1] != 0x00 || | |
122 | pattern[2] != 0xaa || pattern[3] != 0xff) { | |
123 | offset = NEWCARD; | |
124 | ||
125 | err = 0; | |
126 | err |= get_dbe(pattern[0], module + TC_PATTERN0); | |
127 | err |= get_dbe(pattern[1], module + TC_PATTERN1); | |
128 | err |= get_dbe(pattern[2], module + TC_PATTERN2); | |
129 | err |= get_dbe(pattern[3], module + TC_PATTERN3); | |
a5fc9c0b MR |
130 | if (err) { |
131 | iounmap(module); | |
1da177e4 | 132 | continue; |
a5fc9c0b | 133 | } |
1da177e4 LT |
134 | } |
135 | ||
136 | if (pattern[0] != 0x55 || pattern[1] != 0x00 || | |
a5fc9c0b MR |
137 | pattern[2] != 0xaa || pattern[3] != 0xff) { |
138 | iounmap(module); | |
1da177e4 | 139 | continue; |
a5fc9c0b | 140 | } |
1da177e4 | 141 | |
a5fc9c0b MR |
142 | tc_bus[slot].base_addr = slotaddr; |
143 | for (i = 0; i < 8; i++) { | |
1da177e4 LT |
144 | tc_bus[slot].firmware[i] = |
145 | module[TC_FIRM_VER + offset + 4 * i]; | |
146 | tc_bus[slot].vendor[i] = | |
147 | module[TC_VENDOR + offset + 4 * i]; | |
148 | tc_bus[slot].name[i] = | |
149 | module[TC_MODULE + offset + 4 * i]; | |
150 | } | |
151 | tc_bus[slot].firmware[8] = 0; | |
152 | tc_bus[slot].vendor[8] = 0; | |
153 | tc_bus[slot].name[8] = 0; | |
154 | /* | |
155 | * Looks unneccesary, but we may change | |
156 | * TC? in the future | |
157 | */ | |
158 | switch (slot) { | |
159 | case 0: | |
160 | tc_bus[slot].interrupt = dec_interrupt[DEC_IRQ_TC0]; | |
161 | break; | |
162 | case 1: | |
163 | tc_bus[slot].interrupt = dec_interrupt[DEC_IRQ_TC1]; | |
164 | break; | |
165 | case 2: | |
166 | tc_bus[slot].interrupt = dec_interrupt[DEC_IRQ_TC2]; | |
167 | break; | |
168 | /* | |
169 | * Yuck! DS5000/200 onboard devices | |
170 | */ | |
171 | case 5: | |
172 | tc_bus[slot].interrupt = dec_interrupt[DEC_IRQ_TC5]; | |
173 | break; | |
174 | case 6: | |
175 | tc_bus[slot].interrupt = dec_interrupt[DEC_IRQ_TC6]; | |
176 | break; | |
177 | default: | |
178 | tc_bus[slot].interrupt = -1; | |
179 | break; | |
180 | } | |
a5fc9c0b MR |
181 | |
182 | iounmap(module); | |
1da177e4 LT |
183 | } |
184 | } | |
185 | ||
186 | /* | |
187 | * the main entry | |
188 | */ | |
778220f7 | 189 | static int __init tc_init(void) |
1da177e4 LT |
190 | { |
191 | int tc_clock; | |
192 | int i; | |
193 | unsigned long slot0addr; | |
194 | unsigned long slot_size; | |
195 | ||
196 | if (!TURBOCHANNEL) | |
778220f7 | 197 | return 0; |
1da177e4 LT |
198 | |
199 | for (i = 0; i < MAX_SLOT; i++) { | |
200 | tc_bus[i].base_addr = 0; | |
201 | tc_bus[i].name[0] = 0; | |
202 | tc_bus[i].vendor[0] = 0; | |
203 | tc_bus[i].firmware[0] = 0; | |
204 | tc_bus[i].interrupt = -1; | |
205 | tc_bus[i].flags = FREE; | |
206 | } | |
207 | ||
a5fc9c0b MR |
208 | info = rex_gettcinfo(); |
209 | slot0addr = CPHYSADDR((long)rex_slot_address(0)); | |
1da177e4 LT |
210 | |
211 | switch (mips_machtype) { | |
212 | case MACH_DS5000_200: | |
213 | num_tcslots = 7; | |
214 | break; | |
215 | case MACH_DS5000_1XX: | |
216 | case MACH_DS5000_2X0: | |
217 | case MACH_DS5900: | |
218 | num_tcslots = 3; | |
219 | break; | |
220 | case MACH_DS5000_XX: | |
221 | default: | |
222 | num_tcslots = 2; | |
223 | break; | |
224 | } | |
225 | ||
226 | tc_clock = 10000 / info->clk_period; | |
227 | ||
a5fc9c0b MR |
228 | if (info->slot_size && slot0addr) { |
229 | pr_info("TURBOchannel rev. %d at %d.%d MHz (with%s parity)\n", | |
230 | info->revision, tc_clock / 10, tc_clock % 10, | |
231 | info->parity ? "" : "out"); | |
1da177e4 LT |
232 | |
233 | slot_size = info->slot_size << 20; | |
234 | ||
235 | tc_probe(slot0addr, slot_size, num_tcslots); | |
236 | ||
a5fc9c0b MR |
237 | for (i = 0; i < num_tcslots; i++) { |
238 | if (!tc_bus[i].base_addr) | |
239 | continue; | |
240 | pr_info(" slot %d: %s %s %s\n", i, tc_bus[i].vendor, | |
241 | tc_bus[i].name, tc_bus[i].firmware); | |
242 | } | |
1da177e4 | 243 | } |
778220f7 MR |
244 | |
245 | return 0; | |
1da177e4 LT |
246 | } |
247 | ||
248 | subsys_initcall(tc_init); | |
249 | ||
250 | EXPORT_SYMBOL(search_tc_card); | |
251 | EXPORT_SYMBOL(claim_tc_card); | |
252 | EXPORT_SYMBOL(release_tc_card); | |
253 | EXPORT_SYMBOL(get_tc_base_addr); | |
254 | EXPORT_SYMBOL(get_tc_irq_nr); | |
255 | EXPORT_SYMBOL(get_tc_speed); |