162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Macintosh Nubus Interface Code 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Originally by Alan Cox 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Mostly rewritten by David Huggins-Daines, C. Scott Ananian, 862306a36Sopenharmony_ci * and others. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/nubus.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/seq_file.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <asm/setup.h> 2162306a36Sopenharmony_ci#include <asm/page.h> 2262306a36Sopenharmony_ci#include <asm/hwtest.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Constants */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* This is, of course, the size in bytelanes, rather than the size in 2762306a36Sopenharmony_ci actual bytes */ 2862306a36Sopenharmony_ci#define FORMAT_BLOCK_SIZE 20 2962306a36Sopenharmony_ci#define ROM_DIR_OFFSET 0x24 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define NUBUS_TEST_PATTERN 0x5A932BC7 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Globals */ 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* The "nubus.populate_procfs" parameter makes slot resources available in 3662306a36Sopenharmony_ci * procfs. It's deprecated and disabled by default because procfs is no longer 3762306a36Sopenharmony_ci * thought to be suitable for that and some board ROMs make it too expensive. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cibool nubus_populate_procfs; 4062306a36Sopenharmony_cimodule_param_named(populate_procfs, nubus_populate_procfs, bool, 0); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciLIST_HEAD(nubus_func_rsrcs); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Meaning of "bytelanes": 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci The card ROM may appear on any or all bytes of each long word in 4762306a36Sopenharmony_ci NuBus memory. The low 4 bits of the "map" value found in the 4862306a36Sopenharmony_ci format block (at the top of the slot address space, as well as at 4962306a36Sopenharmony_ci the top of the MacOS ROM) tells us which bytelanes, i.e. which byte 5062306a36Sopenharmony_ci offsets within each longword, are valid. Thus: 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci A map of 0x0f, as found in the MacOS ROM, means that all bytelanes 5362306a36Sopenharmony_ci are valid. 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci A map of 0xf0 means that no bytelanes are valid (We pray that we 5662306a36Sopenharmony_ci will never encounter this, but stranger things have happened) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci A map of 0xe1 means that only the MSB of each long word is actually 5962306a36Sopenharmony_ci part of the card ROM. (We hope to never encounter NuBus on a 6062306a36Sopenharmony_ci little-endian machine. Again, stranger things have happened) 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci A map of 0x78 means that only the LSB of each long word is valid. 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci Etcetera, etcetera. Hopefully this clears up some confusion over 6562306a36Sopenharmony_ci what the following code actually does. */ 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic inline int not_useful(void *p, int map) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci unsigned long pv = (unsigned long)p; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci pv &= 3; 7262306a36Sopenharmony_ci if (map & (1 << pv)) 7362306a36Sopenharmony_ci return 0; 7462306a36Sopenharmony_ci return 1; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic unsigned long nubus_get_rom(unsigned char **ptr, int len, int map) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci /* This will hold the result */ 8062306a36Sopenharmony_ci unsigned long v = 0; 8162306a36Sopenharmony_ci unsigned char *p = *ptr; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci while (len) { 8462306a36Sopenharmony_ci v <<= 8; 8562306a36Sopenharmony_ci while (not_useful(p, map)) 8662306a36Sopenharmony_ci p++; 8762306a36Sopenharmony_ci v |= *p++; 8862306a36Sopenharmony_ci len--; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci *ptr = p; 9162306a36Sopenharmony_ci return v; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic void nubus_rewind(unsigned char **ptr, int len, int map) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci unsigned char *p = *ptr; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci while (len) { 9962306a36Sopenharmony_ci do { 10062306a36Sopenharmony_ci p--; 10162306a36Sopenharmony_ci } while (not_useful(p, map)); 10262306a36Sopenharmony_ci len--; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci *ptr = p; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void nubus_advance(unsigned char **ptr, int len, int map) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci unsigned char *p = *ptr; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci while (len) { 11262306a36Sopenharmony_ci while (not_useful(p, map)) 11362306a36Sopenharmony_ci p++; 11462306a36Sopenharmony_ci p++; 11562306a36Sopenharmony_ci len--; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci *ptr = p; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic void nubus_move(unsigned char **ptr, int len, int map) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci unsigned long slot_space = (unsigned long)*ptr & 0xFF000000; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (len > 0) 12562306a36Sopenharmony_ci nubus_advance(ptr, len, map); 12662306a36Sopenharmony_ci else if (len < 0) 12762306a36Sopenharmony_ci nubus_rewind(ptr, -len, map); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (((unsigned long)*ptr & 0xFF000000) != slot_space) 13062306a36Sopenharmony_ci pr_err("%s: moved out of slot address space!\n", __func__); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* Now, functions to read the sResource tree */ 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* Each sResource entry consists of a 1-byte ID and a 3-byte data 13662306a36Sopenharmony_ci field. If that data field contains an offset, then obviously we 13762306a36Sopenharmony_ci have to expand it from a 24-bit signed number to a 32-bit signed 13862306a36Sopenharmony_ci number. */ 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic inline long nubus_expand32(long foo) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci if (foo & 0x00800000) /* 24bit negative */ 14362306a36Sopenharmony_ci foo |= 0xFF000000; 14462306a36Sopenharmony_ci return foo; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic inline void *nubus_rom_addr(int slot) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * Returns the first byte after the card. We then walk 15162306a36Sopenharmony_ci * backwards to get the lane register and the config 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci return (void *)(0xF1000000 + (slot << 24)); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ciunsigned char *nubus_dirptr(const struct nubus_dirent *nd) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci unsigned char *p = nd->base; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Essentially, just step over the bytelanes using whatever 16162306a36Sopenharmony_ci offset we might have found */ 16262306a36Sopenharmony_ci nubus_move(&p, nubus_expand32(nd->data), nd->mask); 16362306a36Sopenharmony_ci /* And return the value */ 16462306a36Sopenharmony_ci return p; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* These two are for pulling resource data blocks (i.e. stuff that's 16862306a36Sopenharmony_ci pointed to with offsets) out of the card ROM. */ 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_civoid nubus_get_rsrc_mem(void *dest, const struct nubus_dirent *dirent, 17162306a36Sopenharmony_ci unsigned int len) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci unsigned char *t = dest; 17462306a36Sopenharmony_ci unsigned char *p = nubus_dirptr(dirent); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci while (len) { 17762306a36Sopenharmony_ci *t++ = nubus_get_rom(&p, 1, dirent->mask); 17862306a36Sopenharmony_ci len--; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_get_rsrc_mem); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ciunsigned int nubus_get_rsrc_str(char *dest, const struct nubus_dirent *dirent, 18462306a36Sopenharmony_ci unsigned int len) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci char *t = dest; 18762306a36Sopenharmony_ci unsigned char *p = nubus_dirptr(dirent); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci while (len > 1) { 19062306a36Sopenharmony_ci unsigned char c = nubus_get_rom(&p, 1, dirent->mask); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!c) 19362306a36Sopenharmony_ci break; 19462306a36Sopenharmony_ci *t++ = c; 19562306a36Sopenharmony_ci len--; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci if (len > 0) 19862306a36Sopenharmony_ci *t = '\0'; 19962306a36Sopenharmony_ci return t - dest; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_get_rsrc_str); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_civoid nubus_seq_write_rsrc_mem(struct seq_file *m, 20462306a36Sopenharmony_ci const struct nubus_dirent *dirent, 20562306a36Sopenharmony_ci unsigned int len) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci unsigned long buf[32]; 20862306a36Sopenharmony_ci unsigned int buf_size = sizeof(buf); 20962306a36Sopenharmony_ci unsigned char *p = nubus_dirptr(dirent); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* If possible, write out full buffers */ 21262306a36Sopenharmony_ci while (len >= buf_size) { 21362306a36Sopenharmony_ci unsigned int i; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(buf); i++) 21662306a36Sopenharmony_ci buf[i] = nubus_get_rom(&p, sizeof(buf[0]), 21762306a36Sopenharmony_ci dirent->mask); 21862306a36Sopenharmony_ci seq_write(m, buf, buf_size); 21962306a36Sopenharmony_ci len -= buf_size; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci /* If not, write out individual bytes */ 22262306a36Sopenharmony_ci while (len--) 22362306a36Sopenharmony_ci seq_putc(m, nubus_get_rom(&p, 1, dirent->mask)); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ciint nubus_get_root_dir(const struct nubus_board *board, 22762306a36Sopenharmony_ci struct nubus_dir *dir) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci dir->ptr = dir->base = board->directory; 23062306a36Sopenharmony_ci dir->done = 0; 23162306a36Sopenharmony_ci dir->mask = board->lanes; 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_get_root_dir); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* This is a slyly renamed version of the above */ 23762306a36Sopenharmony_ciint nubus_get_func_dir(const struct nubus_rsrc *fres, struct nubus_dir *dir) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci dir->ptr = dir->base = fres->directory; 24062306a36Sopenharmony_ci dir->done = 0; 24162306a36Sopenharmony_ci dir->mask = fres->board->lanes; 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_get_func_dir); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciint nubus_get_board_dir(const struct nubus_board *board, 24762306a36Sopenharmony_ci struct nubus_dir *dir) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct nubus_dirent ent; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci dir->ptr = dir->base = board->directory; 25262306a36Sopenharmony_ci dir->done = 0; 25362306a36Sopenharmony_ci dir->mask = board->lanes; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* Now dereference it (the first directory is always the board 25662306a36Sopenharmony_ci directory) */ 25762306a36Sopenharmony_ci if (nubus_readdir(dir, &ent) == -1) 25862306a36Sopenharmony_ci return -1; 25962306a36Sopenharmony_ci if (nubus_get_subdir(&ent, dir) == -1) 26062306a36Sopenharmony_ci return -1; 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_get_board_dir); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciint nubus_get_subdir(const struct nubus_dirent *ent, 26662306a36Sopenharmony_ci struct nubus_dir *dir) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci dir->ptr = dir->base = nubus_dirptr(ent); 26962306a36Sopenharmony_ci dir->done = 0; 27062306a36Sopenharmony_ci dir->mask = ent->mask; 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_get_subdir); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ciint nubus_readdir(struct nubus_dir *nd, struct nubus_dirent *ent) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci u32 resid; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (nd->done) 28062306a36Sopenharmony_ci return -1; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Do this first, otherwise nubus_rewind & co are off by 4 */ 28362306a36Sopenharmony_ci ent->base = nd->ptr; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* This moves nd->ptr forward */ 28662306a36Sopenharmony_ci resid = nubus_get_rom(&nd->ptr, 4, nd->mask); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* EOL marker, as per the Apple docs */ 28962306a36Sopenharmony_ci if ((resid & 0xff000000) == 0xff000000) { 29062306a36Sopenharmony_ci /* Mark it as done */ 29162306a36Sopenharmony_ci nd->done = 1; 29262306a36Sopenharmony_ci return -1; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* First byte is the resource ID */ 29662306a36Sopenharmony_ci ent->type = resid >> 24; 29762306a36Sopenharmony_ci /* Low 3 bytes might contain data (or might not) */ 29862306a36Sopenharmony_ci ent->data = resid & 0xffffff; 29962306a36Sopenharmony_ci ent->mask = nd->mask; 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_readdir); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ciint nubus_rewinddir(struct nubus_dir *dir) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci dir->ptr = dir->base; 30762306a36Sopenharmony_ci dir->done = 0; 30862306a36Sopenharmony_ci return 0; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_rewinddir); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/* Driver interface functions, more or less like in pci.c */ 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistruct nubus_rsrc *nubus_first_rsrc_or_null(void) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci return list_first_entry_or_null(&nubus_func_rsrcs, struct nubus_rsrc, 31762306a36Sopenharmony_ci list); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_first_rsrc_or_null); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistruct nubus_rsrc *nubus_next_rsrc_or_null(struct nubus_rsrc *from) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci if (list_is_last(&from->list, &nubus_func_rsrcs)) 32462306a36Sopenharmony_ci return NULL; 32562306a36Sopenharmony_ci return list_next_entry(from, list); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_next_rsrc_or_null); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ciint 33062306a36Sopenharmony_cinubus_find_rsrc(struct nubus_dir *dir, unsigned char rsrc_type, 33162306a36Sopenharmony_ci struct nubus_dirent *ent) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci while (nubus_readdir(dir, ent) != -1) { 33462306a36Sopenharmony_ci if (ent->type == rsrc_type) 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci return -1; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ciEXPORT_SYMBOL(nubus_find_rsrc); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci/* Initialization functions - decide which slots contain stuff worth 34262306a36Sopenharmony_ci looking at, and print out lots and lots of information from the 34362306a36Sopenharmony_ci resource blocks. */ 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic int __init nubus_get_block_rsrc_dir(struct nubus_board *board, 34662306a36Sopenharmony_ci struct proc_dir_entry *procdir, 34762306a36Sopenharmony_ci const struct nubus_dirent *parent) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct nubus_dir dir; 35062306a36Sopenharmony_ci struct nubus_dirent ent; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci nubus_get_subdir(parent, &dir); 35362306a36Sopenharmony_ci dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci while (nubus_readdir(&dir, &ent) != -1) { 35662306a36Sopenharmony_ci u32 size; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci nubus_get_rsrc_mem(&size, &ent, 4); 35962306a36Sopenharmony_ci pr_debug(" block (0x%x), size %d\n", ent.type, size); 36062306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, size); 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int __init nubus_get_display_vidmode(struct nubus_board *board, 36662306a36Sopenharmony_ci struct proc_dir_entry *procdir, 36762306a36Sopenharmony_ci const struct nubus_dirent *parent) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct nubus_dir dir; 37062306a36Sopenharmony_ci struct nubus_dirent ent; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci nubus_get_subdir(parent, &dir); 37362306a36Sopenharmony_ci dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci while (nubus_readdir(&dir, &ent) != -1) { 37662306a36Sopenharmony_ci switch (ent.type) { 37762306a36Sopenharmony_ci case 1: /* mVidParams */ 37862306a36Sopenharmony_ci case 2: /* mTable */ 37962306a36Sopenharmony_ci { 38062306a36Sopenharmony_ci u32 size; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci nubus_get_rsrc_mem(&size, &ent, 4); 38362306a36Sopenharmony_ci pr_debug(" block (0x%x), size %d\n", ent.type, 38462306a36Sopenharmony_ci size); 38562306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, size); 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci default: 38962306a36Sopenharmony_ci pr_debug(" unknown resource 0x%02x, data 0x%06x\n", 39062306a36Sopenharmony_ci ent.type, ent.data); 39162306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, 0); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int __init nubus_get_display_resource(struct nubus_rsrc *fres, 39862306a36Sopenharmony_ci struct proc_dir_entry *procdir, 39962306a36Sopenharmony_ci const struct nubus_dirent *ent) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci switch (ent->type) { 40262306a36Sopenharmony_ci case NUBUS_RESID_GAMMADIR: 40362306a36Sopenharmony_ci pr_debug(" gamma directory offset: 0x%06x\n", ent->data); 40462306a36Sopenharmony_ci nubus_get_block_rsrc_dir(fres->board, procdir, ent); 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci case 0x0080 ... 0x0085: 40762306a36Sopenharmony_ci pr_debug(" mode 0x%02x info offset: 0x%06x\n", 40862306a36Sopenharmony_ci ent->type, ent->data); 40962306a36Sopenharmony_ci nubus_get_display_vidmode(fres->board, procdir, ent); 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci default: 41262306a36Sopenharmony_ci pr_debug(" unknown resource 0x%02x, data 0x%06x\n", 41362306a36Sopenharmony_ci ent->type, ent->data); 41462306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(procdir, ent, 0); 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci return 0; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic int __init nubus_get_network_resource(struct nubus_rsrc *fres, 42062306a36Sopenharmony_ci struct proc_dir_entry *procdir, 42162306a36Sopenharmony_ci const struct nubus_dirent *ent) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci switch (ent->type) { 42462306a36Sopenharmony_ci case NUBUS_RESID_MAC_ADDRESS: 42562306a36Sopenharmony_ci { 42662306a36Sopenharmony_ci char addr[6]; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci nubus_get_rsrc_mem(addr, ent, 6); 42962306a36Sopenharmony_ci pr_debug(" MAC address: %pM\n", addr); 43062306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(procdir, ent, 6); 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci default: 43462306a36Sopenharmony_ci pr_debug(" unknown resource 0x%02x, data 0x%06x\n", 43562306a36Sopenharmony_ci ent->type, ent->data); 43662306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(procdir, ent, 0); 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci return 0; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic int __init nubus_get_cpu_resource(struct nubus_rsrc *fres, 44262306a36Sopenharmony_ci struct proc_dir_entry *procdir, 44362306a36Sopenharmony_ci const struct nubus_dirent *ent) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci switch (ent->type) { 44662306a36Sopenharmony_ci case NUBUS_RESID_MEMINFO: 44762306a36Sopenharmony_ci { 44862306a36Sopenharmony_ci unsigned long meminfo[2]; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci nubus_get_rsrc_mem(&meminfo, ent, 8); 45162306a36Sopenharmony_ci pr_debug(" memory: [ 0x%08lx 0x%08lx ]\n", 45262306a36Sopenharmony_ci meminfo[0], meminfo[1]); 45362306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(procdir, ent, 8); 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci case NUBUS_RESID_ROMINFO: 45762306a36Sopenharmony_ci { 45862306a36Sopenharmony_ci unsigned long rominfo[2]; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci nubus_get_rsrc_mem(&rominfo, ent, 8); 46162306a36Sopenharmony_ci pr_debug(" ROM: [ 0x%08lx 0x%08lx ]\n", 46262306a36Sopenharmony_ci rominfo[0], rominfo[1]); 46362306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(procdir, ent, 8); 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci default: 46762306a36Sopenharmony_ci pr_debug(" unknown resource 0x%02x, data 0x%06x\n", 46862306a36Sopenharmony_ci ent->type, ent->data); 46962306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(procdir, ent, 0); 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int __init nubus_get_private_resource(struct nubus_rsrc *fres, 47562306a36Sopenharmony_ci struct proc_dir_entry *procdir, 47662306a36Sopenharmony_ci const struct nubus_dirent *ent) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci switch (fres->category) { 47962306a36Sopenharmony_ci case NUBUS_CAT_DISPLAY: 48062306a36Sopenharmony_ci nubus_get_display_resource(fres, procdir, ent); 48162306a36Sopenharmony_ci break; 48262306a36Sopenharmony_ci case NUBUS_CAT_NETWORK: 48362306a36Sopenharmony_ci nubus_get_network_resource(fres, procdir, ent); 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci case NUBUS_CAT_CPU: 48662306a36Sopenharmony_ci nubus_get_cpu_resource(fres, procdir, ent); 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci default: 48962306a36Sopenharmony_ci pr_debug(" unknown resource 0x%02x, data 0x%06x\n", 49062306a36Sopenharmony_ci ent->type, ent->data); 49162306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(procdir, ent, 0); 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic struct nubus_rsrc * __init 49762306a36Sopenharmony_cinubus_get_functional_resource(struct nubus_board *board, int slot, 49862306a36Sopenharmony_ci const struct nubus_dirent *parent) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct nubus_dir dir; 50162306a36Sopenharmony_ci struct nubus_dirent ent; 50262306a36Sopenharmony_ci struct nubus_rsrc *fres; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci pr_debug(" Functional resource 0x%02x:\n", parent->type); 50562306a36Sopenharmony_ci nubus_get_subdir(parent, &dir); 50662306a36Sopenharmony_ci dir.procdir = nubus_proc_add_rsrc_dir(board->procdir, parent, board); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Actually we should probably panic if this fails */ 50962306a36Sopenharmony_ci fres = kzalloc(sizeof(*fres), GFP_ATOMIC); 51062306a36Sopenharmony_ci if (!fres) 51162306a36Sopenharmony_ci return NULL; 51262306a36Sopenharmony_ci fres->resid = parent->type; 51362306a36Sopenharmony_ci fres->directory = dir.base; 51462306a36Sopenharmony_ci fres->board = board; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci while (nubus_readdir(&dir, &ent) != -1) { 51762306a36Sopenharmony_ci switch (ent.type) { 51862306a36Sopenharmony_ci case NUBUS_RESID_TYPE: 51962306a36Sopenharmony_ci { 52062306a36Sopenharmony_ci unsigned short nbtdata[4]; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci nubus_get_rsrc_mem(nbtdata, &ent, 8); 52362306a36Sopenharmony_ci fres->category = nbtdata[0]; 52462306a36Sopenharmony_ci fres->type = nbtdata[1]; 52562306a36Sopenharmony_ci fres->dr_sw = nbtdata[2]; 52662306a36Sopenharmony_ci fres->dr_hw = nbtdata[3]; 52762306a36Sopenharmony_ci pr_debug(" type: [cat 0x%x type 0x%x sw 0x%x hw 0x%x]\n", 52862306a36Sopenharmony_ci nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]); 52962306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, 8); 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci case NUBUS_RESID_NAME: 53362306a36Sopenharmony_ci { 53462306a36Sopenharmony_ci char name[64]; 53562306a36Sopenharmony_ci unsigned int len; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci len = nubus_get_rsrc_str(name, &ent, sizeof(name)); 53862306a36Sopenharmony_ci pr_debug(" name: %s\n", name); 53962306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1); 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci case NUBUS_RESID_DRVRDIR: 54362306a36Sopenharmony_ci { 54462306a36Sopenharmony_ci /* MacOS driver. If we were NetBSD we might 54562306a36Sopenharmony_ci use this :-) */ 54662306a36Sopenharmony_ci pr_debug(" driver directory offset: 0x%06x\n", 54762306a36Sopenharmony_ci ent.data); 54862306a36Sopenharmony_ci nubus_get_block_rsrc_dir(board, dir.procdir, &ent); 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci case NUBUS_RESID_MINOR_BASEOS: 55262306a36Sopenharmony_ci { 55362306a36Sopenharmony_ci /* We will need this in order to support 55462306a36Sopenharmony_ci multiple framebuffers. It might be handy 55562306a36Sopenharmony_ci for Ethernet as well */ 55662306a36Sopenharmony_ci u32 base_offset; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci nubus_get_rsrc_mem(&base_offset, &ent, 4); 55962306a36Sopenharmony_ci pr_debug(" memory offset: 0x%08x\n", base_offset); 56062306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, 4); 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci case NUBUS_RESID_MINOR_LENGTH: 56462306a36Sopenharmony_ci { 56562306a36Sopenharmony_ci /* Ditto */ 56662306a36Sopenharmony_ci u32 length; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci nubus_get_rsrc_mem(&length, &ent, 4); 56962306a36Sopenharmony_ci pr_debug(" memory length: 0x%08x\n", length); 57062306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, 4); 57162306a36Sopenharmony_ci break; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci case NUBUS_RESID_FLAGS: 57462306a36Sopenharmony_ci pr_debug(" flags: 0x%06x\n", ent.data); 57562306a36Sopenharmony_ci nubus_proc_add_rsrc(dir.procdir, &ent); 57662306a36Sopenharmony_ci break; 57762306a36Sopenharmony_ci case NUBUS_RESID_HWDEVID: 57862306a36Sopenharmony_ci pr_debug(" hwdevid: 0x%06x\n", ent.data); 57962306a36Sopenharmony_ci nubus_proc_add_rsrc(dir.procdir, &ent); 58062306a36Sopenharmony_ci break; 58162306a36Sopenharmony_ci default: 58262306a36Sopenharmony_ci if (nubus_populate_procfs) 58362306a36Sopenharmony_ci nubus_get_private_resource(fres, dir.procdir, 58462306a36Sopenharmony_ci &ent); 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return fres; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/* This is *really* cool. */ 59262306a36Sopenharmony_cistatic int __init nubus_get_icon(struct nubus_board *board, 59362306a36Sopenharmony_ci struct proc_dir_entry *procdir, 59462306a36Sopenharmony_ci const struct nubus_dirent *ent) 59562306a36Sopenharmony_ci{ 59662306a36Sopenharmony_ci /* Should be 32x32 if my memory serves me correctly */ 59762306a36Sopenharmony_ci u32 icon[32]; 59862306a36Sopenharmony_ci int i; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci nubus_get_rsrc_mem(&icon, ent, 128); 60162306a36Sopenharmony_ci pr_debug(" icon:\n"); 60262306a36Sopenharmony_ci for (i = 0; i < 8; i++) 60362306a36Sopenharmony_ci pr_debug(" %08x %08x %08x %08x\n", 60462306a36Sopenharmony_ci icon[i * 4 + 0], icon[i * 4 + 1], 60562306a36Sopenharmony_ci icon[i * 4 + 2], icon[i * 4 + 3]); 60662306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(procdir, ent, 128); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic int __init nubus_get_vendorinfo(struct nubus_board *board, 61262306a36Sopenharmony_ci struct proc_dir_entry *procdir, 61362306a36Sopenharmony_ci const struct nubus_dirent *parent) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct nubus_dir dir; 61662306a36Sopenharmony_ci struct nubus_dirent ent; 61762306a36Sopenharmony_ci static char *vendor_fields[6] = { "ID", "serial", "revision", 61862306a36Sopenharmony_ci "part", "date", "unknown field" }; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci pr_debug(" vendor info:\n"); 62162306a36Sopenharmony_ci nubus_get_subdir(parent, &dir); 62262306a36Sopenharmony_ci dir.procdir = nubus_proc_add_rsrc_dir(procdir, parent, board); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci while (nubus_readdir(&dir, &ent) != -1) { 62562306a36Sopenharmony_ci char name[64]; 62662306a36Sopenharmony_ci unsigned int len; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* These are all strings, we think */ 62962306a36Sopenharmony_ci len = nubus_get_rsrc_str(name, &ent, sizeof(name)); 63062306a36Sopenharmony_ci if (ent.type < 1 || ent.type > 5) 63162306a36Sopenharmony_ci ent.type = 5; 63262306a36Sopenharmony_ci pr_debug(" %s: %s\n", vendor_fields[ent.type - 1], name); 63362306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1); 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic int __init nubus_get_board_resource(struct nubus_board *board, int slot, 63962306a36Sopenharmony_ci const struct nubus_dirent *parent) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct nubus_dir dir; 64262306a36Sopenharmony_ci struct nubus_dirent ent; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci pr_debug(" Board resource 0x%02x:\n", parent->type); 64562306a36Sopenharmony_ci nubus_get_subdir(parent, &dir); 64662306a36Sopenharmony_ci dir.procdir = nubus_proc_add_rsrc_dir(board->procdir, parent, board); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci while (nubus_readdir(&dir, &ent) != -1) { 64962306a36Sopenharmony_ci switch (ent.type) { 65062306a36Sopenharmony_ci case NUBUS_RESID_TYPE: 65162306a36Sopenharmony_ci { 65262306a36Sopenharmony_ci unsigned short nbtdata[4]; 65362306a36Sopenharmony_ci /* This type is always the same, and is not 65462306a36Sopenharmony_ci useful except insofar as it tells us that 65562306a36Sopenharmony_ci we really are looking at a board resource. */ 65662306a36Sopenharmony_ci nubus_get_rsrc_mem(nbtdata, &ent, 8); 65762306a36Sopenharmony_ci pr_debug(" type: [cat 0x%x type 0x%x sw 0x%x hw 0x%x]\n", 65862306a36Sopenharmony_ci nbtdata[0], nbtdata[1], nbtdata[2], nbtdata[3]); 65962306a36Sopenharmony_ci if (nbtdata[0] != 1 || nbtdata[1] != 0 || 66062306a36Sopenharmony_ci nbtdata[2] != 0 || nbtdata[3] != 0) 66162306a36Sopenharmony_ci pr_err("Slot %X: sResource is not a board resource!\n", 66262306a36Sopenharmony_ci slot); 66362306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, 8); 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci case NUBUS_RESID_NAME: 66762306a36Sopenharmony_ci { 66862306a36Sopenharmony_ci unsigned int len; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci len = nubus_get_rsrc_str(board->name, &ent, 67162306a36Sopenharmony_ci sizeof(board->name)); 67262306a36Sopenharmony_ci pr_debug(" name: %s\n", board->name); 67362306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, len + 1); 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci case NUBUS_RESID_ICON: 67762306a36Sopenharmony_ci nubus_get_icon(board, dir.procdir, &ent); 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci case NUBUS_RESID_BOARDID: 68062306a36Sopenharmony_ci pr_debug(" board id: 0x%x\n", ent.data); 68162306a36Sopenharmony_ci nubus_proc_add_rsrc(dir.procdir, &ent); 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci case NUBUS_RESID_PRIMARYINIT: 68462306a36Sopenharmony_ci pr_debug(" primary init offset: 0x%06x\n", ent.data); 68562306a36Sopenharmony_ci nubus_proc_add_rsrc(dir.procdir, &ent); 68662306a36Sopenharmony_ci break; 68762306a36Sopenharmony_ci case NUBUS_RESID_VENDORINFO: 68862306a36Sopenharmony_ci nubus_get_vendorinfo(board, dir.procdir, &ent); 68962306a36Sopenharmony_ci break; 69062306a36Sopenharmony_ci case NUBUS_RESID_FLAGS: 69162306a36Sopenharmony_ci pr_debug(" flags: 0x%06x\n", ent.data); 69262306a36Sopenharmony_ci nubus_proc_add_rsrc(dir.procdir, &ent); 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci case NUBUS_RESID_HWDEVID: 69562306a36Sopenharmony_ci pr_debug(" hwdevid: 0x%06x\n", ent.data); 69662306a36Sopenharmony_ci nubus_proc_add_rsrc(dir.procdir, &ent); 69762306a36Sopenharmony_ci break; 69862306a36Sopenharmony_ci case NUBUS_RESID_SECONDINIT: 69962306a36Sopenharmony_ci pr_debug(" secondary init offset: 0x%06x\n", 70062306a36Sopenharmony_ci ent.data); 70162306a36Sopenharmony_ci nubus_proc_add_rsrc(dir.procdir, &ent); 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci /* WTF isn't this in the functional resources? */ 70462306a36Sopenharmony_ci case NUBUS_RESID_VIDNAMES: 70562306a36Sopenharmony_ci pr_debug(" vidnames directory offset: 0x%06x\n", 70662306a36Sopenharmony_ci ent.data); 70762306a36Sopenharmony_ci nubus_get_block_rsrc_dir(board, dir.procdir, &ent); 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci /* Same goes for this */ 71062306a36Sopenharmony_ci case NUBUS_RESID_VIDMODES: 71162306a36Sopenharmony_ci pr_debug(" video mode parameter directory offset: 0x%06x\n", 71262306a36Sopenharmony_ci ent.data); 71362306a36Sopenharmony_ci nubus_proc_add_rsrc(dir.procdir, &ent); 71462306a36Sopenharmony_ci break; 71562306a36Sopenharmony_ci default: 71662306a36Sopenharmony_ci pr_debug(" unknown resource 0x%02x, data 0x%06x\n", 71762306a36Sopenharmony_ci ent.type, ent.data); 71862306a36Sopenharmony_ci nubus_proc_add_rsrc_mem(dir.procdir, &ent, 0); 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci return 0; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic void __init nubus_add_board(int slot, int bytelanes) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct nubus_board *board; 72762306a36Sopenharmony_ci unsigned char *rp; 72862306a36Sopenharmony_ci unsigned long dpat; 72962306a36Sopenharmony_ci struct nubus_dir dir; 73062306a36Sopenharmony_ci struct nubus_dirent ent; 73162306a36Sopenharmony_ci int prev_resid = -1; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci /* Move to the start of the format block */ 73462306a36Sopenharmony_ci rp = nubus_rom_addr(slot); 73562306a36Sopenharmony_ci nubus_rewind(&rp, FORMAT_BLOCK_SIZE, bytelanes); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* Actually we should probably panic if this fails */ 73862306a36Sopenharmony_ci if ((board = kzalloc(sizeof(*board), GFP_ATOMIC)) == NULL) 73962306a36Sopenharmony_ci return; 74062306a36Sopenharmony_ci board->fblock = rp; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci /* Dump the format block for debugging purposes */ 74362306a36Sopenharmony_ci pr_debug("Slot %X, format block at 0x%p:\n", slot, rp); 74462306a36Sopenharmony_ci pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes)); 74562306a36Sopenharmony_ci pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes)); 74662306a36Sopenharmony_ci pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes)); 74762306a36Sopenharmony_ci pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes)); 74862306a36Sopenharmony_ci pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes)); 74962306a36Sopenharmony_ci pr_debug("%08lx\n", nubus_get_rom(&rp, 4, bytelanes)); 75062306a36Sopenharmony_ci pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes)); 75162306a36Sopenharmony_ci pr_debug("%02lx\n", nubus_get_rom(&rp, 1, bytelanes)); 75262306a36Sopenharmony_ci rp = board->fblock; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci board->slot = slot; 75562306a36Sopenharmony_ci board->slot_addr = (unsigned long)nubus_slot_addr(slot); 75662306a36Sopenharmony_ci board->doffset = nubus_get_rom(&rp, 4, bytelanes); 75762306a36Sopenharmony_ci /* rom_length is *supposed* to be the total length of the 75862306a36Sopenharmony_ci * ROM. In practice it is the "amount of ROM used to compute 75962306a36Sopenharmony_ci * the CRC." So some jokers decide to set it to zero and 76062306a36Sopenharmony_ci * set the crc to zero so they don't have to do any math. 76162306a36Sopenharmony_ci * See the Performa 460 ROM, for example. Those Apple "engineers". 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_ci board->rom_length = nubus_get_rom(&rp, 4, bytelanes); 76462306a36Sopenharmony_ci board->crc = nubus_get_rom(&rp, 4, bytelanes); 76562306a36Sopenharmony_ci board->rev = nubus_get_rom(&rp, 1, bytelanes); 76662306a36Sopenharmony_ci board->format = nubus_get_rom(&rp, 1, bytelanes); 76762306a36Sopenharmony_ci board->lanes = bytelanes; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Directory offset should be small and negative... */ 77062306a36Sopenharmony_ci if (!(board->doffset & 0x00FF0000)) 77162306a36Sopenharmony_ci pr_warn("Slot %X: Dodgy doffset!\n", slot); 77262306a36Sopenharmony_ci dpat = nubus_get_rom(&rp, 4, bytelanes); 77362306a36Sopenharmony_ci if (dpat != NUBUS_TEST_PATTERN) 77462306a36Sopenharmony_ci pr_warn("Slot %X: Wrong test pattern %08lx!\n", slot, dpat); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci /* 77762306a36Sopenharmony_ci * I wonder how the CRC is meant to work - 77862306a36Sopenharmony_ci * any takers ? 77962306a36Sopenharmony_ci * CSA: According to MAC docs, not all cards pass the CRC anyway, 78062306a36Sopenharmony_ci * since the initial Macintosh ROM releases skipped the check. 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Set up the directory pointer */ 78462306a36Sopenharmony_ci board->directory = board->fblock; 78562306a36Sopenharmony_ci nubus_move(&board->directory, nubus_expand32(board->doffset), 78662306a36Sopenharmony_ci board->lanes); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci nubus_get_root_dir(board, &dir); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* We're ready to rock */ 79162306a36Sopenharmony_ci pr_debug("Slot %X resources:\n", slot); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* Each slot should have one board resource and any number of 79462306a36Sopenharmony_ci * functional resources. So we'll fill in some fields in the 79562306a36Sopenharmony_ci * struct nubus_board from the board resource, then walk down 79662306a36Sopenharmony_ci * the list of functional resources, spinning out a nubus_rsrc 79762306a36Sopenharmony_ci * for each of them. 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_ci if (nubus_readdir(&dir, &ent) == -1) { 80062306a36Sopenharmony_ci /* We can't have this! */ 80162306a36Sopenharmony_ci pr_err("Slot %X: Board resource not found!\n", slot); 80262306a36Sopenharmony_ci kfree(board); 80362306a36Sopenharmony_ci return; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (ent.type < 1 || ent.type > 127) 80762306a36Sopenharmony_ci pr_warn("Slot %X: Board resource ID is invalid!\n", slot); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci board->procdir = nubus_proc_add_board(board); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci nubus_get_board_resource(board, slot, &ent); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci while (nubus_readdir(&dir, &ent) != -1) { 81462306a36Sopenharmony_ci struct nubus_rsrc *fres; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci fres = nubus_get_functional_resource(board, slot, &ent); 81762306a36Sopenharmony_ci if (fres == NULL) 81862306a36Sopenharmony_ci continue; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* Resources should appear in ascending ID order. This sanity 82162306a36Sopenharmony_ci * check prevents duplicate resource IDs. 82262306a36Sopenharmony_ci */ 82362306a36Sopenharmony_ci if (fres->resid <= prev_resid) { 82462306a36Sopenharmony_ci kfree(fres); 82562306a36Sopenharmony_ci continue; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci prev_resid = fres->resid; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci list_add_tail(&fres->list, &nubus_func_rsrcs); 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (nubus_device_register(board)) 83362306a36Sopenharmony_ci put_device(&board->dev); 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_cistatic void __init nubus_probe_slot(int slot) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci unsigned char dp; 83962306a36Sopenharmony_ci unsigned char *rp; 84062306a36Sopenharmony_ci int i; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci rp = nubus_rom_addr(slot); 84362306a36Sopenharmony_ci for (i = 4; i; i--) { 84462306a36Sopenharmony_ci rp--; 84562306a36Sopenharmony_ci if (!hwreg_present(rp)) 84662306a36Sopenharmony_ci continue; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci dp = *rp; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* The last byte of the format block consists of two 85162306a36Sopenharmony_ci nybbles which are "mirror images" of each other. 85262306a36Sopenharmony_ci These show us the valid bytelanes */ 85362306a36Sopenharmony_ci if ((((dp >> 4) ^ dp) & 0x0F) != 0x0F) 85462306a36Sopenharmony_ci continue; 85562306a36Sopenharmony_ci /* Check that this value is actually *on* one of the 85662306a36Sopenharmony_ci bytelanes it claims are valid! */ 85762306a36Sopenharmony_ci if (not_useful(rp, dp)) 85862306a36Sopenharmony_ci continue; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci /* Looks promising. Let's put it on the list. */ 86162306a36Sopenharmony_ci nubus_add_board(slot, dp); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic void __init nubus_scan_bus(void) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci int slot; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci pr_info("NuBus: Scanning NuBus slots.\n"); 87262306a36Sopenharmony_ci for (slot = 9; slot < 15; slot++) { 87362306a36Sopenharmony_ci nubus_probe_slot(slot); 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic int __init nubus_init(void) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci int err; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (!MACH_IS_MAC) 88262306a36Sopenharmony_ci return 0; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci nubus_proc_init(); 88562306a36Sopenharmony_ci err = nubus_parent_device_register(); 88662306a36Sopenharmony_ci if (err) 88762306a36Sopenharmony_ci return err; 88862306a36Sopenharmony_ci nubus_scan_bus(); 88962306a36Sopenharmony_ci return 0; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cisubsys_initcall(nubus_init); 893