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