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