1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Macintosh Nubus Interface Code
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Originally by Alan Cox
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * Mostly rewritten by David Huggins-Daines, C. Scott Ananian,
81da177e4SLinus Torvalds * and others.
91da177e4SLinus Torvalds */
101da177e4SLinus Torvalds
111da177e4SLinus Torvalds #include <linux/types.h>
121da177e4SLinus Torvalds #include <linux/kernel.h>
131da177e4SLinus Torvalds #include <linux/string.h>
141da177e4SLinus Torvalds #include <linux/nubus.h>
151da177e4SLinus Torvalds #include <linux/errno.h>
161da177e4SLinus Torvalds #include <linux/init.h>
1799ffab81SAdrian Bunk #include <linux/module.h>
182f7dd07eSFinn Thain #include <linux/seq_file.h>
195a0e3ad6STejun Heo #include <linux/slab.h>
201da177e4SLinus Torvalds #include <asm/setup.h>
211da177e4SLinus Torvalds #include <asm/page.h>
221da177e4SLinus Torvalds #include <asm/hwtest.h>
231da177e4SLinus Torvalds
241da177e4SLinus Torvalds /* Constants */
251da177e4SLinus Torvalds
261da177e4SLinus Torvalds /* This is, of course, the size in bytelanes, rather than the size in
271da177e4SLinus Torvalds actual bytes */
281da177e4SLinus Torvalds #define FORMAT_BLOCK_SIZE 20
291da177e4SLinus Torvalds #define ROM_DIR_OFFSET 0x24
301da177e4SLinus Torvalds
311da177e4SLinus Torvalds #define NUBUS_TEST_PATTERN 0x5A932BC7
321da177e4SLinus Torvalds
331da177e4SLinus Torvalds /* Globals */
341da177e4SLinus Torvalds
35*72b44f65SFinn Thain /* The "nubus.populate_procfs" parameter makes slot resources available in
36*72b44f65SFinn Thain * procfs. It's deprecated and disabled by default because procfs is no longer
37*72b44f65SFinn Thain * thought to be suitable for that and some board ROMs make it too expensive.
38*72b44f65SFinn Thain */
39*72b44f65SFinn Thain bool nubus_populate_procfs;
40*72b44f65SFinn Thain module_param_named(populate_procfs, nubus_populate_procfs, bool, 0);
41*72b44f65SFinn Thain
4241b84816SFinn Thain LIST_HEAD(nubus_func_rsrcs);
431da177e4SLinus Torvalds
441da177e4SLinus Torvalds /* Meaning of "bytelanes":
451da177e4SLinus Torvalds
461da177e4SLinus Torvalds The card ROM may appear on any or all bytes of each long word in
471da177e4SLinus Torvalds NuBus memory. The low 4 bits of the "map" value found in the
481da177e4SLinus Torvalds format block (at the top of the slot address space, as well as at
491da177e4SLinus Torvalds the top of the MacOS ROM) tells us which bytelanes, i.e. which byte
501da177e4SLinus Torvalds offsets within each longword, are valid. Thus:
511da177e4SLinus Torvalds
521da177e4SLinus Torvalds A map of 0x0f, as found in the MacOS ROM, means that all bytelanes
531da177e4SLinus Torvalds are valid.
541da177e4SLinus Torvalds
551da177e4SLinus Torvalds A map of 0xf0 means that no bytelanes are valid (We pray that we
561da177e4SLinus Torvalds will never encounter this, but stranger things have happened)
571da177e4SLinus Torvalds
581da177e4SLinus Torvalds A map of 0xe1 means that only the MSB of each long word is actually
591da177e4SLinus Torvalds part of the card ROM. (We hope to never encounter NuBus on a
601da177e4SLinus Torvalds little-endian machine. Again, stranger things have happened)
611da177e4SLinus Torvalds
621da177e4SLinus Torvalds A map of 0x78 means that only the LSB of each long word is valid.
631da177e4SLinus Torvalds
641da177e4SLinus Torvalds Etcetera, etcetera. Hopefully this clears up some confusion over
651da177e4SLinus Torvalds what the following code actually does. */
661da177e4SLinus Torvalds
not_useful(void * p,int map)671da177e4SLinus Torvalds static inline int not_useful(void *p, int map)
681da177e4SLinus Torvalds {
691da177e4SLinus Torvalds unsigned long pv = (unsigned long)p;
70f42e5550SFinn Thain
711da177e4SLinus Torvalds pv &= 3;
721da177e4SLinus Torvalds if (map & (1 << pv))
731da177e4SLinus Torvalds return 0;
741da177e4SLinus Torvalds return 1;
751da177e4SLinus Torvalds }
761da177e4SLinus Torvalds
nubus_get_rom(unsigned char ** ptr,int len,int map)771da177e4SLinus Torvalds static unsigned long nubus_get_rom(unsigned char **ptr, int len, int map)
781da177e4SLinus Torvalds {
791da177e4SLinus Torvalds /* This will hold the result */
801da177e4SLinus Torvalds unsigned long v = 0;
811da177e4SLinus Torvalds unsigned char *p = *ptr;
821da177e4SLinus Torvalds
83f42e5550SFinn Thain while (len) {
841da177e4SLinus Torvalds v <<= 8;
851da177e4SLinus Torvalds while (not_useful(p, map))
861da177e4SLinus Torvalds p++;
871da177e4SLinus Torvalds v |= *p++;
881da177e4SLinus Torvalds len--;
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds *ptr = p;
911da177e4SLinus Torvalds return v;
921da177e4SLinus Torvalds }
931da177e4SLinus Torvalds
nubus_rewind(unsigned char ** ptr,int len,int map)941da177e4SLinus Torvalds static void nubus_rewind(unsigned char **ptr, int len, int map)
951da177e4SLinus Torvalds {
961da177e4SLinus Torvalds unsigned char *p = *ptr;
971da177e4SLinus Torvalds
98f42e5550SFinn Thain while (len) {
99f42e5550SFinn Thain do {
1001da177e4SLinus Torvalds p--;
101f42e5550SFinn Thain } while (not_useful(p, map));
1021da177e4SLinus Torvalds len--;
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds *ptr = p;
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds
nubus_advance(unsigned char ** ptr,int len,int map)1071da177e4SLinus Torvalds static void nubus_advance(unsigned char **ptr, int len, int map)
1081da177e4SLinus Torvalds {
1091da177e4SLinus Torvalds unsigned char *p = *ptr;
110f42e5550SFinn Thain
111f42e5550SFinn Thain while (len) {
1121da177e4SLinus Torvalds while (not_useful(p, map))
1131da177e4SLinus Torvalds p++;
1141da177e4SLinus Torvalds p++;
1151da177e4SLinus Torvalds len--;
1161da177e4SLinus Torvalds }
1171da177e4SLinus Torvalds *ptr = p;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds
nubus_move(unsigned char ** ptr,int len,int map)1201da177e4SLinus Torvalds static void nubus_move(unsigned char **ptr, int len, int map)
1211da177e4SLinus Torvalds {
12285cc313aSFinn Thain unsigned long slot_space = (unsigned long)*ptr & 0xFF000000;
12385cc313aSFinn Thain
1241da177e4SLinus Torvalds if (len > 0)
1251da177e4SLinus Torvalds nubus_advance(ptr, len, map);
1261da177e4SLinus Torvalds else if (len < 0)
1271da177e4SLinus Torvalds nubus_rewind(ptr, -len, map);
12885cc313aSFinn Thain
12985cc313aSFinn Thain if (((unsigned long)*ptr & 0xFF000000) != slot_space)
13085cc313aSFinn Thain pr_err("%s: moved out of slot address space!\n", __func__);
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds
1331da177e4SLinus Torvalds /* Now, functions to read the sResource tree */
1341da177e4SLinus Torvalds
1351da177e4SLinus Torvalds /* Each sResource entry consists of a 1-byte ID and a 3-byte data
1361da177e4SLinus Torvalds field. If that data field contains an offset, then obviously we
1371da177e4SLinus Torvalds have to expand it from a 24-bit signed number to a 32-bit signed
1381da177e4SLinus Torvalds number. */
1391da177e4SLinus Torvalds
nubus_expand32(long foo)1401da177e4SLinus Torvalds static inline long nubus_expand32(long foo)
1411da177e4SLinus Torvalds {
1421da177e4SLinus Torvalds if (foo & 0x00800000) /* 24bit negative */
1431da177e4SLinus Torvalds foo |= 0xFF000000;
1441da177e4SLinus Torvalds return foo;
1451da177e4SLinus Torvalds }
1461da177e4SLinus Torvalds
nubus_rom_addr(int slot)1471da177e4SLinus Torvalds static inline void *nubus_rom_addr(int slot)
1481da177e4SLinus Torvalds {
1491da177e4SLinus Torvalds /*
1501da177e4SLinus Torvalds * Returns the first byte after the card. We then walk
1511da177e4SLinus Torvalds * backwards to get the lane register and the config
1521da177e4SLinus Torvalds */
1531da177e4SLinus Torvalds return (void *)(0xF1000000 + (slot << 24));
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds
nubus_dirptr(const struct nubus_dirent * nd)1562f7dd07eSFinn Thain unsigned char *nubus_dirptr(const struct nubus_dirent *nd)
1571da177e4SLinus Torvalds {
1581da177e4SLinus Torvalds unsigned char *p = nd->base;
159f42e5550SFinn Thain
1601da177e4SLinus Torvalds /* Essentially, just step over the bytelanes using whatever
1611da177e4SLinus Torvalds offset we might have found */
1621da177e4SLinus Torvalds nubus_move(&p, nubus_expand32(nd->data), nd->mask);
1631da177e4SLinus Torvalds /* And return the value */
1641da177e4SLinus Torvalds return p;
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds
1671da177e4SLinus Torvalds /* These two are for pulling resource data blocks (i.e. stuff that's
1681da177e4SLinus Torvalds pointed to with offsets) out of the card ROM. */
1691da177e4SLinus Torvalds
nubus_get_rsrc_mem(void * dest,const struct nubus_dirent * dirent,unsigned int len)1701da177e4SLinus Torvalds void nubus_get_rsrc_mem(void *dest, const struct nubus_dirent *dirent,
1712f828fb2SFinn Thain unsigned int len)
1721da177e4SLinus Torvalds {
17351b67a6eSHimanshu Jha unsigned char *t = dest;
1741da177e4SLinus Torvalds unsigned char *p = nubus_dirptr(dirent);
175f42e5550SFinn Thain
176f42e5550SFinn Thain while (len) {
1771da177e4SLinus Torvalds *t++ = nubus_get_rom(&p, 1, dirent->mask);
1781da177e4SLinus Torvalds len--;
1791da177e4SLinus Torvalds }
1801da177e4SLinus Torvalds }
18199ffab81SAdrian Bunk EXPORT_SYMBOL(nubus_get_rsrc_mem);
1821da177e4SLinus Torvalds
nubus_get_rsrc_str(char * dest,const struct nubus_dirent * dirent,unsigned int len)1832f7dd07eSFinn Thain unsigned int nubus_get_rsrc_str(char *dest, const struct nubus_dirent *dirent,
1842f828fb2SFinn Thain unsigned int len)
1851da177e4SLinus Torvalds {
1862f828fb2SFinn Thain char *t = dest;
1871da177e4SLinus Torvalds unsigned char *p = nubus_dirptr(dirent);
188f42e5550SFinn Thain
1892f828fb2SFinn Thain while (len > 1) {
1902f828fb2SFinn Thain unsigned char c = nubus_get_rom(&p, 1, dirent->mask);
1912f828fb2SFinn Thain
1922f828fb2SFinn Thain if (!c)
1931da177e4SLinus Torvalds break;
1942f828fb2SFinn Thain *t++ = c;
1951da177e4SLinus Torvalds len--;
1961da177e4SLinus Torvalds }
1972f828fb2SFinn Thain if (len > 0)
1982f828fb2SFinn Thain *t = '\0';
1992f7dd07eSFinn Thain return t - dest;
2001da177e4SLinus Torvalds }
20199ffab81SAdrian Bunk EXPORT_SYMBOL(nubus_get_rsrc_str);
2021da177e4SLinus Torvalds
nubus_seq_write_rsrc_mem(struct seq_file * m,const struct nubus_dirent * dirent,unsigned int len)2032f7dd07eSFinn Thain void nubus_seq_write_rsrc_mem(struct seq_file *m,
2042f7dd07eSFinn Thain const struct nubus_dirent *dirent,
2052f7dd07eSFinn Thain unsigned int len)
2062f7dd07eSFinn Thain {
2072f7dd07eSFinn Thain unsigned long buf[32];
2082f7dd07eSFinn Thain unsigned int buf_size = sizeof(buf);
2092f7dd07eSFinn Thain unsigned char *p = nubus_dirptr(dirent);
2102f7dd07eSFinn Thain
2112f7dd07eSFinn Thain /* If possible, write out full buffers */
2122f7dd07eSFinn Thain while (len >= buf_size) {
2132f7dd07eSFinn Thain unsigned int i;
2142f7dd07eSFinn Thain
2152f7dd07eSFinn Thain for (i = 0; i < ARRAY_SIZE(buf); i++)
2162f7dd07eSFinn Thain buf[i] = nubus_get_rom(&p, sizeof(buf[0]),
2172f7dd07eSFinn Thain dirent->mask);
2182f7dd07eSFinn Thain seq_write(m, buf, buf_size);
2192f7dd07eSFinn Thain len -= buf_size;
2202f7dd07eSFinn Thain }
2212f7dd07eSFinn Thain /* If not, write out individual bytes */
2222f7dd07eSFinn Thain while (len--)
2232f7dd07eSFinn Thain seq_putc(m, nubus_get_rom(&p, 1, dirent->mask));
2242f7dd07eSFinn Thain }
2252f7dd07eSFinn Thain
nubus_get_root_dir(const struct nubus_board * board,struct nubus_dir * dir)2261da177e4SLinus Torvalds int nubus_get_root_dir(const struct nubus_board *board,
2271da177e4SLinus Torvalds struct nubus_dir *dir)
2281da177e4SLinus Torvalds {
2291da177e4SLinus Torvalds dir->ptr = dir->base = board->directory;
2301da177e4SLinus Torvalds dir->done = 0;
2311da177e4SLinus Torvalds dir->mask = board->lanes;
2321da177e4SLinus Torvalds return 0;
2331da177e4SLinus Torvalds }
23499ffab81SAdrian Bunk EXPORT_SYMBOL(nubus_get_root_dir);
2351da177e4SLinus Torvalds
2361da177e4SLinus Torvalds /* This is a slyly renamed version of the above */
nubus_get_func_dir(const struct nubus_rsrc * fres,struct nubus_dir * dir)237189e19e8SFinn Thain int nubus_get_func_dir(const struct nubus_rsrc *fres, struct nubus_dir *dir)
2381da177e4SLinus Torvalds {
239189e19e8SFinn Thain dir->ptr = dir->base = fres->directory;
2401da177e4SLinus Torvalds dir->done = 0;
241189e19e8SFinn Thain dir->mask = fres->board->lanes;
2421da177e4SLinus Torvalds return 0;
2431da177e4SLinus Torvalds }
24499ffab81SAdrian Bunk EXPORT_SYMBOL(nubus_get_func_dir);
2451da177e4SLinus Torvalds
nubus_get_board_dir(const struct nubus_board * board,struct nubus_dir * dir)2461da177e4SLinus Torvalds int nubus_get_board_dir(const struct nubus_board *board,
2471da177e4SLinus Torvalds struct nubus_dir *dir)
2481da177e4SLinus Torvalds {
2491da177e4SLinus Torvalds struct nubus_dirent ent;
2501da177e4SLinus Torvalds
2511da177e4SLinus Torvalds dir->ptr = dir->base = board->directory;
2521da177e4SLinus Torvalds dir->done = 0;
2531da177e4SLinus Torvalds dir->mask = board->lanes;
2541da177e4SLinus Torvalds
2551da177e4SLinus Torvalds /* Now dereference it (the first directory is always the board
2561da177e4SLinus Torvalds directory) */
2571da177e4SLinus Torvalds if (nubus_readdir(dir, &ent) == -1)
2581da177e4SLinus Torvalds return -1;
2591da177e4SLinus Torvalds if (nubus_get_subdir(&ent, dir) == -1)
2601da177e4SLinus Torvalds return -1;
2611da177e4SLinus Torvalds return 0;
2621da177e4SLinus Torvalds }
26399ffab81SAdrian Bunk EXPORT_SYMBOL(nubus_get_board_dir);
2641da177e4SLinus Torvalds
nubus_get_subdir(const struct nubus_dirent * ent,struct nubus_dir * dir)2651da177e4SLinus Torvalds int nubus_get_subdir(const struct nubus_dirent *ent,
2661da177e4SLinus Torvalds struct nubus_dir *dir)
2671da177e4SLinus Torvalds {
2681da177e4SLinus Torvalds dir->ptr = dir->base = nubus_dirptr(ent);
2691da177e4SLinus Torvalds dir->done = 0;
2701da177e4SLinus Torvalds dir->mask = ent->mask;
2711da177e4SLinus Torvalds return 0;
2721da177e4SLinus Torvalds }
27399ffab81SAdrian Bunk EXPORT_SYMBOL(nubus_get_subdir);
2741da177e4SLinus Torvalds
nubus_readdir(struct nubus_dir * nd,struct nubus_dirent * ent)2751da177e4SLinus Torvalds int nubus_readdir(struct nubus_dir *nd, struct nubus_dirent *ent)
2761da177e4SLinus Torvalds {
2771da177e4SLinus Torvalds u32 resid;
278f42e5550SFinn Thain
2791da177e4SLinus Torvalds if (nd->done)
2801da177e4SLinus Torvalds return -1;
2811da177e4SLinus Torvalds
2821da177e4SLinus Torvalds /* Do this first, otherwise nubus_rewind & co are off by 4 */
2831da177e4SLinus Torvalds ent->base = nd->ptr;
2841da177e4SLinus Torvalds
2851da177e4SLinus Torvalds /* This moves nd->ptr forward */
2861da177e4SLinus Torvalds resid = nubus_get_rom(&nd->ptr, 4, nd->mask);
2871da177e4SLinus Torvalds
2881da177e4SLinus Torvalds /* EOL marker, as per the Apple docs */
289f42e5550SFinn Thain if ((resid & 0xff000000) == 0xff000000) {
2901da177e4SLinus Torvalds /* Mark it as done */
2911da177e4SLinus Torvalds nd->done = 1;
2921da177e4SLinus Torvalds return -1;
2931da177e4SLinus Torvalds }
2941da177e4SLinus Torvalds
2951da177e4SLinus Torvalds /* First byte is the resource ID */
2961da177e4SLinus Torvalds ent->type = resid >> 24;
2971da177e4SLinus Torvalds /* Low 3 bytes might contain data (or might not) */
2981da177e4SLinus Torvalds ent->data = resid & 0xffffff;
2991da177e4SLinus Torvalds ent->mask = nd->mask;
3001da177e4SLinus Torvalds return 0;
3011da177e4SLinus Torvalds }
30299ffab81SAdrian Bunk EXPORT_SYMBOL(nubus_readdir);
3031da177e4SLinus Torvalds
nubus_rewinddir(struct nubus_dir * dir)3041da177e4SLinus Torvalds int nubus_rewinddir(struct nubus_dir *dir)
3051da177e4SLinus Torvalds {
3061da177e4SLinus Torvalds dir->ptr = dir->base;
307e36b9913SDavid Huggins-Daines dir->done = 0;
3081da177e4SLinus Torvalds return 0;
3091da177e4SLinus Torvalds }
31099ffab81SAdrian Bunk EXPORT_SYMBOL(nubus_rewinddir);
3111da177e4SLinus Torvalds
3121da177e4SLinus Torvalds /* Driver interface functions, more or less like in pci.c */
3131da177e4SLinus Torvalds
nubus_first_rsrc_or_null(void)31441b84816SFinn Thain struct nubus_rsrc *nubus_first_rsrc_or_null(void)
3151da177e4SLinus Torvalds {
31641b84816SFinn Thain return list_first_entry_or_null(&nubus_func_rsrcs, struct nubus_rsrc,
31741b84816SFinn Thain list);
3181da177e4SLinus Torvalds }
31941b84816SFinn Thain EXPORT_SYMBOL(nubus_first_rsrc_or_null);
3201da177e4SLinus Torvalds
nubus_next_rsrc_or_null(struct nubus_rsrc * from)32141b84816SFinn Thain struct nubus_rsrc *nubus_next_rsrc_or_null(struct nubus_rsrc *from)
3221da177e4SLinus Torvalds {
32341b84816SFinn Thain if (list_is_last(&from->list, &nubus_func_rsrcs))
3241da177e4SLinus Torvalds return NULL;
32541b84816SFinn Thain return list_next_entry(from, list);
3261da177e4SLinus Torvalds }
32741b84816SFinn Thain EXPORT_SYMBOL(nubus_next_rsrc_or_null);
3281da177e4SLinus Torvalds
3291da177e4SLinus Torvalds int
nubus_find_rsrc(struct nubus_dir * dir,unsigned char rsrc_type,struct nubus_dirent * ent)3301da177e4SLinus Torvalds nubus_find_rsrc(struct nubus_dir *dir, unsigned char rsrc_type,
3311da177e4SLinus Torvalds struct nubus_dirent *ent)
3321da177e4SLinus Torvalds {
3331da177e4SLinus Torvalds while (nubus_readdir(dir, ent) != -1) {
3341da177e4SLinus Torvalds if (ent->type == rsrc_type)
3351da177e4SLinus Torvalds return 0;
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds return -1;
3381da177e4SLinus Torvalds }
33999ffab81SAdrian Bunk EXPORT_SYMBOL(nubus_find_rsrc);
3401da177e4SLinus Torvalds
3411da177e4SLinus Torvalds /* Initialization functions - decide which slots contain stuff worth
3421da177e4SLinus Torvalds looking at, and print out lots and lots of information from the
3431da177e4SLinus Torvalds resource blocks. */
3441da177e4SLinus Torvalds
nubus_get_block_rsrc_dir(struct nubus_board * board,struct proc_dir_entry * procdir,const struct nubus_dirent * parent)345883b8cb3SFinn Thain static int __init nubus_get_block_rsrc_dir(struct nubus_board *board,
3462f7dd07eSFinn Thain struct proc_dir_entry *procdir,
347883b8cb3SFinn Thain const struct nubus_dirent *parent)
348883b8cb3SFinn Thain {
349883b8cb3SFinn Thain struct nubus_dir dir;
350883b8cb3SFinn Thain struct nubus_dirent ent;
351883b8cb3SFinn Thain
352883b8cb3SFinn Thain nubus_get_subdir(parent, &dir);
3532f7dd07eSFinn Thain dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board);
354883b8cb3SFinn Thain
355883b8cb3SFinn Thain while (nubus_readdir(&dir, &ent) != -1) {
356883b8cb3SFinn Thain u32 size;
357883b8cb3SFinn Thain
358883b8cb3SFinn Thain nubus_get_rsrc_mem(&size, &ent, 4);
359883b8cb3SFinn Thain pr_debug(" block (0x%x), size %d\n", ent.type, size);
3602f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, size);
361883b8cb3SFinn Thain }
362883b8cb3SFinn Thain return 0;
363883b8cb3SFinn Thain }
364883b8cb3SFinn Thain
nubus_get_display_vidmode(struct nubus_board * board,struct proc_dir_entry * procdir,const struct nubus_dirent * parent)365883b8cb3SFinn Thain static int __init nubus_get_display_vidmode(struct nubus_board *board,
3662f7dd07eSFinn Thain struct proc_dir_entry *procdir,
367883b8cb3SFinn Thain const struct nubus_dirent *parent)
368883b8cb3SFinn Thain {
369883b8cb3SFinn Thain struct nubus_dir dir;
370883b8cb3SFinn Thain struct nubus_dirent ent;
371883b8cb3SFinn Thain
372883b8cb3SFinn Thain nubus_get_subdir(parent, &dir);
3732f7dd07eSFinn Thain dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board);
374883b8cb3SFinn Thain
375883b8cb3SFinn Thain while (nubus_readdir(&dir, &ent) != -1) {
376883b8cb3SFinn Thain switch (ent.type) {
377883b8cb3SFinn Thain case 1: /* mVidParams */
378883b8cb3SFinn Thain case 2: /* mTable */
379883b8cb3SFinn Thain {
380883b8cb3SFinn Thain u32 size;
381883b8cb3SFinn Thain
382883b8cb3SFinn Thain nubus_get_rsrc_mem(&size, &ent, 4);
383883b8cb3SFinn Thain pr_debug(" block (0x%x), size %d\n", ent.type,
384883b8cb3SFinn Thain size);
3852f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, size);
386883b8cb3SFinn Thain break;
387883b8cb3SFinn Thain }
388883b8cb3SFinn Thain default:
389883b8cb3SFinn Thain pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
390883b8cb3SFinn Thain ent.type, ent.data);
3912f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, 0);
392883b8cb3SFinn Thain }
393883b8cb3SFinn Thain }
394883b8cb3SFinn Thain return 0;
395883b8cb3SFinn Thain }
396883b8cb3SFinn Thain
nubus_get_display_resource(struct nubus_rsrc * fres,struct proc_dir_entry * procdir,const struct nubus_dirent * ent)397189e19e8SFinn Thain static int __init nubus_get_display_resource(struct nubus_rsrc *fres,
3982f7dd07eSFinn Thain struct proc_dir_entry *procdir,
3991da177e4SLinus Torvalds const struct nubus_dirent *ent)
4001da177e4SLinus Torvalds {
4011da177e4SLinus Torvalds switch (ent->type) {
4021da177e4SLinus Torvalds case NUBUS_RESID_GAMMADIR:
403f53bad08SFinn Thain pr_debug(" gamma directory offset: 0x%06x\n", ent->data);
404189e19e8SFinn Thain nubus_get_block_rsrc_dir(fres->board, procdir, ent);
4051da177e4SLinus Torvalds break;
4061da177e4SLinus Torvalds case 0x0080 ... 0x0085:
407f53bad08SFinn Thain pr_debug(" mode 0x%02x info offset: 0x%06x\n",
4081da177e4SLinus Torvalds ent->type, ent->data);
409189e19e8SFinn Thain nubus_get_display_vidmode(fres->board, procdir, ent);
4101da177e4SLinus Torvalds break;
4111da177e4SLinus Torvalds default:
412f53bad08SFinn Thain pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
4131da177e4SLinus Torvalds ent->type, ent->data);
4142f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(procdir, ent, 0);
4151da177e4SLinus Torvalds }
4161da177e4SLinus Torvalds return 0;
4171da177e4SLinus Torvalds }
4181da177e4SLinus Torvalds
nubus_get_network_resource(struct nubus_rsrc * fres,struct proc_dir_entry * procdir,const struct nubus_dirent * ent)419189e19e8SFinn Thain static int __init nubus_get_network_resource(struct nubus_rsrc *fres,
4202f7dd07eSFinn Thain struct proc_dir_entry *procdir,
4211da177e4SLinus Torvalds const struct nubus_dirent *ent)
4221da177e4SLinus Torvalds {
4231da177e4SLinus Torvalds switch (ent->type) {
4241da177e4SLinus Torvalds case NUBUS_RESID_MAC_ADDRESS:
4251da177e4SLinus Torvalds {
4261da177e4SLinus Torvalds char addr[6];
4271da177e4SLinus Torvalds
4281da177e4SLinus Torvalds nubus_get_rsrc_mem(addr, ent, 6);
429f53bad08SFinn Thain pr_debug(" MAC address: %pM\n", addr);
4302f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(procdir, ent, 6);
4311da177e4SLinus Torvalds break;
4321da177e4SLinus Torvalds }
4331da177e4SLinus Torvalds default:
434f53bad08SFinn Thain pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
4351da177e4SLinus Torvalds ent->type, ent->data);
4362f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(procdir, ent, 0);
4371da177e4SLinus Torvalds }
4381da177e4SLinus Torvalds return 0;
4391da177e4SLinus Torvalds }
4401da177e4SLinus Torvalds
nubus_get_cpu_resource(struct nubus_rsrc * fres,struct proc_dir_entry * procdir,const struct nubus_dirent * ent)441189e19e8SFinn Thain static int __init nubus_get_cpu_resource(struct nubus_rsrc *fres,
4422f7dd07eSFinn Thain struct proc_dir_entry *procdir,
4431da177e4SLinus Torvalds const struct nubus_dirent *ent)
4441da177e4SLinus Torvalds {
4451da177e4SLinus Torvalds switch (ent->type) {
4461da177e4SLinus Torvalds case NUBUS_RESID_MEMINFO:
4471da177e4SLinus Torvalds {
4481da177e4SLinus Torvalds unsigned long meminfo[2];
449f42e5550SFinn Thain
4501da177e4SLinus Torvalds nubus_get_rsrc_mem(&meminfo, ent, 8);
451f53bad08SFinn Thain pr_debug(" memory: [ 0x%08lx 0x%08lx ]\n",
4521da177e4SLinus Torvalds meminfo[0], meminfo[1]);
4532f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(procdir, ent, 8);
4541da177e4SLinus Torvalds break;
4551da177e4SLinus Torvalds }
4561da177e4SLinus Torvalds case NUBUS_RESID_ROMINFO:
4571da177e4SLinus Torvalds {
4581da177e4SLinus Torvalds unsigned long rominfo[2];
459f42e5550SFinn Thain
4601da177e4SLinus Torvalds nubus_get_rsrc_mem(&rominfo, ent, 8);
461f53bad08SFinn Thain pr_debug(" ROM: [ 0x%08lx 0x%08lx ]\n",
4621da177e4SLinus Torvalds rominfo[0], rominfo[1]);
4632f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(procdir, ent, 8);
4641da177e4SLinus Torvalds break;
4651da177e4SLinus Torvalds }
4661da177e4SLinus Torvalds default:
467f53bad08SFinn Thain pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
4681da177e4SLinus Torvalds ent->type, ent->data);
4692f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(procdir, ent, 0);
4701da177e4SLinus Torvalds }
4711da177e4SLinus Torvalds return 0;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds
nubus_get_private_resource(struct nubus_rsrc * fres,struct proc_dir_entry * procdir,const struct nubus_dirent * ent)474189e19e8SFinn Thain static int __init nubus_get_private_resource(struct nubus_rsrc *fres,
4752f7dd07eSFinn Thain struct proc_dir_entry *procdir,
4761da177e4SLinus Torvalds const struct nubus_dirent *ent)
4771da177e4SLinus Torvalds {
478189e19e8SFinn Thain switch (fres->category) {
4791da177e4SLinus Torvalds case NUBUS_CAT_DISPLAY:
480189e19e8SFinn Thain nubus_get_display_resource(fres, procdir, ent);
4811da177e4SLinus Torvalds break;
4821da177e4SLinus Torvalds case NUBUS_CAT_NETWORK:
483189e19e8SFinn Thain nubus_get_network_resource(fres, procdir, ent);
4841da177e4SLinus Torvalds break;
4851da177e4SLinus Torvalds case NUBUS_CAT_CPU:
486189e19e8SFinn Thain nubus_get_cpu_resource(fres, procdir, ent);
4871da177e4SLinus Torvalds break;
4881da177e4SLinus Torvalds default:
489f53bad08SFinn Thain pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
4901da177e4SLinus Torvalds ent->type, ent->data);
4912f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(procdir, ent, 0);
4921da177e4SLinus Torvalds }
4931da177e4SLinus Torvalds return 0;
4941da177e4SLinus Torvalds }
4951da177e4SLinus Torvalds
496189e19e8SFinn Thain static struct nubus_rsrc * __init
nubus_get_functional_resource(struct nubus_board * board,int slot,const struct nubus_dirent * parent)497f42e5550SFinn Thain nubus_get_functional_resource(struct nubus_board *board, int slot,
4981da177e4SLinus Torvalds const struct nubus_dirent *parent)
4991da177e4SLinus Torvalds {
5001da177e4SLinus Torvalds struct nubus_dir dir;
5011da177e4SLinus Torvalds struct nubus_dirent ent;
502189e19e8SFinn Thain struct nubus_rsrc *fres;
5031da177e4SLinus Torvalds
504f53bad08SFinn Thain pr_debug(" Functional resource 0x%02x:\n", parent->type);
5051da177e4SLinus Torvalds nubus_get_subdir(parent, &dir);
5062f7dd07eSFinn Thain dir.procdir = nubus_proc_add_rsrc_dir(board->procdir, parent, board);
5071da177e4SLinus Torvalds
5081da177e4SLinus Torvalds /* Actually we should probably panic if this fails */
509189e19e8SFinn Thain fres = kzalloc(sizeof(*fres), GFP_ATOMIC);
510189e19e8SFinn Thain if (!fres)
5111da177e4SLinus Torvalds return NULL;
512189e19e8SFinn Thain fres->resid = parent->type;
513189e19e8SFinn Thain fres->directory = dir.base;
514189e19e8SFinn Thain fres->board = board;
5151da177e4SLinus Torvalds
516f42e5550SFinn Thain while (nubus_readdir(&dir, &ent) != -1) {
517f42e5550SFinn Thain switch (ent.type) {
5181da177e4SLinus Torvalds case NUBUS_RESID_TYPE:
5191da177e4SLinus Torvalds {
5201da177e4SLinus Torvalds unsigned short nbtdata[4];
521f42e5550SFinn Thain
5221da177e4SLinus Torvalds nubus_get_rsrc_mem(nbtdata, &ent, 8);
523189e19e8SFinn Thain fres->category = nbtdata[0];
524189e19e8SFinn Thain fres->type = nbtdata[1];
525189e19e8SFinn Thain fres->dr_sw = nbtdata[2];
526189e19e8SFinn Thain fres->dr_hw = nbtdata[3];
527f53bad08SFinn Thain pr_debug(" type: [cat 0x%x type 0x%x sw 0x%x hw 0x%x]\n",
5281da177e4SLinus Torvalds nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]);
5292f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, 8);
5301da177e4SLinus Torvalds break;
5311da177e4SLinus Torvalds }
5321da177e4SLinus Torvalds case NUBUS_RESID_NAME:
5331da177e4SLinus Torvalds {
5349f97977dSFinn Thain char name[64];
5352f7dd07eSFinn Thain unsigned int len;
5369f97977dSFinn Thain
5372f7dd07eSFinn Thain len = nubus_get_rsrc_str(name, &ent, sizeof(name));
5389f97977dSFinn Thain pr_debug(" name: %s\n", name);
5392f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1);
5401da177e4SLinus Torvalds break;
5411da177e4SLinus Torvalds }
5421da177e4SLinus Torvalds case NUBUS_RESID_DRVRDIR:
5431da177e4SLinus Torvalds {
5441da177e4SLinus Torvalds /* MacOS driver. If we were NetBSD we might
5451da177e4SLinus Torvalds use this :-) */
546883b8cb3SFinn Thain pr_debug(" driver directory offset: 0x%06x\n",
547883b8cb3SFinn Thain ent.data);
5482f7dd07eSFinn Thain nubus_get_block_rsrc_dir(board, dir.procdir, &ent);
5491da177e4SLinus Torvalds break;
5501da177e4SLinus Torvalds }
5511da177e4SLinus Torvalds case NUBUS_RESID_MINOR_BASEOS:
5529f97977dSFinn Thain {
5531da177e4SLinus Torvalds /* We will need this in order to support
5541da177e4SLinus Torvalds multiple framebuffers. It might be handy
5551da177e4SLinus Torvalds for Ethernet as well */
5569f97977dSFinn Thain u32 base_offset;
5579f97977dSFinn Thain
5589f97977dSFinn Thain nubus_get_rsrc_mem(&base_offset, &ent, 4);
5599f97977dSFinn Thain pr_debug(" memory offset: 0x%08x\n", base_offset);
5602f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, 4);
5611da177e4SLinus Torvalds break;
5629f97977dSFinn Thain }
5631da177e4SLinus Torvalds case NUBUS_RESID_MINOR_LENGTH:
5649f97977dSFinn Thain {
5651da177e4SLinus Torvalds /* Ditto */
5669f97977dSFinn Thain u32 length;
5679f97977dSFinn Thain
5689f97977dSFinn Thain nubus_get_rsrc_mem(&length, &ent, 4);
5699f97977dSFinn Thain pr_debug(" memory length: 0x%08x\n", length);
5702f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, 4);
5711da177e4SLinus Torvalds break;
5729f97977dSFinn Thain }
5731da177e4SLinus Torvalds case NUBUS_RESID_FLAGS:
5749f97977dSFinn Thain pr_debug(" flags: 0x%06x\n", ent.data);
5752f7dd07eSFinn Thain nubus_proc_add_rsrc(dir.procdir, &ent);
5761da177e4SLinus Torvalds break;
5771da177e4SLinus Torvalds case NUBUS_RESID_HWDEVID:
5789f97977dSFinn Thain pr_debug(" hwdevid: 0x%06x\n", ent.data);
5792f7dd07eSFinn Thain nubus_proc_add_rsrc(dir.procdir, &ent);
5801da177e4SLinus Torvalds break;
5811da177e4SLinus Torvalds default:
582*72b44f65SFinn Thain if (nubus_populate_procfs)
583*72b44f65SFinn Thain nubus_get_private_resource(fres, dir.procdir,
584*72b44f65SFinn Thain &ent);
5851da177e4SLinus Torvalds }
5861da177e4SLinus Torvalds }
5871da177e4SLinus Torvalds
588189e19e8SFinn Thain return fres;
5891da177e4SLinus Torvalds }
5901da177e4SLinus Torvalds
5911da177e4SLinus Torvalds /* This is *really* cool. */
nubus_get_icon(struct nubus_board * board,struct proc_dir_entry * procdir,const struct nubus_dirent * ent)5921da177e4SLinus Torvalds static int __init nubus_get_icon(struct nubus_board *board,
5932f7dd07eSFinn Thain struct proc_dir_entry *procdir,
5941da177e4SLinus Torvalds const struct nubus_dirent *ent)
5951da177e4SLinus Torvalds {
5961da177e4SLinus Torvalds /* Should be 32x32 if my memory serves me correctly */
597f53bad08SFinn Thain u32 icon[32];
598f53bad08SFinn Thain int i;
5991da177e4SLinus Torvalds
6001da177e4SLinus Torvalds nubus_get_rsrc_mem(&icon, ent, 128);
601f53bad08SFinn Thain pr_debug(" icon:\n");
602f53bad08SFinn Thain for (i = 0; i < 8; i++)
603f53bad08SFinn Thain pr_debug(" %08x %08x %08x %08x\n",
604f53bad08SFinn Thain icon[i * 4 + 0], icon[i * 4 + 1],
605f53bad08SFinn Thain icon[i * 4 + 2], icon[i * 4 + 3]);
6062f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(procdir, ent, 128);
6071da177e4SLinus Torvalds
6081da177e4SLinus Torvalds return 0;
6091da177e4SLinus Torvalds }
6101da177e4SLinus Torvalds
nubus_get_vendorinfo(struct nubus_board * board,struct proc_dir_entry * procdir,const struct nubus_dirent * parent)6111da177e4SLinus Torvalds static int __init nubus_get_vendorinfo(struct nubus_board *board,
6122f7dd07eSFinn Thain struct proc_dir_entry *procdir,
6131da177e4SLinus Torvalds const struct nubus_dirent *parent)
6141da177e4SLinus Torvalds {
6151da177e4SLinus Torvalds struct nubus_dir dir;
6161da177e4SLinus Torvalds struct nubus_dirent ent;
6171da177e4SLinus Torvalds static char *vendor_fields[6] = { "ID", "serial", "revision",
6181da177e4SLinus Torvalds "part", "date", "unknown field" };
6191da177e4SLinus Torvalds
620f53bad08SFinn Thain pr_debug(" vendor info:\n");
6211da177e4SLinus Torvalds nubus_get_subdir(parent, &dir);
6222f7dd07eSFinn Thain dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board);
6231da177e4SLinus Torvalds
624f42e5550SFinn Thain while (nubus_readdir(&dir, &ent) != -1) {
6251da177e4SLinus Torvalds char name[64];
6262f7dd07eSFinn Thain unsigned int len;
6271da177e4SLinus Torvalds
6281da177e4SLinus Torvalds /* These are all strings, we think */
6292f7dd07eSFinn Thain len = nubus_get_rsrc_str(name, &ent, sizeof(name));
6302f828fb2SFinn Thain if (ent.type < 1 || ent.type > 5)
6311da177e4SLinus Torvalds ent.type = 5;
632f53bad08SFinn Thain pr_debug(" %s: %s\n", vendor_fields[ent.type - 1], name);
6332f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1);
6341da177e4SLinus Torvalds }
6351da177e4SLinus Torvalds return 0;
6361da177e4SLinus Torvalds }
6371da177e4SLinus Torvalds
nubus_get_board_resource(struct nubus_board * board,int slot,const struct nubus_dirent * parent)6381da177e4SLinus Torvalds static int __init nubus_get_board_resource(struct nubus_board *board, int slot,
6391da177e4SLinus Torvalds const struct nubus_dirent *parent)
6401da177e4SLinus Torvalds {
6411da177e4SLinus Torvalds struct nubus_dir dir;
6421da177e4SLinus Torvalds struct nubus_dirent ent;
6431da177e4SLinus Torvalds
644f53bad08SFinn Thain pr_debug(" Board resource 0x%02x:\n", parent->type);
6451da177e4SLinus Torvalds nubus_get_subdir(parent, &dir);
6462f7dd07eSFinn Thain dir.procdir = nubus_proc_add_rsrc_dir(board->procdir, parent, board);
6471da177e4SLinus Torvalds
648f42e5550SFinn Thain while (nubus_readdir(&dir, &ent) != -1) {
6491da177e4SLinus Torvalds switch (ent.type) {
6501da177e4SLinus Torvalds case NUBUS_RESID_TYPE:
6511da177e4SLinus Torvalds {
6521da177e4SLinus Torvalds unsigned short nbtdata[4];
6531da177e4SLinus Torvalds /* This type is always the same, and is not
6541da177e4SLinus Torvalds useful except insofar as it tells us that
6551da177e4SLinus Torvalds we really are looking at a board resource. */
6561da177e4SLinus Torvalds nubus_get_rsrc_mem(nbtdata, &ent, 8);
657f53bad08SFinn Thain pr_debug(" type: [cat 0x%x type 0x%x sw 0x%x hw 0x%x]\n",
65871ae40e4SDavid Huggins-Daines nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]);
6591da177e4SLinus Torvalds if (nbtdata[0] != 1 || nbtdata[1] != 0 ||
6601da177e4SLinus Torvalds nbtdata[2] != 0 || nbtdata[3] != 0)
661d7811a36SFinn Thain pr_err("Slot %X: sResource is not a board resource!\n",
662d7811a36SFinn Thain slot);
6632f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, 8);
6641da177e4SLinus Torvalds break;
6651da177e4SLinus Torvalds }
6661da177e4SLinus Torvalds case NUBUS_RESID_NAME:
6672f7dd07eSFinn Thain {
6682f7dd07eSFinn Thain unsigned int len;
6692f7dd07eSFinn Thain
6702f7dd07eSFinn Thain len = nubus_get_rsrc_str(board->name, &ent,
6712f828fb2SFinn Thain sizeof(board->name));
672f53bad08SFinn Thain pr_debug(" name: %s\n", board->name);
6732f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1);
6741da177e4SLinus Torvalds break;
6752f7dd07eSFinn Thain }
6761da177e4SLinus Torvalds case NUBUS_RESID_ICON:
6772f7dd07eSFinn Thain nubus_get_icon(board, dir.procdir, &ent);
6781da177e4SLinus Torvalds break;
6791da177e4SLinus Torvalds case NUBUS_RESID_BOARDID:
680f53bad08SFinn Thain pr_debug(" board id: 0x%x\n", ent.data);
6812f7dd07eSFinn Thain nubus_proc_add_rsrc(dir.procdir, &ent);
6821da177e4SLinus Torvalds break;
6831da177e4SLinus Torvalds case NUBUS_RESID_PRIMARYINIT:
684f53bad08SFinn Thain pr_debug(" primary init offset: 0x%06x\n", ent.data);
6852f7dd07eSFinn Thain nubus_proc_add_rsrc(dir.procdir, &ent);
6861da177e4SLinus Torvalds break;
6871da177e4SLinus Torvalds case NUBUS_RESID_VENDORINFO:
6882f7dd07eSFinn Thain nubus_get_vendorinfo(board, dir.procdir, &ent);
6891da177e4SLinus Torvalds break;
6901da177e4SLinus Torvalds case NUBUS_RESID_FLAGS:
691f53bad08SFinn Thain pr_debug(" flags: 0x%06x\n", ent.data);
6922f7dd07eSFinn Thain nubus_proc_add_rsrc(dir.procdir, &ent);
6931da177e4SLinus Torvalds break;
6941da177e4SLinus Torvalds case NUBUS_RESID_HWDEVID:
695f53bad08SFinn Thain pr_debug(" hwdevid: 0x%06x\n", ent.data);
6962f7dd07eSFinn Thain nubus_proc_add_rsrc(dir.procdir, &ent);
6971da177e4SLinus Torvalds break;
6981da177e4SLinus Torvalds case NUBUS_RESID_SECONDINIT:
699f53bad08SFinn Thain pr_debug(" secondary init offset: 0x%06x\n",
700f53bad08SFinn Thain ent.data);
7012f7dd07eSFinn Thain nubus_proc_add_rsrc(dir.procdir, &ent);
7021da177e4SLinus Torvalds break;
7031da177e4SLinus Torvalds /* WTF isn't this in the functional resources? */
7041da177e4SLinus Torvalds case NUBUS_RESID_VIDNAMES:
705883b8cb3SFinn Thain pr_debug(" vidnames directory offset: 0x%06x\n",
706883b8cb3SFinn Thain ent.data);
7072f7dd07eSFinn Thain nubus_get_block_rsrc_dir(board, dir.procdir, &ent);
7081da177e4SLinus Torvalds break;
7091da177e4SLinus Torvalds /* Same goes for this */
7101da177e4SLinus Torvalds case NUBUS_RESID_VIDMODES:
711f53bad08SFinn Thain pr_debug(" video mode parameter directory offset: 0x%06x\n",
7121da177e4SLinus Torvalds ent.data);
7132f7dd07eSFinn Thain nubus_proc_add_rsrc(dir.procdir, &ent);
7141da177e4SLinus Torvalds break;
7151da177e4SLinus Torvalds default:
716f53bad08SFinn Thain pr_debug(" unknown resource 0x%02x, data 0x%06x\n",
7171da177e4SLinus Torvalds ent.type, ent.data);
7182f7dd07eSFinn Thain nubus_proc_add_rsrc_mem(dir.procdir, &ent, 0);
7191da177e4SLinus Torvalds }
7201da177e4SLinus Torvalds }
7211da177e4SLinus Torvalds return 0;
7221da177e4SLinus Torvalds }
7231da177e4SLinus Torvalds
nubus_add_board(int slot,int bytelanes)7247f86c765SFinn Thain static void __init nubus_add_board(int slot, int bytelanes)
7251da177e4SLinus Torvalds {
7261da177e4SLinus Torvalds struct nubus_board *board;
7271da177e4SLinus Torvalds unsigned char *rp;
7281da177e4SLinus Torvalds unsigned long dpat;
7291da177e4SLinus Torvalds struct nubus_dir dir;
7301da177e4SLinus Torvalds struct nubus_dirent ent;
731d7811a36SFinn Thain int prev_resid = -1;
7321da177e4SLinus Torvalds
7331da177e4SLinus Torvalds /* Move to the start of the format block */
7341da177e4SLinus Torvalds rp = nubus_rom_addr(slot);
7351da177e4SLinus Torvalds nubus_rewind(&rp, FORMAT_BLOCK_SIZE, bytelanes);
7361da177e4SLinus Torvalds
7371da177e4SLinus Torvalds /* Actually we should probably panic if this fails */
738dd00cc48SYoann Padioleau if ((board = kzalloc(sizeof(*board), GFP_ATOMIC)) == NULL)
7397f86c765SFinn Thain return;
7401da177e4SLinus Torvalds board->fblock = rp;
7411da177e4SLinus Torvalds
7421da177e4SLinus Torvalds /* Dump the format block for debugging purposes */
74371ae40e4SDavid Huggins-Daines pr_debug("Slot %X, format block at 0x%p:\n", slot, rp);
744f53bad08SFinn Thain pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes));
745f53bad08SFinn Thain pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes));
746f53bad08SFinn Thain pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes));
74771ae40e4SDavid Huggins-Daines pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes));
74871ae40e4SDavid Huggins-Daines pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes));
74971ae40e4SDavid Huggins-Daines pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes));
75071ae40e4SDavid Huggins-Daines pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes));
75171ae40e4SDavid Huggins-Daines pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes));
7521da177e4SLinus Torvalds rp = board->fblock;
7531da177e4SLinus Torvalds
7541da177e4SLinus Torvalds board->slot = slot;
7551da177e4SLinus Torvalds board->slot_addr = (unsigned long)nubus_slot_addr(slot);
7561da177e4SLinus Torvalds board->doffset = nubus_get_rom(&rp, 4, bytelanes);
7571da177e4SLinus Torvalds /* rom_length is *supposed* to be the total length of the
7581da177e4SLinus Torvalds * ROM. In practice it is the "amount of ROM used to compute
7591da177e4SLinus Torvalds * the CRC." So some jokers decide to set it to zero and
7601da177e4SLinus Torvalds * set the crc to zero so they don't have to do any math.
7611da177e4SLinus Torvalds * See the Performa 460 ROM, for example. Those Apple "engineers".
7621da177e4SLinus Torvalds */
7631da177e4SLinus Torvalds board->rom_length = nubus_get_rom(&rp, 4, bytelanes);
7641da177e4SLinus Torvalds board->crc = nubus_get_rom(&rp, 4, bytelanes);
7651da177e4SLinus Torvalds board->rev = nubus_get_rom(&rp, 1, bytelanes);
7661da177e4SLinus Torvalds board->format = nubus_get_rom(&rp, 1, bytelanes);
7671da177e4SLinus Torvalds board->lanes = bytelanes;
7681da177e4SLinus Torvalds
7691da177e4SLinus Torvalds /* Directory offset should be small and negative... */
7701da177e4SLinus Torvalds if (!(board->doffset & 0x00FF0000))
771d7811a36SFinn Thain pr_warn("Slot %X: Dodgy doffset!\n", slot);
7721da177e4SLinus Torvalds dpat = nubus_get_rom(&rp, 4, bytelanes);
7731da177e4SLinus Torvalds if (dpat != NUBUS_TEST_PATTERN)
774d7811a36SFinn Thain pr_warn("Slot %X: Wrong test pattern %08lx!\n", slot, dpat);
7751da177e4SLinus Torvalds
7761da177e4SLinus Torvalds /*
7771da177e4SLinus Torvalds * I wonder how the CRC is meant to work -
7781da177e4SLinus Torvalds * any takers ?
7791da177e4SLinus Torvalds * CSA: According to MAC docs, not all cards pass the CRC anyway,
7801da177e4SLinus Torvalds * since the initial Macintosh ROM releases skipped the check.
7811da177e4SLinus Torvalds */
7821da177e4SLinus Torvalds
783475e6e15SDavid Huggins-Daines /* Set up the directory pointer */
784475e6e15SDavid Huggins-Daines board->directory = board->fblock;
785475e6e15SDavid Huggins-Daines nubus_move(&board->directory, nubus_expand32(board->doffset),
786475e6e15SDavid Huggins-Daines board->lanes);
787475e6e15SDavid Huggins-Daines
7881da177e4SLinus Torvalds nubus_get_root_dir(board, &dir);
7891da177e4SLinus Torvalds
7901da177e4SLinus Torvalds /* We're ready to rock */
791f53bad08SFinn Thain pr_debug("Slot %X resources:\n", slot);
7921da177e4SLinus Torvalds
7931da177e4SLinus Torvalds /* Each slot should have one board resource and any number of
794189e19e8SFinn Thain * functional resources. So we'll fill in some fields in the
795189e19e8SFinn Thain * struct nubus_board from the board resource, then walk down
796189e19e8SFinn Thain * the list of functional resources, spinning out a nubus_rsrc
797189e19e8SFinn Thain * for each of them.
798189e19e8SFinn Thain */
7991da177e4SLinus Torvalds if (nubus_readdir(&dir, &ent) == -1) {
8001da177e4SLinus Torvalds /* We can't have this! */
801d7811a36SFinn Thain pr_err("Slot %X: Board resource not found!\n", slot);
8027f86c765SFinn Thain kfree(board);
8037f86c765SFinn Thain return;
8041da177e4SLinus Torvalds }
8051da177e4SLinus Torvalds
806d7811a36SFinn Thain if (ent.type < 1 || ent.type > 127)
807d7811a36SFinn Thain pr_warn("Slot %X: Board resource ID is invalid!\n", slot);
808d7811a36SFinn Thain
8092f7dd07eSFinn Thain board->procdir = nubus_proc_add_board(board);
8102f7dd07eSFinn Thain
811d7811a36SFinn Thain nubus_get_board_resource(board, slot, &ent);
812d7811a36SFinn Thain
8131da177e4SLinus Torvalds while (nubus_readdir(&dir, &ent) != -1) {
814189e19e8SFinn Thain struct nubus_rsrc *fres;
815f42e5550SFinn Thain
816189e19e8SFinn Thain fres = nubus_get_functional_resource(board, slot, &ent);
817189e19e8SFinn Thain if (fres == NULL)
8181da177e4SLinus Torvalds continue;
8191da177e4SLinus Torvalds
820d7811a36SFinn Thain /* Resources should appear in ascending ID order. This sanity
821d7811a36SFinn Thain * check prevents duplicate resource IDs.
822d7811a36SFinn Thain */
823189e19e8SFinn Thain if (fres->resid <= prev_resid) {
824189e19e8SFinn Thain kfree(fres);
825d7811a36SFinn Thain continue;
826d7811a36SFinn Thain }
827189e19e8SFinn Thain prev_resid = fres->resid;
828d7811a36SFinn Thain
82941b84816SFinn Thain list_add_tail(&fres->list, &nubus_func_rsrcs);
8301da177e4SLinus Torvalds }
8311da177e4SLinus Torvalds
8327f86c765SFinn Thain if (nubus_device_register(board))
8337f86c765SFinn Thain put_device(&board->dev);
8341da177e4SLinus Torvalds }
8351da177e4SLinus Torvalds
nubus_probe_slot(int slot)836460cf95eSFinn Thain static void __init nubus_probe_slot(int slot)
8371da177e4SLinus Torvalds {
8381da177e4SLinus Torvalds unsigned char dp;
8391da177e4SLinus Torvalds unsigned char *rp;
8401da177e4SLinus Torvalds int i;
8411da177e4SLinus Torvalds
8421da177e4SLinus Torvalds rp = nubus_rom_addr(slot);
843f42e5550SFinn Thain for (i = 4; i; i--) {
8441da177e4SLinus Torvalds rp--;
8459f97977dSFinn Thain if (!hwreg_present(rp))
8461da177e4SLinus Torvalds continue;
8471da177e4SLinus Torvalds
8481da177e4SLinus Torvalds dp = *rp;
8491da177e4SLinus Torvalds
8501da177e4SLinus Torvalds /* The last byte of the format block consists of two
8511da177e4SLinus Torvalds nybbles which are "mirror images" of each other.
8521da177e4SLinus Torvalds These show us the valid bytelanes */
8531da177e4SLinus Torvalds if ((((dp >> 4) ^ dp) & 0x0F) != 0x0F)
8541da177e4SLinus Torvalds continue;
8551da177e4SLinus Torvalds /* Check that this value is actually *on* one of the
8561da177e4SLinus Torvalds bytelanes it claims are valid! */
85785cc313aSFinn Thain if (not_useful(rp, dp))
8581da177e4SLinus Torvalds continue;
8591da177e4SLinus Torvalds
8601da177e4SLinus Torvalds /* Looks promising. Let's put it on the list. */
8611da177e4SLinus Torvalds nubus_add_board(slot, dp);
8621da177e4SLinus Torvalds
8631da177e4SLinus Torvalds return;
8641da177e4SLinus Torvalds }
8651da177e4SLinus Torvalds }
8661da177e4SLinus Torvalds
nubus_scan_bus(void)867460cf95eSFinn Thain static void __init nubus_scan_bus(void)
8681da177e4SLinus Torvalds {
8691da177e4SLinus Torvalds int slot;
870f42e5550SFinn Thain
871f53bad08SFinn Thain pr_info("NuBus: Scanning NuBus slots.\n");
872f42e5550SFinn Thain for (slot = 9; slot < 15; slot++) {
8731da177e4SLinus Torvalds nubus_probe_slot(slot);
8741da177e4SLinus Torvalds }
8751da177e4SLinus Torvalds }
8761da177e4SLinus Torvalds
nubus_init(void)8771da177e4SLinus Torvalds static int __init nubus_init(void)
8781da177e4SLinus Torvalds {
8797f86c765SFinn Thain int err;
8807f86c765SFinn Thain
8811da177e4SLinus Torvalds if (!MACH_IS_MAC)
8821da177e4SLinus Torvalds return 0;
8831da177e4SLinus Torvalds
8841da177e4SLinus Torvalds nubus_proc_init();
885bdeeed09SFinn Thain err = nubus_parent_device_register();
8867f86c765SFinn Thain if (err)
8877f86c765SFinn Thain return err;
8882f7dd07eSFinn Thain nubus_scan_bus();
8891da177e4SLinus Torvalds return 0;
8901da177e4SLinus Torvalds }
8911da177e4SLinus Torvalds
8921da177e4SLinus Torvalds subsys_initcall(nubus_init);
893