xref: /OK3568_Linux_fs/kernel/drivers/nubus/nubus.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *	Macintosh Nubus Interface Code
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *      Originally by Alan Cox
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *      Mostly rewritten by David Huggins-Daines, C. Scott Ananian,
8*4882a593Smuzhiyun  *      and others.
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include <linux/types.h>
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/string.h>
14*4882a593Smuzhiyun #include <linux/nubus.h>
15*4882a593Smuzhiyun #include <linux/errno.h>
16*4882a593Smuzhiyun #include <linux/init.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/seq_file.h>
19*4882a593Smuzhiyun #include <linux/slab.h>
20*4882a593Smuzhiyun #include <asm/setup.h>
21*4882a593Smuzhiyun #include <asm/page.h>
22*4882a593Smuzhiyun #include <asm/hwtest.h>
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun /* Constants */
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun /* This is, of course, the size in bytelanes, rather than the size in
27*4882a593Smuzhiyun    actual bytes */
28*4882a593Smuzhiyun #define FORMAT_BLOCK_SIZE 20
29*4882a593Smuzhiyun #define ROM_DIR_OFFSET 0x24
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #define NUBUS_TEST_PATTERN 0x5A932BC7
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun /* Globals */
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun LIST_HEAD(nubus_func_rsrcs);
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /* Meaning of "bytelanes":
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun    The card ROM may appear on any or all bytes of each long word in
40*4882a593Smuzhiyun    NuBus memory.  The low 4 bits of the "map" value found in the
41*4882a593Smuzhiyun    format block (at the top of the slot address space, as well as at
42*4882a593Smuzhiyun    the top of the MacOS ROM) tells us which bytelanes, i.e. which byte
43*4882a593Smuzhiyun    offsets within each longword, are valid.  Thus:
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun    A map of 0x0f, as found in the MacOS ROM, means that all bytelanes
46*4882a593Smuzhiyun    are valid.
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun    A map of 0xf0 means that no bytelanes are valid (We pray that we
49*4882a593Smuzhiyun    will never encounter this, but stranger things have happened)
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun    A map of 0xe1 means that only the MSB of each long word is actually
52*4882a593Smuzhiyun    part of the card ROM.  (We hope to never encounter NuBus on a
53*4882a593Smuzhiyun    little-endian machine.  Again, stranger things have happened)
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun    A map of 0x78 means that only the LSB of each long word is valid.
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun    Etcetera, etcetera.  Hopefully this clears up some confusion over
58*4882a593Smuzhiyun    what the following code actually does.  */
59*4882a593Smuzhiyun 
not_useful(void * p,int map)60*4882a593Smuzhiyun static inline int not_useful(void *p, int map)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	unsigned long pv = (unsigned long)p;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	pv &= 3;
65*4882a593Smuzhiyun 	if (map & (1 << pv))
66*4882a593Smuzhiyun 		return 0;
67*4882a593Smuzhiyun 	return 1;
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun 
nubus_get_rom(unsigned char ** ptr,int len,int map)70*4882a593Smuzhiyun static unsigned long nubus_get_rom(unsigned char **ptr, int len, int map)
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun 	/* This will hold the result */
73*4882a593Smuzhiyun 	unsigned long v = 0;
74*4882a593Smuzhiyun 	unsigned char *p = *ptr;
75*4882a593Smuzhiyun 
76*4882a593Smuzhiyun 	while (len) {
77*4882a593Smuzhiyun 		v <<= 8;
78*4882a593Smuzhiyun 		while (not_useful(p, map))
79*4882a593Smuzhiyun 			p++;
80*4882a593Smuzhiyun 		v |= *p++;
81*4882a593Smuzhiyun 		len--;
82*4882a593Smuzhiyun 	}
83*4882a593Smuzhiyun 	*ptr = p;
84*4882a593Smuzhiyun 	return v;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
nubus_rewind(unsigned char ** ptr,int len,int map)87*4882a593Smuzhiyun static void nubus_rewind(unsigned char **ptr, int len, int map)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun 	unsigned char *p = *ptr;
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	while (len) {
92*4882a593Smuzhiyun 		do {
93*4882a593Smuzhiyun 			p--;
94*4882a593Smuzhiyun 		} while (not_useful(p, map));
95*4882a593Smuzhiyun 		len--;
96*4882a593Smuzhiyun 	}
97*4882a593Smuzhiyun 	*ptr = p;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun 
nubus_advance(unsigned char ** ptr,int len,int map)100*4882a593Smuzhiyun static void nubus_advance(unsigned char **ptr, int len, int map)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun 	unsigned char *p = *ptr;
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	while (len) {
105*4882a593Smuzhiyun 		while (not_useful(p, map))
106*4882a593Smuzhiyun 			p++;
107*4882a593Smuzhiyun 		p++;
108*4882a593Smuzhiyun 		len--;
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun 	*ptr = p;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
nubus_move(unsigned char ** ptr,int len,int map)113*4882a593Smuzhiyun static void nubus_move(unsigned char **ptr, int len, int map)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	unsigned long slot_space = (unsigned long)*ptr & 0xFF000000;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	if (len > 0)
118*4882a593Smuzhiyun 		nubus_advance(ptr, len, map);
119*4882a593Smuzhiyun 	else if (len < 0)
120*4882a593Smuzhiyun 		nubus_rewind(ptr, -len, map);
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	if (((unsigned long)*ptr & 0xFF000000) != slot_space)
123*4882a593Smuzhiyun 		pr_err("%s: moved out of slot address space!\n", __func__);
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun /* Now, functions to read the sResource tree */
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun /* Each sResource entry consists of a 1-byte ID and a 3-byte data
129*4882a593Smuzhiyun    field.  If that data field contains an offset, then obviously we
130*4882a593Smuzhiyun    have to expand it from a 24-bit signed number to a 32-bit signed
131*4882a593Smuzhiyun    number. */
132*4882a593Smuzhiyun 
nubus_expand32(long foo)133*4882a593Smuzhiyun static inline long nubus_expand32(long foo)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	if (foo & 0x00800000)	/* 24bit negative */
136*4882a593Smuzhiyun 		foo |= 0xFF000000;
137*4882a593Smuzhiyun 	return foo;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun 
nubus_rom_addr(int slot)140*4882a593Smuzhiyun static inline void *nubus_rom_addr(int slot)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun 	/*
143*4882a593Smuzhiyun 	 *	Returns the first byte after the card. We then walk
144*4882a593Smuzhiyun 	 *	backwards to get the lane register and the config
145*4882a593Smuzhiyun 	 */
146*4882a593Smuzhiyun 	return (void *)(0xF1000000 + (slot << 24));
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun 
nubus_dirptr(const struct nubus_dirent * nd)149*4882a593Smuzhiyun unsigned char *nubus_dirptr(const struct nubus_dirent *nd)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun 	unsigned char *p = nd->base;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	/* Essentially, just step over the bytelanes using whatever
154*4882a593Smuzhiyun 	   offset we might have found */
155*4882a593Smuzhiyun 	nubus_move(&p, nubus_expand32(nd->data), nd->mask);
156*4882a593Smuzhiyun 	/* And return the value */
157*4882a593Smuzhiyun 	return p;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun /* These two are for pulling resource data blocks (i.e. stuff that's
161*4882a593Smuzhiyun    pointed to with offsets) out of the card ROM. */
162*4882a593Smuzhiyun 
nubus_get_rsrc_mem(void * dest,const struct nubus_dirent * dirent,unsigned int len)163*4882a593Smuzhiyun void nubus_get_rsrc_mem(void *dest, const struct nubus_dirent *dirent,
164*4882a593Smuzhiyun 			unsigned int len)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun 	unsigned char *t = dest;
167*4882a593Smuzhiyun 	unsigned char *p = nubus_dirptr(dirent);
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	while (len) {
170*4882a593Smuzhiyun 		*t++ = nubus_get_rom(&p, 1, dirent->mask);
171*4882a593Smuzhiyun 		len--;
172*4882a593Smuzhiyun 	}
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_get_rsrc_mem);
175*4882a593Smuzhiyun 
nubus_get_rsrc_str(char * dest,const struct nubus_dirent * dirent,unsigned int len)176*4882a593Smuzhiyun unsigned int nubus_get_rsrc_str(char *dest, const struct nubus_dirent *dirent,
177*4882a593Smuzhiyun 				unsigned int len)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun 	char *t = dest;
180*4882a593Smuzhiyun 	unsigned char *p = nubus_dirptr(dirent);
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	while (len > 1) {
183*4882a593Smuzhiyun 		unsigned char c = nubus_get_rom(&p, 1, dirent->mask);
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 		if (!c)
186*4882a593Smuzhiyun 			break;
187*4882a593Smuzhiyun 		*t++ = c;
188*4882a593Smuzhiyun 		len--;
189*4882a593Smuzhiyun 	}
190*4882a593Smuzhiyun 	if (len > 0)
191*4882a593Smuzhiyun 		*t = '\0';
192*4882a593Smuzhiyun 	return t - dest;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_get_rsrc_str);
195*4882a593Smuzhiyun 
nubus_seq_write_rsrc_mem(struct seq_file * m,const struct nubus_dirent * dirent,unsigned int len)196*4882a593Smuzhiyun void nubus_seq_write_rsrc_mem(struct seq_file *m,
197*4882a593Smuzhiyun 			      const struct nubus_dirent *dirent,
198*4882a593Smuzhiyun 			      unsigned int len)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun 	unsigned long buf[32];
201*4882a593Smuzhiyun 	unsigned int buf_size = sizeof(buf);
202*4882a593Smuzhiyun 	unsigned char *p = nubus_dirptr(dirent);
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	/* If possible, write out full buffers */
205*4882a593Smuzhiyun 	while (len >= buf_size) {
206*4882a593Smuzhiyun 		unsigned int i;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 		for (i = 0; i < ARRAY_SIZE(buf); i++)
209*4882a593Smuzhiyun 			buf[i] = nubus_get_rom(&p, sizeof(buf[0]),
210*4882a593Smuzhiyun 					       dirent->mask);
211*4882a593Smuzhiyun 		seq_write(m, buf, buf_size);
212*4882a593Smuzhiyun 		len -= buf_size;
213*4882a593Smuzhiyun 	}
214*4882a593Smuzhiyun 	/* If not, write out individual bytes */
215*4882a593Smuzhiyun 	while (len--)
216*4882a593Smuzhiyun 		seq_putc(m, nubus_get_rom(&p, 1, dirent->mask));
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun 
nubus_get_root_dir(const struct nubus_board * board,struct nubus_dir * dir)219*4882a593Smuzhiyun int nubus_get_root_dir(const struct nubus_board *board,
220*4882a593Smuzhiyun 		       struct nubus_dir *dir)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun 	dir->ptr = dir->base = board->directory;
223*4882a593Smuzhiyun 	dir->done = 0;
224*4882a593Smuzhiyun 	dir->mask = board->lanes;
225*4882a593Smuzhiyun 	return 0;
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_get_root_dir);
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun /* This is a slyly renamed version of the above */
nubus_get_func_dir(const struct nubus_rsrc * fres,struct nubus_dir * dir)230*4882a593Smuzhiyun int nubus_get_func_dir(const struct nubus_rsrc *fres, struct nubus_dir *dir)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun 	dir->ptr = dir->base = fres->directory;
233*4882a593Smuzhiyun 	dir->done = 0;
234*4882a593Smuzhiyun 	dir->mask = fres->board->lanes;
235*4882a593Smuzhiyun 	return 0;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_get_func_dir);
238*4882a593Smuzhiyun 
nubus_get_board_dir(const struct nubus_board * board,struct nubus_dir * dir)239*4882a593Smuzhiyun int nubus_get_board_dir(const struct nubus_board *board,
240*4882a593Smuzhiyun 			struct nubus_dir *dir)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	struct nubus_dirent ent;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	dir->ptr = dir->base = board->directory;
245*4882a593Smuzhiyun 	dir->done = 0;
246*4882a593Smuzhiyun 	dir->mask = board->lanes;
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	/* Now dereference it (the first directory is always the board
249*4882a593Smuzhiyun 	   directory) */
250*4882a593Smuzhiyun 	if (nubus_readdir(dir, &ent) == -1)
251*4882a593Smuzhiyun 		return -1;
252*4882a593Smuzhiyun 	if (nubus_get_subdir(&ent, dir) == -1)
253*4882a593Smuzhiyun 		return -1;
254*4882a593Smuzhiyun 	return 0;
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_get_board_dir);
257*4882a593Smuzhiyun 
nubus_get_subdir(const struct nubus_dirent * ent,struct nubus_dir * dir)258*4882a593Smuzhiyun int nubus_get_subdir(const struct nubus_dirent *ent,
259*4882a593Smuzhiyun 		     struct nubus_dir *dir)
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun 	dir->ptr = dir->base = nubus_dirptr(ent);
262*4882a593Smuzhiyun 	dir->done = 0;
263*4882a593Smuzhiyun 	dir->mask = ent->mask;
264*4882a593Smuzhiyun 	return 0;
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_get_subdir);
267*4882a593Smuzhiyun 
nubus_readdir(struct nubus_dir * nd,struct nubus_dirent * ent)268*4882a593Smuzhiyun int nubus_readdir(struct nubus_dir *nd, struct nubus_dirent *ent)
269*4882a593Smuzhiyun {
270*4882a593Smuzhiyun 	u32 resid;
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	if (nd->done)
273*4882a593Smuzhiyun 		return -1;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	/* Do this first, otherwise nubus_rewind & co are off by 4 */
276*4882a593Smuzhiyun 	ent->base = nd->ptr;
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	/* This moves nd->ptr forward */
279*4882a593Smuzhiyun 	resid = nubus_get_rom(&nd->ptr, 4, nd->mask);
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	/* EOL marker, as per the Apple docs */
282*4882a593Smuzhiyun 	if ((resid & 0xff000000) == 0xff000000) {
283*4882a593Smuzhiyun 		/* Mark it as done */
284*4882a593Smuzhiyun 		nd->done = 1;
285*4882a593Smuzhiyun 		return -1;
286*4882a593Smuzhiyun 	}
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	/* First byte is the resource ID */
289*4882a593Smuzhiyun 	ent->type = resid >> 24;
290*4882a593Smuzhiyun 	/* Low 3 bytes might contain data (or might not) */
291*4882a593Smuzhiyun 	ent->data = resid & 0xffffff;
292*4882a593Smuzhiyun 	ent->mask = nd->mask;
293*4882a593Smuzhiyun 	return 0;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_readdir);
296*4882a593Smuzhiyun 
nubus_rewinddir(struct nubus_dir * dir)297*4882a593Smuzhiyun int nubus_rewinddir(struct nubus_dir *dir)
298*4882a593Smuzhiyun {
299*4882a593Smuzhiyun 	dir->ptr = dir->base;
300*4882a593Smuzhiyun 	dir->done = 0;
301*4882a593Smuzhiyun 	return 0;
302*4882a593Smuzhiyun }
303*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_rewinddir);
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun /* Driver interface functions, more or less like in pci.c */
306*4882a593Smuzhiyun 
nubus_first_rsrc_or_null(void)307*4882a593Smuzhiyun struct nubus_rsrc *nubus_first_rsrc_or_null(void)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun 	return list_first_entry_or_null(&nubus_func_rsrcs, struct nubus_rsrc,
310*4882a593Smuzhiyun 					list);
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_first_rsrc_or_null);
313*4882a593Smuzhiyun 
nubus_next_rsrc_or_null(struct nubus_rsrc * from)314*4882a593Smuzhiyun struct nubus_rsrc *nubus_next_rsrc_or_null(struct nubus_rsrc *from)
315*4882a593Smuzhiyun {
316*4882a593Smuzhiyun 	if (list_is_last(&from->list, &nubus_func_rsrcs))
317*4882a593Smuzhiyun 		return NULL;
318*4882a593Smuzhiyun 	return list_next_entry(from, list);
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_next_rsrc_or_null);
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun int
nubus_find_rsrc(struct nubus_dir * dir,unsigned char rsrc_type,struct nubus_dirent * ent)323*4882a593Smuzhiyun nubus_find_rsrc(struct nubus_dir *dir, unsigned char rsrc_type,
324*4882a593Smuzhiyun 		struct nubus_dirent *ent)
325*4882a593Smuzhiyun {
326*4882a593Smuzhiyun 	while (nubus_readdir(dir, ent) != -1) {
327*4882a593Smuzhiyun 		if (ent->type == rsrc_type)
328*4882a593Smuzhiyun 			return 0;
329*4882a593Smuzhiyun 	}
330*4882a593Smuzhiyun 	return -1;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun EXPORT_SYMBOL(nubus_find_rsrc);
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun /* Initialization functions - decide which slots contain stuff worth
335*4882a593Smuzhiyun    looking at, and print out lots and lots of information from the
336*4882a593Smuzhiyun    resource blocks. */
337*4882a593Smuzhiyun 
nubus_get_block_rsrc_dir(struct nubus_board * board,struct proc_dir_entry * procdir,const struct nubus_dirent * parent)338*4882a593Smuzhiyun static int __init nubus_get_block_rsrc_dir(struct nubus_board *board,
339*4882a593Smuzhiyun 					   struct proc_dir_entry *procdir,
340*4882a593Smuzhiyun 					   const struct nubus_dirent *parent)
341*4882a593Smuzhiyun {
342*4882a593Smuzhiyun 	struct nubus_dir dir;
343*4882a593Smuzhiyun 	struct nubus_dirent ent;
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	nubus_get_subdir(parent, &dir);
346*4882a593Smuzhiyun 	dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board);
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	while (nubus_readdir(&dir, &ent) != -1) {
349*4882a593Smuzhiyun 		u32 size;
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 		nubus_get_rsrc_mem(&size, &ent, 4);
352*4882a593Smuzhiyun 		pr_debug("        block (0x%x), size %d\n", ent.type, size);
353*4882a593Smuzhiyun 		nubus_proc_add_rsrc_mem(dir.procdir, &ent, size);
354*4882a593Smuzhiyun 	}
355*4882a593Smuzhiyun 	return 0;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun 
nubus_get_display_vidmode(struct nubus_board * board,struct proc_dir_entry * procdir,const struct nubus_dirent * parent)358*4882a593Smuzhiyun static int __init nubus_get_display_vidmode(struct nubus_board *board,
359*4882a593Smuzhiyun 					    struct proc_dir_entry *procdir,
360*4882a593Smuzhiyun 					    const struct nubus_dirent *parent)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun 	struct nubus_dir dir;
363*4882a593Smuzhiyun 	struct nubus_dirent ent;
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	nubus_get_subdir(parent, &dir);
366*4882a593Smuzhiyun 	dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board);
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	while (nubus_readdir(&dir, &ent) != -1) {
369*4882a593Smuzhiyun 		switch (ent.type) {
370*4882a593Smuzhiyun 		case 1: /* mVidParams */
371*4882a593Smuzhiyun 		case 2: /* mTable */
372*4882a593Smuzhiyun 		{
373*4882a593Smuzhiyun 			u32 size;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 			nubus_get_rsrc_mem(&size, &ent, 4);
376*4882a593Smuzhiyun 			pr_debug("        block (0x%x), size %d\n", ent.type,
377*4882a593Smuzhiyun 				size);
378*4882a593Smuzhiyun 			nubus_proc_add_rsrc_mem(dir.procdir, &ent, size);
379*4882a593Smuzhiyun 			break;
380*4882a593Smuzhiyun 		}
381*4882a593Smuzhiyun 		default:
382*4882a593Smuzhiyun 			pr_debug("        unknown resource 0x%02x, data 0x%06x\n",
383*4882a593Smuzhiyun 				ent.type, ent.data);
384*4882a593Smuzhiyun 			nubus_proc_add_rsrc_mem(dir.procdir, &ent, 0);
385*4882a593Smuzhiyun 		}
386*4882a593Smuzhiyun 	}
387*4882a593Smuzhiyun 	return 0;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun 
nubus_get_display_resource(struct nubus_rsrc * fres,struct proc_dir_entry * procdir,const struct nubus_dirent * ent)390*4882a593Smuzhiyun static int __init nubus_get_display_resource(struct nubus_rsrc *fres,
391*4882a593Smuzhiyun 					     struct proc_dir_entry *procdir,
392*4882a593Smuzhiyun 					     const struct nubus_dirent *ent)
393*4882a593Smuzhiyun {
394*4882a593Smuzhiyun 	switch (ent->type) {
395*4882a593Smuzhiyun 	case NUBUS_RESID_GAMMADIR:
396*4882a593Smuzhiyun 		pr_debug("    gamma directory offset: 0x%06x\n", ent->data);
397*4882a593Smuzhiyun 		nubus_get_block_rsrc_dir(fres->board, procdir, ent);
398*4882a593Smuzhiyun 		break;
399*4882a593Smuzhiyun 	case 0x0080 ... 0x0085:
400*4882a593Smuzhiyun 		pr_debug("    mode 0x%02x info offset: 0x%06x\n",
401*4882a593Smuzhiyun 			ent->type, ent->data);
402*4882a593Smuzhiyun 		nubus_get_display_vidmode(fres->board, procdir, ent);
403*4882a593Smuzhiyun 		break;
404*4882a593Smuzhiyun 	default:
405*4882a593Smuzhiyun 		pr_debug("    unknown resource 0x%02x, data 0x%06x\n",
406*4882a593Smuzhiyun 			ent->type, ent->data);
407*4882a593Smuzhiyun 		nubus_proc_add_rsrc_mem(procdir, ent, 0);
408*4882a593Smuzhiyun 	}
409*4882a593Smuzhiyun 	return 0;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun 
nubus_get_network_resource(struct nubus_rsrc * fres,struct proc_dir_entry * procdir,const struct nubus_dirent * ent)412*4882a593Smuzhiyun static int __init nubus_get_network_resource(struct nubus_rsrc *fres,
413*4882a593Smuzhiyun 					     struct proc_dir_entry *procdir,
414*4882a593Smuzhiyun 					     const struct nubus_dirent *ent)
415*4882a593Smuzhiyun {
416*4882a593Smuzhiyun 	switch (ent->type) {
417*4882a593Smuzhiyun 	case NUBUS_RESID_MAC_ADDRESS:
418*4882a593Smuzhiyun 	{
419*4882a593Smuzhiyun 		char addr[6];
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 		nubus_get_rsrc_mem(addr, ent, 6);
422*4882a593Smuzhiyun 		pr_debug("    MAC address: %pM\n", addr);
423*4882a593Smuzhiyun 		nubus_proc_add_rsrc_mem(procdir, ent, 6);
424*4882a593Smuzhiyun 		break;
425*4882a593Smuzhiyun 	}
426*4882a593Smuzhiyun 	default:
427*4882a593Smuzhiyun 		pr_debug("    unknown resource 0x%02x, data 0x%06x\n",
428*4882a593Smuzhiyun 			ent->type, ent->data);
429*4882a593Smuzhiyun 		nubus_proc_add_rsrc_mem(procdir, ent, 0);
430*4882a593Smuzhiyun 	}
431*4882a593Smuzhiyun 	return 0;
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun 
nubus_get_cpu_resource(struct nubus_rsrc * fres,struct proc_dir_entry * procdir,const struct nubus_dirent * ent)434*4882a593Smuzhiyun static int __init nubus_get_cpu_resource(struct nubus_rsrc *fres,
435*4882a593Smuzhiyun 					 struct proc_dir_entry *procdir,
436*4882a593Smuzhiyun 					 const struct nubus_dirent *ent)
437*4882a593Smuzhiyun {
438*4882a593Smuzhiyun 	switch (ent->type) {
439*4882a593Smuzhiyun 	case NUBUS_RESID_MEMINFO:
440*4882a593Smuzhiyun 	{
441*4882a593Smuzhiyun 		unsigned long meminfo[2];
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 		nubus_get_rsrc_mem(&meminfo, ent, 8);
444*4882a593Smuzhiyun 		pr_debug("    memory: [ 0x%08lx 0x%08lx ]\n",
445*4882a593Smuzhiyun 			meminfo[0], meminfo[1]);
446*4882a593Smuzhiyun 		nubus_proc_add_rsrc_mem(procdir, ent, 8);
447*4882a593Smuzhiyun 		break;
448*4882a593Smuzhiyun 	}
449*4882a593Smuzhiyun 	case NUBUS_RESID_ROMINFO:
450*4882a593Smuzhiyun 	{
451*4882a593Smuzhiyun 		unsigned long rominfo[2];
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 		nubus_get_rsrc_mem(&rominfo, ent, 8);
454*4882a593Smuzhiyun 		pr_debug("    ROM:    [ 0x%08lx 0x%08lx ]\n",
455*4882a593Smuzhiyun 			rominfo[0], rominfo[1]);
456*4882a593Smuzhiyun 		nubus_proc_add_rsrc_mem(procdir, ent, 8);
457*4882a593Smuzhiyun 		break;
458*4882a593Smuzhiyun 	}
459*4882a593Smuzhiyun 	default:
460*4882a593Smuzhiyun 		pr_debug("    unknown resource 0x%02x, data 0x%06x\n",
461*4882a593Smuzhiyun 			ent->type, ent->data);
462*4882a593Smuzhiyun 		nubus_proc_add_rsrc_mem(procdir, ent, 0);
463*4882a593Smuzhiyun 	}
464*4882a593Smuzhiyun 	return 0;
465*4882a593Smuzhiyun }
466*4882a593Smuzhiyun 
nubus_get_private_resource(struct nubus_rsrc * fres,struct proc_dir_entry * procdir,const struct nubus_dirent * ent)467*4882a593Smuzhiyun static int __init nubus_get_private_resource(struct nubus_rsrc *fres,
468*4882a593Smuzhiyun 					     struct proc_dir_entry *procdir,
469*4882a593Smuzhiyun 					     const struct nubus_dirent *ent)
470*4882a593Smuzhiyun {
471*4882a593Smuzhiyun 	switch (fres->category) {
472*4882a593Smuzhiyun 	case NUBUS_CAT_DISPLAY:
473*4882a593Smuzhiyun 		nubus_get_display_resource(fres, procdir, ent);
474*4882a593Smuzhiyun 		break;
475*4882a593Smuzhiyun 	case NUBUS_CAT_NETWORK:
476*4882a593Smuzhiyun 		nubus_get_network_resource(fres, procdir, ent);
477*4882a593Smuzhiyun 		break;
478*4882a593Smuzhiyun 	case NUBUS_CAT_CPU:
479*4882a593Smuzhiyun 		nubus_get_cpu_resource(fres, procdir, ent);
480*4882a593Smuzhiyun 		break;
481*4882a593Smuzhiyun 	default:
482*4882a593Smuzhiyun 		pr_debug("    unknown resource 0x%02x, data 0x%06x\n",
483*4882a593Smuzhiyun 			ent->type, ent->data);
484*4882a593Smuzhiyun 		nubus_proc_add_rsrc_mem(procdir, ent, 0);
485*4882a593Smuzhiyun 	}
486*4882a593Smuzhiyun 	return 0;
487*4882a593Smuzhiyun }
488*4882a593Smuzhiyun 
489*4882a593Smuzhiyun static struct nubus_rsrc * __init
nubus_get_functional_resource(struct nubus_board * board,int slot,const struct nubus_dirent * parent)490*4882a593Smuzhiyun nubus_get_functional_resource(struct nubus_board *board, int slot,
491*4882a593Smuzhiyun 			      const struct nubus_dirent *parent)
492*4882a593Smuzhiyun {
493*4882a593Smuzhiyun 	struct nubus_dir dir;
494*4882a593Smuzhiyun 	struct nubus_dirent ent;
495*4882a593Smuzhiyun 	struct nubus_rsrc *fres;
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	pr_debug("  Functional resource 0x%02x:\n", parent->type);
498*4882a593Smuzhiyun 	nubus_get_subdir(parent, &dir);
499*4882a593Smuzhiyun 	dir.procdir = nubus_proc_add_rsrc_dir(board->procdir, parent, board);
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	/* Actually we should probably panic if this fails */
502*4882a593Smuzhiyun 	fres = kzalloc(sizeof(*fres), GFP_ATOMIC);
503*4882a593Smuzhiyun 	if (!fres)
504*4882a593Smuzhiyun 		return NULL;
505*4882a593Smuzhiyun 	fres->resid = parent->type;
506*4882a593Smuzhiyun 	fres->directory = dir.base;
507*4882a593Smuzhiyun 	fres->board = board;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	while (nubus_readdir(&dir, &ent) != -1) {
510*4882a593Smuzhiyun 		switch (ent.type) {
511*4882a593Smuzhiyun 		case NUBUS_RESID_TYPE:
512*4882a593Smuzhiyun 		{
513*4882a593Smuzhiyun 			unsigned short nbtdata[4];
514*4882a593Smuzhiyun 
515*4882a593Smuzhiyun 			nubus_get_rsrc_mem(nbtdata, &ent, 8);
516*4882a593Smuzhiyun 			fres->category = nbtdata[0];
517*4882a593Smuzhiyun 			fres->type     = nbtdata[1];
518*4882a593Smuzhiyun 			fres->dr_sw    = nbtdata[2];
519*4882a593Smuzhiyun 			fres->dr_hw    = nbtdata[3];
520*4882a593Smuzhiyun 			pr_debug("    type: [cat 0x%x type 0x%x sw 0x%x hw 0x%x]\n",
521*4882a593Smuzhiyun 				nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]);
522*4882a593Smuzhiyun 			nubus_proc_add_rsrc_mem(dir.procdir, &ent, 8);
523*4882a593Smuzhiyun 			break;
524*4882a593Smuzhiyun 		}
525*4882a593Smuzhiyun 		case NUBUS_RESID_NAME:
526*4882a593Smuzhiyun 		{
527*4882a593Smuzhiyun 			char name[64];
528*4882a593Smuzhiyun 			unsigned int len;
529*4882a593Smuzhiyun 
530*4882a593Smuzhiyun 			len = nubus_get_rsrc_str(name, &ent, sizeof(name));
531*4882a593Smuzhiyun 			pr_debug("    name: %s\n", name);
532*4882a593Smuzhiyun 			nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1);
533*4882a593Smuzhiyun 			break;
534*4882a593Smuzhiyun 		}
535*4882a593Smuzhiyun 		case NUBUS_RESID_DRVRDIR:
536*4882a593Smuzhiyun 		{
537*4882a593Smuzhiyun 			/* MacOS driver.  If we were NetBSD we might
538*4882a593Smuzhiyun 			   use this :-) */
539*4882a593Smuzhiyun 			pr_debug("    driver directory offset: 0x%06x\n",
540*4882a593Smuzhiyun 				ent.data);
541*4882a593Smuzhiyun 			nubus_get_block_rsrc_dir(board, dir.procdir, &ent);
542*4882a593Smuzhiyun 			break;
543*4882a593Smuzhiyun 		}
544*4882a593Smuzhiyun 		case NUBUS_RESID_MINOR_BASEOS:
545*4882a593Smuzhiyun 		{
546*4882a593Smuzhiyun 			/* We will need this in order to support
547*4882a593Smuzhiyun 			   multiple framebuffers.  It might be handy
548*4882a593Smuzhiyun 			   for Ethernet as well */
549*4882a593Smuzhiyun 			u32 base_offset;
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 			nubus_get_rsrc_mem(&base_offset, &ent, 4);
552*4882a593Smuzhiyun 			pr_debug("    memory offset: 0x%08x\n", base_offset);
553*4882a593Smuzhiyun 			nubus_proc_add_rsrc_mem(dir.procdir, &ent, 4);
554*4882a593Smuzhiyun 			break;
555*4882a593Smuzhiyun 		}
556*4882a593Smuzhiyun 		case NUBUS_RESID_MINOR_LENGTH:
557*4882a593Smuzhiyun 		{
558*4882a593Smuzhiyun 			/* Ditto */
559*4882a593Smuzhiyun 			u32 length;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 			nubus_get_rsrc_mem(&length, &ent, 4);
562*4882a593Smuzhiyun 			pr_debug("    memory length: 0x%08x\n", length);
563*4882a593Smuzhiyun 			nubus_proc_add_rsrc_mem(dir.procdir, &ent, 4);
564*4882a593Smuzhiyun 			break;
565*4882a593Smuzhiyun 		}
566*4882a593Smuzhiyun 		case NUBUS_RESID_FLAGS:
567*4882a593Smuzhiyun 			pr_debug("    flags: 0x%06x\n", ent.data);
568*4882a593Smuzhiyun 			nubus_proc_add_rsrc(dir.procdir, &ent);
569*4882a593Smuzhiyun 			break;
570*4882a593Smuzhiyun 		case NUBUS_RESID_HWDEVID:
571*4882a593Smuzhiyun 			pr_debug("    hwdevid: 0x%06x\n", ent.data);
572*4882a593Smuzhiyun 			nubus_proc_add_rsrc(dir.procdir, &ent);
573*4882a593Smuzhiyun 			break;
574*4882a593Smuzhiyun 		default:
575*4882a593Smuzhiyun 			/* Local/Private resources have their own
576*4882a593Smuzhiyun 			   function */
577*4882a593Smuzhiyun 			nubus_get_private_resource(fres, dir.procdir, &ent);
578*4882a593Smuzhiyun 		}
579*4882a593Smuzhiyun 	}
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	return fres;
582*4882a593Smuzhiyun }
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun /* This is *really* cool. */
nubus_get_icon(struct nubus_board * board,struct proc_dir_entry * procdir,const struct nubus_dirent * ent)585*4882a593Smuzhiyun static int __init nubus_get_icon(struct nubus_board *board,
586*4882a593Smuzhiyun 				 struct proc_dir_entry *procdir,
587*4882a593Smuzhiyun 				 const struct nubus_dirent *ent)
588*4882a593Smuzhiyun {
589*4882a593Smuzhiyun 	/* Should be 32x32 if my memory serves me correctly */
590*4882a593Smuzhiyun 	u32 icon[32];
591*4882a593Smuzhiyun 	int i;
592*4882a593Smuzhiyun 
593*4882a593Smuzhiyun 	nubus_get_rsrc_mem(&icon, ent, 128);
594*4882a593Smuzhiyun 	pr_debug("    icon:\n");
595*4882a593Smuzhiyun 	for (i = 0; i < 8; i++)
596*4882a593Smuzhiyun 		pr_debug("        %08x %08x %08x %08x\n",
597*4882a593Smuzhiyun 			icon[i * 4 + 0], icon[i * 4 + 1],
598*4882a593Smuzhiyun 			icon[i * 4 + 2], icon[i * 4 + 3]);
599*4882a593Smuzhiyun 	nubus_proc_add_rsrc_mem(procdir, ent, 128);
600*4882a593Smuzhiyun 
601*4882a593Smuzhiyun 	return 0;
602*4882a593Smuzhiyun }
603*4882a593Smuzhiyun 
nubus_get_vendorinfo(struct nubus_board * board,struct proc_dir_entry * procdir,const struct nubus_dirent * parent)604*4882a593Smuzhiyun static int __init nubus_get_vendorinfo(struct nubus_board *board,
605*4882a593Smuzhiyun 				       struct proc_dir_entry *procdir,
606*4882a593Smuzhiyun 				       const struct nubus_dirent *parent)
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun 	struct nubus_dir dir;
609*4882a593Smuzhiyun 	struct nubus_dirent ent;
610*4882a593Smuzhiyun 	static char *vendor_fields[6] = { "ID", "serial", "revision",
611*4882a593Smuzhiyun 	                                  "part", "date", "unknown field" };
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun 	pr_debug("    vendor info:\n");
614*4882a593Smuzhiyun 	nubus_get_subdir(parent, &dir);
615*4882a593Smuzhiyun 	dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board);
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun 	while (nubus_readdir(&dir, &ent) != -1) {
618*4882a593Smuzhiyun 		char name[64];
619*4882a593Smuzhiyun 		unsigned int len;
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun 		/* These are all strings, we think */
622*4882a593Smuzhiyun 		len = nubus_get_rsrc_str(name, &ent, sizeof(name));
623*4882a593Smuzhiyun 		if (ent.type < 1 || ent.type > 5)
624*4882a593Smuzhiyun 			ent.type = 5;
625*4882a593Smuzhiyun 		pr_debug("    %s: %s\n", vendor_fields[ent.type - 1], name);
626*4882a593Smuzhiyun 		nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1);
627*4882a593Smuzhiyun 	}
628*4882a593Smuzhiyun 	return 0;
629*4882a593Smuzhiyun }
630*4882a593Smuzhiyun 
nubus_get_board_resource(struct nubus_board * board,int slot,const struct nubus_dirent * parent)631*4882a593Smuzhiyun static int __init nubus_get_board_resource(struct nubus_board *board, int slot,
632*4882a593Smuzhiyun 					   const struct nubus_dirent *parent)
633*4882a593Smuzhiyun {
634*4882a593Smuzhiyun 	struct nubus_dir dir;
635*4882a593Smuzhiyun 	struct nubus_dirent ent;
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun 	pr_debug("  Board resource 0x%02x:\n", parent->type);
638*4882a593Smuzhiyun 	nubus_get_subdir(parent, &dir);
639*4882a593Smuzhiyun 	dir.procdir = nubus_proc_add_rsrc_dir(board->procdir, parent, board);
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun 	while (nubus_readdir(&dir, &ent) != -1) {
642*4882a593Smuzhiyun 		switch (ent.type) {
643*4882a593Smuzhiyun 		case NUBUS_RESID_TYPE:
644*4882a593Smuzhiyun 		{
645*4882a593Smuzhiyun 			unsigned short nbtdata[4];
646*4882a593Smuzhiyun 			/* This type is always the same, and is not
647*4882a593Smuzhiyun 			   useful except insofar as it tells us that
648*4882a593Smuzhiyun 			   we really are looking at a board resource. */
649*4882a593Smuzhiyun 			nubus_get_rsrc_mem(nbtdata, &ent, 8);
650*4882a593Smuzhiyun 			pr_debug("    type: [cat 0x%x type 0x%x sw 0x%x hw 0x%x]\n",
651*4882a593Smuzhiyun 				nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]);
652*4882a593Smuzhiyun 			if (nbtdata[0] != 1 || nbtdata[1] != 0 ||
653*4882a593Smuzhiyun 			    nbtdata[2] != 0 || nbtdata[3] != 0)
654*4882a593Smuzhiyun 				pr_err("Slot %X: sResource is not a board resource!\n",
655*4882a593Smuzhiyun 				       slot);
656*4882a593Smuzhiyun 			nubus_proc_add_rsrc_mem(dir.procdir, &ent, 8);
657*4882a593Smuzhiyun 			break;
658*4882a593Smuzhiyun 		}
659*4882a593Smuzhiyun 		case NUBUS_RESID_NAME:
660*4882a593Smuzhiyun 		{
661*4882a593Smuzhiyun 			unsigned int len;
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun 			len = nubus_get_rsrc_str(board->name, &ent,
664*4882a593Smuzhiyun 						 sizeof(board->name));
665*4882a593Smuzhiyun 			pr_debug("    name: %s\n", board->name);
666*4882a593Smuzhiyun 			nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1);
667*4882a593Smuzhiyun 			break;
668*4882a593Smuzhiyun 		}
669*4882a593Smuzhiyun 		case NUBUS_RESID_ICON:
670*4882a593Smuzhiyun 			nubus_get_icon(board, dir.procdir, &ent);
671*4882a593Smuzhiyun 			break;
672*4882a593Smuzhiyun 		case NUBUS_RESID_BOARDID:
673*4882a593Smuzhiyun 			pr_debug("    board id: 0x%x\n", ent.data);
674*4882a593Smuzhiyun 			nubus_proc_add_rsrc(dir.procdir, &ent);
675*4882a593Smuzhiyun 			break;
676*4882a593Smuzhiyun 		case NUBUS_RESID_PRIMARYINIT:
677*4882a593Smuzhiyun 			pr_debug("    primary init offset: 0x%06x\n", ent.data);
678*4882a593Smuzhiyun 			nubus_proc_add_rsrc(dir.procdir, &ent);
679*4882a593Smuzhiyun 			break;
680*4882a593Smuzhiyun 		case NUBUS_RESID_VENDORINFO:
681*4882a593Smuzhiyun 			nubus_get_vendorinfo(board, dir.procdir, &ent);
682*4882a593Smuzhiyun 			break;
683*4882a593Smuzhiyun 		case NUBUS_RESID_FLAGS:
684*4882a593Smuzhiyun 			pr_debug("    flags: 0x%06x\n", ent.data);
685*4882a593Smuzhiyun 			nubus_proc_add_rsrc(dir.procdir, &ent);
686*4882a593Smuzhiyun 			break;
687*4882a593Smuzhiyun 		case NUBUS_RESID_HWDEVID:
688*4882a593Smuzhiyun 			pr_debug("    hwdevid: 0x%06x\n", ent.data);
689*4882a593Smuzhiyun 			nubus_proc_add_rsrc(dir.procdir, &ent);
690*4882a593Smuzhiyun 			break;
691*4882a593Smuzhiyun 		case NUBUS_RESID_SECONDINIT:
692*4882a593Smuzhiyun 			pr_debug("    secondary init offset: 0x%06x\n",
693*4882a593Smuzhiyun 				 ent.data);
694*4882a593Smuzhiyun 			nubus_proc_add_rsrc(dir.procdir, &ent);
695*4882a593Smuzhiyun 			break;
696*4882a593Smuzhiyun 			/* WTF isn't this in the functional resources? */
697*4882a593Smuzhiyun 		case NUBUS_RESID_VIDNAMES:
698*4882a593Smuzhiyun 			pr_debug("    vidnames directory offset: 0x%06x\n",
699*4882a593Smuzhiyun 				ent.data);
700*4882a593Smuzhiyun 			nubus_get_block_rsrc_dir(board, dir.procdir, &ent);
701*4882a593Smuzhiyun 			break;
702*4882a593Smuzhiyun 			/* Same goes for this */
703*4882a593Smuzhiyun 		case NUBUS_RESID_VIDMODES:
704*4882a593Smuzhiyun 			pr_debug("    video mode parameter directory offset: 0x%06x\n",
705*4882a593Smuzhiyun 				ent.data);
706*4882a593Smuzhiyun 			nubus_proc_add_rsrc(dir.procdir, &ent);
707*4882a593Smuzhiyun 			break;
708*4882a593Smuzhiyun 		default:
709*4882a593Smuzhiyun 			pr_debug("    unknown resource 0x%02x, data 0x%06x\n",
710*4882a593Smuzhiyun 				ent.type, ent.data);
711*4882a593Smuzhiyun 			nubus_proc_add_rsrc_mem(dir.procdir, &ent, 0);
712*4882a593Smuzhiyun 		}
713*4882a593Smuzhiyun 	}
714*4882a593Smuzhiyun 	return 0;
715*4882a593Smuzhiyun }
716*4882a593Smuzhiyun 
nubus_add_board(int slot,int bytelanes)717*4882a593Smuzhiyun static void __init nubus_add_board(int slot, int bytelanes)
718*4882a593Smuzhiyun {
719*4882a593Smuzhiyun 	struct nubus_board *board;
720*4882a593Smuzhiyun 	unsigned char *rp;
721*4882a593Smuzhiyun 	unsigned long dpat;
722*4882a593Smuzhiyun 	struct nubus_dir dir;
723*4882a593Smuzhiyun 	struct nubus_dirent ent;
724*4882a593Smuzhiyun 	int prev_resid = -1;
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	/* Move to the start of the format block */
727*4882a593Smuzhiyun 	rp = nubus_rom_addr(slot);
728*4882a593Smuzhiyun 	nubus_rewind(&rp, FORMAT_BLOCK_SIZE, bytelanes);
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 	/* Actually we should probably panic if this fails */
731*4882a593Smuzhiyun 	if ((board = kzalloc(sizeof(*board), GFP_ATOMIC)) == NULL)
732*4882a593Smuzhiyun 		return;
733*4882a593Smuzhiyun 	board->fblock = rp;
734*4882a593Smuzhiyun 
735*4882a593Smuzhiyun 	/* Dump the format block for debugging purposes */
736*4882a593Smuzhiyun 	pr_debug("Slot %X, format block at 0x%p:\n", slot, rp);
737*4882a593Smuzhiyun 	pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes));
738*4882a593Smuzhiyun 	pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes));
739*4882a593Smuzhiyun 	pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes));
740*4882a593Smuzhiyun 	pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes));
741*4882a593Smuzhiyun 	pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes));
742*4882a593Smuzhiyun 	pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes));
743*4882a593Smuzhiyun 	pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes));
744*4882a593Smuzhiyun 	pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes));
745*4882a593Smuzhiyun 	rp = board->fblock;
746*4882a593Smuzhiyun 
747*4882a593Smuzhiyun 	board->slot = slot;
748*4882a593Smuzhiyun 	board->slot_addr = (unsigned long)nubus_slot_addr(slot);
749*4882a593Smuzhiyun 	board->doffset = nubus_get_rom(&rp, 4, bytelanes);
750*4882a593Smuzhiyun 	/* rom_length is *supposed* to be the total length of the
751*4882a593Smuzhiyun 	 * ROM.  In practice it is the "amount of ROM used to compute
752*4882a593Smuzhiyun 	 * the CRC."  So some jokers decide to set it to zero and
753*4882a593Smuzhiyun 	 * set the crc to zero so they don't have to do any math.
754*4882a593Smuzhiyun 	 * See the Performa 460 ROM, for example.  Those Apple "engineers".
755*4882a593Smuzhiyun 	 */
756*4882a593Smuzhiyun 	board->rom_length = nubus_get_rom(&rp, 4, bytelanes);
757*4882a593Smuzhiyun 	board->crc = nubus_get_rom(&rp, 4, bytelanes);
758*4882a593Smuzhiyun 	board->rev = nubus_get_rom(&rp, 1, bytelanes);
759*4882a593Smuzhiyun 	board->format = nubus_get_rom(&rp, 1, bytelanes);
760*4882a593Smuzhiyun 	board->lanes = bytelanes;
761*4882a593Smuzhiyun 
762*4882a593Smuzhiyun 	/* Directory offset should be small and negative... */
763*4882a593Smuzhiyun 	if (!(board->doffset & 0x00FF0000))
764*4882a593Smuzhiyun 		pr_warn("Slot %X: Dodgy doffset!\n", slot);
765*4882a593Smuzhiyun 	dpat = nubus_get_rom(&rp, 4, bytelanes);
766*4882a593Smuzhiyun 	if (dpat != NUBUS_TEST_PATTERN)
767*4882a593Smuzhiyun 		pr_warn("Slot %X: Wrong test pattern %08lx!\n", slot, dpat);
768*4882a593Smuzhiyun 
769*4882a593Smuzhiyun 	/*
770*4882a593Smuzhiyun 	 *	I wonder how the CRC is meant to work -
771*4882a593Smuzhiyun 	 *		any takers ?
772*4882a593Smuzhiyun 	 * CSA: According to MAC docs, not all cards pass the CRC anyway,
773*4882a593Smuzhiyun 	 * since the initial Macintosh ROM releases skipped the check.
774*4882a593Smuzhiyun 	 */
775*4882a593Smuzhiyun 
776*4882a593Smuzhiyun 	/* Set up the directory pointer */
777*4882a593Smuzhiyun 	board->directory = board->fblock;
778*4882a593Smuzhiyun 	nubus_move(&board->directory, nubus_expand32(board->doffset),
779*4882a593Smuzhiyun 	           board->lanes);
780*4882a593Smuzhiyun 
781*4882a593Smuzhiyun 	nubus_get_root_dir(board, &dir);
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	/* We're ready to rock */
784*4882a593Smuzhiyun 	pr_debug("Slot %X resources:\n", slot);
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	/* Each slot should have one board resource and any number of
787*4882a593Smuzhiyun 	 * functional resources.  So we'll fill in some fields in the
788*4882a593Smuzhiyun 	 * struct nubus_board from the board resource, then walk down
789*4882a593Smuzhiyun 	 * the list of functional resources, spinning out a nubus_rsrc
790*4882a593Smuzhiyun 	 * for each of them.
791*4882a593Smuzhiyun 	 */
792*4882a593Smuzhiyun 	if (nubus_readdir(&dir, &ent) == -1) {
793*4882a593Smuzhiyun 		/* We can't have this! */
794*4882a593Smuzhiyun 		pr_err("Slot %X: Board resource not found!\n", slot);
795*4882a593Smuzhiyun 		kfree(board);
796*4882a593Smuzhiyun 		return;
797*4882a593Smuzhiyun 	}
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	if (ent.type < 1 || ent.type > 127)
800*4882a593Smuzhiyun 		pr_warn("Slot %X: Board resource ID is invalid!\n", slot);
801*4882a593Smuzhiyun 
802*4882a593Smuzhiyun 	board->procdir = nubus_proc_add_board(board);
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 	nubus_get_board_resource(board, slot, &ent);
805*4882a593Smuzhiyun 
806*4882a593Smuzhiyun 	while (nubus_readdir(&dir, &ent) != -1) {
807*4882a593Smuzhiyun 		struct nubus_rsrc *fres;
808*4882a593Smuzhiyun 
809*4882a593Smuzhiyun 		fres = nubus_get_functional_resource(board, slot, &ent);
810*4882a593Smuzhiyun 		if (fres == NULL)
811*4882a593Smuzhiyun 			continue;
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 		/* Resources should appear in ascending ID order. This sanity
814*4882a593Smuzhiyun 		 * check prevents duplicate resource IDs.
815*4882a593Smuzhiyun 		 */
816*4882a593Smuzhiyun 		if (fres->resid <= prev_resid) {
817*4882a593Smuzhiyun 			kfree(fres);
818*4882a593Smuzhiyun 			continue;
819*4882a593Smuzhiyun 		}
820*4882a593Smuzhiyun 		prev_resid = fres->resid;
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun 		list_add_tail(&fres->list, &nubus_func_rsrcs);
823*4882a593Smuzhiyun 	}
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	if (nubus_device_register(board))
826*4882a593Smuzhiyun 		put_device(&board->dev);
827*4882a593Smuzhiyun }
828*4882a593Smuzhiyun 
nubus_probe_slot(int slot)829*4882a593Smuzhiyun static void __init nubus_probe_slot(int slot)
830*4882a593Smuzhiyun {
831*4882a593Smuzhiyun 	unsigned char dp;
832*4882a593Smuzhiyun 	unsigned char *rp;
833*4882a593Smuzhiyun 	int i;
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun 	rp = nubus_rom_addr(slot);
836*4882a593Smuzhiyun 	for (i = 4; i; i--) {
837*4882a593Smuzhiyun 		rp--;
838*4882a593Smuzhiyun 		if (!hwreg_present(rp))
839*4882a593Smuzhiyun 			continue;
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun 		dp = *rp;
842*4882a593Smuzhiyun 
843*4882a593Smuzhiyun 		/* The last byte of the format block consists of two
844*4882a593Smuzhiyun 		   nybbles which are "mirror images" of each other.
845*4882a593Smuzhiyun 		   These show us the valid bytelanes */
846*4882a593Smuzhiyun 		if ((((dp >> 4) ^ dp) & 0x0F) != 0x0F)
847*4882a593Smuzhiyun 			continue;
848*4882a593Smuzhiyun 		/* Check that this value is actually *on* one of the
849*4882a593Smuzhiyun 		   bytelanes it claims are valid! */
850*4882a593Smuzhiyun 		if (not_useful(rp, dp))
851*4882a593Smuzhiyun 			continue;
852*4882a593Smuzhiyun 
853*4882a593Smuzhiyun 		/* Looks promising.  Let's put it on the list. */
854*4882a593Smuzhiyun 		nubus_add_board(slot, dp);
855*4882a593Smuzhiyun 
856*4882a593Smuzhiyun 		return;
857*4882a593Smuzhiyun 	}
858*4882a593Smuzhiyun }
859*4882a593Smuzhiyun 
nubus_scan_bus(void)860*4882a593Smuzhiyun static void __init nubus_scan_bus(void)
861*4882a593Smuzhiyun {
862*4882a593Smuzhiyun 	int slot;
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun 	pr_info("NuBus: Scanning NuBus slots.\n");
865*4882a593Smuzhiyun 	for (slot = 9; slot < 15; slot++) {
866*4882a593Smuzhiyun 		nubus_probe_slot(slot);
867*4882a593Smuzhiyun 	}
868*4882a593Smuzhiyun }
869*4882a593Smuzhiyun 
nubus_init(void)870*4882a593Smuzhiyun static int __init nubus_init(void)
871*4882a593Smuzhiyun {
872*4882a593Smuzhiyun 	int err;
873*4882a593Smuzhiyun 
874*4882a593Smuzhiyun 	if (!MACH_IS_MAC)
875*4882a593Smuzhiyun 		return 0;
876*4882a593Smuzhiyun 
877*4882a593Smuzhiyun 	nubus_proc_init();
878*4882a593Smuzhiyun 	err = nubus_parent_device_register();
879*4882a593Smuzhiyun 	if (err)
880*4882a593Smuzhiyun 		return err;
881*4882a593Smuzhiyun 	nubus_scan_bus();
882*4882a593Smuzhiyun 	return 0;
883*4882a593Smuzhiyun }
884*4882a593Smuzhiyun 
885*4882a593Smuzhiyun subsys_initcall(nubus_init);
886