summaryrefslogtreecommitdiffstats
path: root/drivers/nubus
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-04-05 11:23:36 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-04-05 11:23:36 +0000
commit4318fbda2a7ee51caafdc4eb1f8028a3f0605142 (patch)
treecddb50a81d7d1a628cc400519162080c6d87868e /drivers/nubus
parent36ea5120664550fae6d31f1c6f695e4f8975cb06 (diff)
o Merge with Linux 2.1.91.
o First round of bugfixes for the SC/MC CPUs. o FPU context switch fixes. o Lazy context switches. o Faster syscalls. o Removed dead code. o Shitloads of other things I forgot ...
Diffstat (limited to 'drivers/nubus')
-rw-r--r--drivers/nubus/Makefile15
-rw-r--r--drivers/nubus/nubus.c629
2 files changed, 644 insertions, 0 deletions
diff --git a/drivers/nubus/Makefile b/drivers/nubus/Makefile
new file mode 100644
index 000000000..781edd052
--- /dev/null
+++ b/drivers/nubus/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for the nubus specific drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now inherited from the
+# parent makefile.
+#
+
+L_OBJS := nubus.o
+L_TARGET := nubus.a
+
+include $(TOPDIR)/Rules.make
diff --git a/drivers/nubus/nubus.c b/drivers/nubus/nubus.c
new file mode 100644
index 000000000..d266c64b7
--- /dev/null
+++ b/drivers/nubus/nubus.c
@@ -0,0 +1,629 @@
+/*
+ * Macintosh Nubus Interface Code
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/nubus.h>
+#include <linux/errno.h>
+#include <asm/setup.h>
+#include <asm/system.h>
+#include <asm/page.h>
+/* for LCIII stuff; better find a general way like MACH_HAS_NUBUS */
+#include <asm/macintosh.h>
+
+#undef LCIII_WEIRDNESS
+
+static struct nubus_slot nubus_slots[16];
+
+/*
+ * Please skip to the bottom of this file if you ate lunch recently
+ * -- Alan
+ */
+
+
+
+/* This function tests for the presence of an address, specially a
+ * hardware register address. It is called very early in the kernel
+ * initialization process, when the VBR register isn't set up yet. On
+ * an Atari, it still points to address 0, which is unmapped. So a bus
+ * error would cause another bus error while fetching the exception
+ * vector, and the CPU would do nothing at all. So we needed to set up
+ * a temporary VBR and a vector table for the duration of the test.
+ *
+ * See the atari/config.c code we nicked it from for more clues.
+ */
+
+int nubus_hwreg_present( volatile void *regp )
+{
+ int ret = 0;
+ long save_sp, save_vbr;
+ long tmp_vectors[3];
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ __asm__ __volatile__
+ ( "movec %/vbr,%2\n\t"
+ "movel #Lberr1,%4@(8)\n\t"
+ "movec %4,%/vbr\n\t"
+ "movel %/sp,%1\n\t"
+ "moveq #0,%0\n\t"
+ "tstb %3@\n\t"
+ "nop\n\t"
+ "moveq #1,%0\n"
+ "Lberr1:\n\t"
+ "movel %1,%/sp\n\t"
+ "movec %2,%/vbr"
+ : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
+ : "a" (regp), "a" (tmp_vectors)
+ );
+ restore_flags(flags);
+ return( ret );
+}
+
+
+
+/*
+ * Yes this sucks. The ROM can appear on arbitary bytes of the long
+ * word. We are not amused.
+ */
+
+extern __inline__ int not_useful(void *p, int map)
+{
+ unsigned long pv=(unsigned long)p;
+ pv&=3;
+ if(map&(1<<pv))
+ return 0;
+ return 1;
+}
+
+static unsigned long nubus_get_rom(unsigned char **ptr, int len, int map)
+{
+ unsigned long v=0;
+ unsigned char *p=*ptr; /* as v|=*((*ptr)++) upset someone */
+ while(len)
+ {
+ v<<=8;
+ while(not_useful(p,map))
+ p++;
+ v|=*p++;
+ len--;
+ }
+ *ptr=p;
+ return v;
+}
+
+static void nubus_rewind(unsigned char **ptr, int len, int map)
+{
+ unsigned char *p=*ptr;
+
+ if(len>8192)
+ printk("rewind of %d!\n", len);
+ while(len)
+ {
+ do
+ {
+ p--;
+ }
+ while(not_useful(p,map));
+ len--;
+ }
+ *ptr=p;
+}
+
+static void nubus_advance(unsigned char **ptr, int len, int map)
+{
+ unsigned char *p=*ptr;
+ if(len>8192)
+ printk("advance of %d!\n", len);
+ while(len)
+ {
+ while(not_useful(p,map))
+ p++;
+ p++;
+ len--;
+ }
+ *ptr=p;
+}
+
+/*
+ * 24bit signed offset to 32bit
+ */
+
+static unsigned long nubus_expand32(unsigned long foo)
+{
+ if(foo&0x00800000) /* 24bit negative */
+ foo|=0xFF000000;
+ return foo;
+}
+
+static void nubus_move(unsigned char **ptr, int len, int map)
+{
+ if(len>0)
+ nubus_advance(ptr,len,map);
+ else if(len<0)
+ nubus_rewind(ptr,-len,map);
+}
+
+static void *nubus_rom_addr(int slot)
+{
+ /*
+ * Returns the first byte after the card. We then walk
+ * backwards to get the lane register and the config
+ */
+ return (void *)(0xF1000000+(slot<<24));
+}
+
+void nubus_memcpy(int slot, void *to, unsigned char *p, int len)
+{
+ unsigned char *t=(unsigned char *)to;
+ while(len)
+ {
+ *t++=nubus_get_rom(&p,1, nubus_slots[slot].slot_lanes);
+ len--;
+ }
+}
+
+void nubus_strncpy(int slot, void *to, unsigned char *p, int len)
+{
+ unsigned char *t=(unsigned char *)to;
+ while(len)
+ {
+ *t=nubus_get_rom(&p,1, nubus_slots[slot].slot_lanes);
+ if(!*t++)
+ break;
+ len--;
+ }
+}
+
+
+
+
+unsigned char *nubus_dirptr(struct nubus_dirent *nd)
+{
+ unsigned char *p=(unsigned char *)(nd->base);
+
+ nubus_move(&p, nubus_expand32(nd->value), nd->mask);
+ return p;
+}
+
+
+struct nubus_dir *nubus_openrootdir(int slot)
+{
+ static struct nubus_dir nbdir;
+ unsigned char *rp=nubus_rom_addr(slot);
+
+ nubus_rewind(&rp,20, nubus_slots[slot].slot_lanes);
+
+ nubus_move(&rp, nubus_expand32(nubus_slots[slot].slot_directory),
+ nubus_slots[slot].slot_lanes);
+
+ nbdir.base=rp;
+ nbdir.length=nubus_slots[slot].slot_dlength;
+ nbdir.count=0;
+ nbdir.mask=nubus_slots[slot].slot_lanes;
+ return &nbdir;
+}
+
+struct nubus_dir *nubus_opensubdir(struct nubus_dirent *d)
+{
+ static struct nubus_dir nbdir;
+ unsigned char *rp=nubus_dirptr(d);
+ nbdir.base=rp;
+ nbdir.length=99999;/*slots[i].slot_dlength;*/
+ nbdir.count=0;
+ nbdir.mask=d->mask;
+ return &nbdir;
+}
+
+void nubus_closedir(struct nubus_dir *nd)
+{
+ ;
+}
+
+struct nubus_dirent *nubus_readdir(struct nubus_dir *nd)
+{
+ u32 resid;
+ u8 rescode;
+ static struct nubus_dirent d;
+
+ if(nd->count==nd->length)
+ return NULL;
+
+ d.base=(unsigned long)nd->base;
+
+ resid=nubus_get_rom(&nd->base, 4, nd->mask);
+ nd->count++;
+ rescode=resid>>24;
+ if(rescode==0xFF)
+ {
+ nd->count=nd->length;
+ return NULL;
+ }
+ d.type=rescode;
+ d.value=resid&0xFFFFFF;
+ d.mask=nd->mask;
+ return &d;
+}
+
+/*
+ * MAC video handling irritations
+ */
+
+static unsigned char nubus_vid_byte[16];
+static unsigned long nubus_vid_offset[16];
+
+static void nubus_irqsplat(int slot, void *dev_id, struct pt_regs *regs)
+{
+ unsigned char *p=((unsigned char *)nubus_slot_addr(slot))+
+ nubus_vid_offset[slot];
+ *p=nubus_vid_byte[slot];
+}
+
+static int nubus_add_irqsplatter(int slot, unsigned long ptr, unsigned char v)
+{
+ nubus_vid_byte[slot]=v;
+ nubus_vid_offset[slot]=ptr;
+ nubus_request_irq(slot, NULL, nubus_irqsplat);
+ return 0;
+}
+
+void nubus_video_shutup(int slot, struct nubus_type *nt)
+{
+ if(nt->category!=3 /* Display */ || nt->type!=1 /* Video */
+ || nt->DrSW!=1 /* Quickdraw device */)
+ return;
+ switch(nt->DrHW)
+ {
+ /*
+ * Toby and MacII Hires cards. These behave in a MacII
+ * anyway but not on an RBV box
+ */
+ case 0x0001:
+ case 0x0013:
+ nubus_add_irqsplatter(slot, 0xA0000, 0);
+ break;
+ /*
+ * Apple workstation video card.
+ */
+ case 0x0006:
+ nubus_add_irqsplatter(slot, 0xA00000, 0);
+ break;
+ /*
+ * Futura cards
+ */
+ case 0x0417:
+ case 0x042F:
+ nubus_add_irqsplatter(slot, 0xF05000, 0x80);
+ break;
+
+ /*
+ * Fingers crossed 8)
+ *
+ * If you have another card and an RBV based mac you'll
+ * almost certainly have to add it here to make it work.
+ */
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Device list
+ */
+
+static struct nubus_device_specifier *nubus_device_list=NULL;
+
+void register_nubus_device(struct nubus_device_specifier *d)
+{
+ d->next=nubus_device_list;
+ nubus_device_list=d;
+}
+
+void unregister_nubus_device(struct nubus_device_specifier *nb)
+{
+ struct nubus_device_specifier **t=&nubus_device_list;
+ while(*t!=nb && *t)
+ t=&((*t)->next);
+ *t=nb->next;
+}
+
+static struct nubus_device_specifier *find_nubus_device(int slot, struct nubus_type *nt)
+{
+ struct nubus_device_specifier *t=nubus_device_list;
+ while(t!=NULL)
+ {
+ if(t->setup(t,slot, nt)==0)
+ return t;
+ t=t->next;
+ }
+ printk("No driver for device [%d %d %d %d]\n",
+ nt->category, nt->type, nt->DrHW, nt->DrSW);
+ return NULL;
+}
+
+/*
+ * Probe a nubus slot
+ */
+
+void nubus_probe_slot(int slot, int mode)
+{
+ unsigned char *rp;
+ unsigned char dp;
+ int lanes;
+ int i;
+ unsigned long dpat;
+ struct nubus_dir *dir;
+ struct nubus_dirent *nd;
+ struct nubus_type type_info;
+
+ /*
+ * Ok see whats cooking in the bytelanes
+ */
+
+ rp=nubus_rom_addr(slot);
+
+ for(i=4;i;i--)
+ {
+ rp--;
+
+ if(!nubus_hwreg_present(rp))
+ continue;
+
+ dp=*rp;
+
+ if(dp==0)
+ continue;
+
+ /*
+ * Valid ?
+ */
+
+ if((((dp>>4)^dp)&0x0F)!=0x0F)
+ continue;
+
+ if((dp&0x0F) >= 1<<i)
+ continue;
+
+ /*
+ * Looks promising
+ */
+
+ nubus_slots[slot].slot_flags|=NUBUS_DEVICE_PRESENT;
+ lanes=dp;
+
+ if (mode==0)
+ printk("nubus%c: ",
+ "0123456789abcdef"[slot]);
+
+
+ /*
+ * Time to dig deeper. Find the ROM base
+ * and read it
+ */
+
+ rp=nubus_rom_addr(slot);
+
+ /*
+ * Now to make this more fun the ROM is only visible
+ * on its bytelanes - that is smeared across the address
+ * space.
+ */
+
+ nubus_rewind(&rp,20,lanes);
+
+ nubus_slots[slot].slot_directory= nubus_get_rom(&rp,4,lanes);
+ nubus_slots[slot].slot_dlength = nubus_get_rom(&rp,4,lanes);
+ nubus_slots[slot].slot_crc = nubus_get_rom(&rp,4,lanes);
+ nubus_slots[slot].slot_rev = nubus_get_rom(&rp,1,lanes);
+ nubus_slots[slot].slot_format = nubus_get_rom(&rp,1,lanes);
+ nubus_slots[slot].slot_lanes = lanes;
+
+ dpat=nubus_get_rom(&rp,4,lanes);
+
+ /*
+ * Ok now check what we got
+ */
+
+ if(!(nubus_slots[slot].slot_directory&0x00FF0000))
+ printk("Dodgy doffset ??\n");
+ if(dpat!=0x5A932BC7)
+ printk("Wrong test pattern %lx\n",dpat);
+
+ /*
+ * I wonder how the CRC is meant to work -
+ * any takers ?
+ */
+
+
+ /*
+ * Now parse the directories on the card
+ */
+
+
+ dir=nubus_openrootdir(slot);
+
+ /*
+ * Find the board resource
+ */
+
+ while((nd=nubus_readdir(dir))!=NULL)
+ {
+ /*
+ * This ought to be 1. 1 doesn't work, 0x80
+ * does. Seems the Apple docs are wrong.
+ */
+ if(nd->type==0x80/*RES_ID_BOARD_DIR*/)
+ break;
+ }
+
+ nubus_closedir(dir);
+
+ if(nd==NULL)
+ {
+ printk("board resource not found!\n");
+ return;
+ }
+
+ dir=nubus_opensubdir(nd);
+
+ /*
+ * Walk the board resource
+ */
+
+ while((nd=nubus_readdir(dir))!=NULL)
+ {
+ switch(nd->type)
+ {
+ case RES_ID_TYPE:
+ {
+ unsigned short nbtdata[4];
+ nubus_memcpy(slot, nbtdata, nubus_dirptr(nd), 8);
+ type_info.category=nbtdata[0];
+ type_info.type=nbtdata[1];
+ type_info.DrHW=nbtdata[2];
+ type_info.DrSW=nbtdata[3];
+ break;
+ }
+ case RES_ID_NAME:
+ nubus_strncpy(slot, nubus_slots[slot].slot_cardname,nubus_dirptr(nd),64);
+ break;
+ default:
+ ;
+ }
+ }
+
+ nubus_closedir(dir);
+
+ /*
+ * Attempt to bind a driver to this slot
+ */
+
+ if (mode==0) {
+ printk("%s\n",
+ nubus_slots[slot].slot_cardname);
+ find_nubus_device(slot,&type_info);
+ }
+ if (mode==1)
+ nubus_video_shutup(slot, &type_info);
+
+ return;
+ }
+}
+
+
+void nubus_probe_bus(void)
+{
+ int i;
+ for(i=9;i<15;i++)
+ {
+ /* printk("nubus: probing slot %d !\n", i); */
+ nubus_probe_slot(i, 0);
+ }
+}
+
+/*
+ * RBV machines have level triggered video interrupts, and a VIA
+ * emulation that doesn't always seem to include being able to disable
+ * an interrupt. Totally lusing hardware. Before we can init irq's we
+ * have to install a handler to shut the bloody things up.
+ */
+
+void nubus_sweep_video(void)
+{
+ int i;
+ return; /* XXX why ?? */
+ for(i=9;i<15;i++)
+ {
+ nubus_probe_slot(i,1);
+ }
+}
+
+/*
+ * Support functions
+ */
+
+int nubus_ethernet_addr(int slot, unsigned char *addr)
+{
+ struct nubus_dir *nb;
+ struct nubus_dirent *d;
+ int ng=-ENOENT;
+
+ nb=nubus_openrootdir(slot);
+
+ if(nb==NULL)
+ return -ENOENT;
+
+ while((d=nubus_readdir(nb))!=NULL)
+ {
+ if(d->type==0x80) /* First private resource */
+ break;
+ }
+ if(d==NULL)
+ return -ENOENT;
+
+ nb=nubus_opensubdir(d);
+
+ while((d=nubus_readdir(nb))!=NULL)
+ {
+ if(d->type==0x80) /* First private field is the mac */
+ {
+ int i;
+ nubus_memcpy(slot, addr, nubus_dirptr(d), 6);
+/* printk("d.base=%lX, d.value=%lX\n",
+ d->base,d->value);
+ memcpy(addr,"\xC0\xC1\xC2\xC3\xC4\xC5",6);*/
+ printk("MAC address: ");
+ for(i=0;i<6;i++)
+ {
+ printk("%s%02X", i?":":"", addr[i]);
+ }
+ ng=0;
+ break;
+ }
+ else
+ printk("ID=%d val=%x\n",
+ d->type, d->value);
+ }
+ return ng;
+}
+
+void nubus_init(void)
+{
+ /*
+ * Register cards
+ */
+#ifdef CONFIG_DAYNAPORT
+ extern struct nubus_device_specifier nubus_8390;
+#endif
+
+ if (!MACH_IS_MAC)
+ return;
+
+#ifdef LCIII_WEIRDNESS
+ if (macintosh_config->ident == MAC_MODEL_LCIII) {
+ printk("nubus init: LCIII has no nubus!\n");
+ return;
+ }
+#endif
+
+#ifdef CONFIG_DAYNAPORT
+ register_nubus_device(&nubus_8390);
+#endif
+
+ /*
+ * And probe
+ */
+
+ nubus_init_via();
+ printk("Scanning nubus slots.\n");
+ nubus_probe_bus();
+}