summaryrefslogtreecommitdiffstats
path: root/drivers/sbus/sbus.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /drivers/sbus/sbus.c
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'drivers/sbus/sbus.c')
-rw-r--r--drivers/sbus/sbus.c209
1 files changed, 171 insertions, 38 deletions
diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c
index 0484ae0a8..4d59647cc 100644
--- a/drivers/sbus/sbus.c
+++ b/drivers/sbus/sbus.c
@@ -5,11 +5,14 @@
#include <linux/kernel.h>
#include <linux/malloc.h>
+#include <linux/config.h>
+#include <linux/init.h>
#include <asm/system.h>
#include <asm/sbus.h>
#include <asm/dma.h>
#include <asm/oplib.h>
+#include <asm/bpp.h>
/* This file has been written to be more dynamic and a bit cleaner,
* but it still needs some spring cleaning.
@@ -19,6 +22,8 @@ struct linux_sbus *SBus_chain;
static char lbuf[128];
+extern void prom_sbus_ranges_init (int, struct linux_sbus *);
+
/* Perhaps when I figure out more about the iommu we'll put a
* device registration routine here that probe_sbus() calls to
* setup the iommu for each Sbus.
@@ -30,8 +35,9 @@ static char lbuf[128];
*/
/* #define DEBUG_FILL */
-void
-fill_sbus_device(int nd, struct linux_sbus_device *sbus_dev)
+
+__initfunc(static void
+fill_sbus_device(int nd, struct linux_sbus_device *sbus_dev))
{
int grrr, len;
unsigned long dev_base_addr, base;
@@ -46,6 +52,8 @@ fill_sbus_device(int nd, struct linux_sbus_device *sbus_dev)
len = prom_getproperty(nd, "reg", (void *) sbus_dev->reg_addrs,
sizeof(sbus_dev->reg_addrs));
+ if(len == -1)
+ goto no_regs;
if(len%sizeof(struct linux_prom_registers)) {
prom_printf("WHOOPS: proplen for %s was %d, need multiple of %d\n",
sbus_dev->prom_name, len,
@@ -53,9 +61,12 @@ fill_sbus_device(int nd, struct linux_sbus_device *sbus_dev)
panic("fill_sbus_device");
}
sbus_dev->num_registers = (len/sizeof(struct linux_prom_registers));
+ sbus_dev->ranges_applied = 0;
base = (unsigned long) sbus_dev->reg_addrs[0].phys_addr;
- if(base>=SUN_SBUS_BVADDR || sparc_cpu_model == sun4m) {
+ if(base>=SUN_SBUS_BVADDR ||
+ sparc_cpu_model == sun4m ||
+ sparc_cpu_model == sun4u) {
/* Ahh, we can determine the slot and offset */
sbus_dev->slot = sbus_dev_slot(base);
sbus_dev->offset = sbus_dev_offset(base);
@@ -63,22 +74,22 @@ fill_sbus_device(int nd, struct linux_sbus_device *sbus_dev)
sbus_dev->slot = sbus_dev->reg_addrs[0].which_io;
sbus_dev->offset = base;
sbus_dev->reg_addrs[0].phys_addr =
- (char *) sbus_devaddr(sbus_dev->slot, base);
+ sbus_devaddr(sbus_dev->slot, base);
for(grrr=1; grrr<sbus_dev->num_registers; grrr++) {
base = (unsigned long) sbus_dev->reg_addrs[grrr].phys_addr;
- sbus_dev->reg_addrs[grrr].phys_addr = (char *)
+ sbus_dev->reg_addrs[grrr].phys_addr =
sbus_devaddr(sbus_dev->slot, base);
}
/* That surely sucked */
}
sbus_dev->sbus_addr = (unsigned long) sbus_dev->reg_addrs[0].phys_addr;
-
if(len>(sizeof(struct linux_prom_registers)*PROMREG_MAX)) {
prom_printf("WHOOPS: I got too many register addresses for %s len=%d\n",
sbus_dev->prom_name, len);
panic("sbus device register overflow");
}
+no_regs:
len = prom_getproperty(nd, "address", (void *) sbus_dev->sbus_vaddrs,
sizeof(sbus_dev->sbus_vaddrs));
if(len == -1) len=0;
@@ -89,39 +100,58 @@ fill_sbus_device(int nd, struct linux_sbus_device *sbus_dev)
}
sbus_dev->num_vaddrs = (len/4);
- len = prom_getproperty(nd, "intr", (void *)sbus_dev->irqs,
- sizeof(sbus_dev->irqs));
- if (len == -1) len=0;
- if (len&7) {
- prom_printf("Grrr, I didn't get a multiple of 8 proplen for "
- "device %s got %d\n", sbus_dev->prom_name, len);
- len=0;
- }
- sbus_dev->num_irqs=(len/8);
-#if OLD_STYLE_IRQ
- /* Grrr, V3 prom tries to be efficient */
- for(len=0; len<sbus_dev->num_irqs; len++) {
- sbus_dev->irqs[len].pri &= 0xf;
+ if(sparc_cpu_model == sun4u) {
+ len = prom_getproperty(nd, "interrupts", (void *)&sbus_dev->irqs[0].pri,
+ sizeof(sbus_dev->irqs[0].pri));
+ if((len == -1) || (len == 0)) {
+ sbus_dev->irqs[0].pri = 0;
+ sbus_dev->num_irqs = 0;
+ } else {
+ sbus_dev->num_irqs = 1;
+ }
+ } else {
+ len = prom_getproperty(nd, "intr", (void *)sbus_dev->irqs,
+ sizeof(sbus_dev->irqs));
+ if (len == -1) len=0;
+ if (len&7) {
+ prom_printf("Grrr, I didn't get a multiple of 8 proplen for "
+ "device %s got %d\n", sbus_dev->prom_name, len);
+ len=0;
+ }
+ sbus_dev->num_irqs=(len/8);
+ if(sbus_dev->num_irqs == 0)
+ sbus_dev->irqs[0].pri=0;
}
-#endif
- if(sbus_dev->num_irqs == 0) sbus_dev->irqs[0].pri=0;
-
#ifdef DEBUG_FILL
+#ifdef __sparc_v9__
+ prom_printf("Found %s at SBUS slot %x offset %016lx irq-level %d\n",
+ sbus_dev->prom_name, sbus_dev->slot, sbus_dev->offset,
+ sbus_dev->irqs[0].pri);
+ prom_printf("Base address %016lx\n", sbus_dev->sbus_addr);
+#else
prom_printf("Found %s at SBUS slot %x offset %08lx irq-level %d\n",
sbus_dev->prom_name, sbus_dev->slot, sbus_dev->offset,
sbus_dev->irqs[0].pri);
prom_printf("Base address %08lx\n", sbus_dev->sbus_addr);
+#endif
prom_printf("REGISTERS: Probed %d register(s)\n", sbus_dev->num_registers);
for(len=0; len<sbus_dev->num_registers; len++)
+#ifdef __sparc_v9__
prom_printf("Regs<%d> at address<%08lx> IO-space<%d> size<%d "
"bytes, %d words>\n", (int) len,
(unsigned long) sbus_dev->reg_addrs[len].phys_addr,
sbus_dev->reg_addrs[len].which_io,
sbus_dev->reg_addrs[len].reg_size,
(sbus_dev->reg_addrs[len].reg_size/4));
+#else
+ prom_printf("Regs<%d> at address<%016lx> IO-space<%d> size<%d "
+ "bytes, %d words>\n", (int) len,
+ (unsigned long) sbus_dev->reg_addrs[len].phys_addr,
+ sbus_dev->reg_addrs[len].which_io,
+ sbus_dev->reg_addrs[len].reg_size,
+ (sbus_dev->reg_addrs[len].reg_size/4));
+#endif
#endif
-
- return;
}
/* This routine gets called from whoever needs the sbus first, to scan
@@ -130,12 +160,60 @@ fill_sbus_device(int nd, struct linux_sbus_device *sbus_dev)
* devices.
*/
-extern void sun_console_init(void);
+extern unsigned long sun_console_init(unsigned long);
extern unsigned long iommu_init(int iommu_node, unsigned long memstart,
unsigned long memend, struct linux_sbus *sbus);
+extern void iommu_sun4d_init(int sbi_node, struct linux_sbus *sbus);
+#ifdef CONFIG_SUN_OPENPROMIO
+extern int openprom_init(void);
+#endif
+#ifdef CONFIG_SUN_MOSTEK_RTC
+extern int rtc_init(void);
+#endif
+#ifdef CONFIG_SPARCAUDIO
+extern int sparcaudio_init(void);
+#endif
+#ifdef CONFIG_SUN_AUXIO
+extern void auxio_probe(void);
+#endif
+
+__initfunc(static unsigned long
+sbus_do_child_siblings(unsigned long memory_start, int start_node,
+ struct linux_sbus_device *child,
+ struct linux_sbus *sbus))
+{
+ struct linux_sbus_device *this_dev = child;
+ int this_node = start_node;
-unsigned long
-sbus_init(unsigned long memory_start, unsigned long memory_end)
+ /* Child already filled in, just need to traverse siblings. */
+ child->child = 0;
+ while((this_node = prom_getsibling(this_node)) != 0) {
+ this_dev->next = (struct linux_sbus_device *) memory_start;
+ memory_start += sizeof(struct linux_sbus_device);
+ this_dev = this_dev->next;
+ this_dev->next = 0;
+
+ fill_sbus_device(this_node, this_dev);
+ this_dev->my_bus = sbus;
+
+ if(prom_getchild(this_node)) {
+ this_dev->child = (struct linux_sbus_device *) memory_start;
+ memory_start += sizeof(struct linux_sbus_device);
+ fill_sbus_device(prom_getchild(this_node), this_dev->child);
+ this_dev->child->my_bus = sbus;
+ memory_start = sbus_do_child_siblings(memory_start,
+ prom_getchild(this_node),
+ this_dev->child,
+ sbus);
+ } else {
+ this_dev->child = 0;
+ }
+ }
+ return memory_start;
+}
+
+__initfunc(unsigned long
+sbus_init(unsigned long memory_start, unsigned long memory_end))
{
register int nd, this_sbus, sbus_devs, topnd, iommund;
unsigned int sbus_clock;
@@ -149,7 +227,20 @@ sbus_init(unsigned long memory_start, unsigned long memory_end)
/* Finding the first sbus is a special case... */
iommund = 0;
- if((nd = prom_searchsiblings(topnd, "sbus")) == 0) {
+ if(sparc_cpu_model == sun4u) {
+ /* IOMMU "hides" inside SBUS/SYSIO node. */
+ iommund = nd = prom_searchsiblings(topnd, "sbus");
+ if(nd == 0) {
+ prom_printf("YEEE, UltraSparc sbus not found\n");
+ prom_halt();
+ }
+ } else if(sparc_cpu_model == sun4d) {
+ if((iommund = prom_searchsiblings(topnd, "io-unit")) == 0 ||
+ (nd = prom_getchild(iommund)) == 0 ||
+ (nd = prom_searchsiblings(nd, "sbi")) == 0) {
+ panic("sbi not found");
+ }
+ } else if((nd = prom_searchsiblings(topnd, "sbus")) == 0) {
if((iommund = prom_searchsiblings(topnd, "iommu")) == 0 ||
(nd = prom_getchild(iommund)) == 0 ||
(nd = prom_searchsiblings(nd, "sbus")) == 0) {
@@ -167,8 +258,12 @@ sbus_init(unsigned long memory_start, unsigned long memory_end)
this_sbus=nd;
/* Have IOMMU will travel. XXX grrr - this should be per sbus... */
- if(iommund)
- memory_start = iommu_init(iommund, memory_start, memory_end, sbus);
+ if(iommund) {
+ if (sparc_cpu_model == sun4d)
+ iommu_sun4d_init(this_sbus, sbus);
+ else
+ memory_start = iommu_init(iommund, memory_start, memory_end, sbus);
+ }
/* Loop until we find no more SBUS's */
while(this_sbus) {
@@ -183,6 +278,8 @@ sbus_init(unsigned long memory_start, unsigned long memory_end)
sbus->prom_node = this_sbus;
strcpy(sbus->prom_name, lbuf);
sbus->clock_freq = sbus_clock;
+
+ prom_sbus_ranges_init (iommund, sbus);
sbus_devs = prom_getchild(this_sbus);
@@ -196,14 +293,17 @@ sbus_init(unsigned long memory_start, unsigned long memory_end)
this_dev->my_bus = sbus;
/* Should we traverse for children? */
- if(strcmp(this_dev->prom_name, "espdma")==0 ||
- strcmp(this_dev->prom_name, "ledma")==0) {
+ if(prom_getchild(sbus_devs)) {
/* Allocate device node */
this_dev->child = (struct linux_sbus_device *) memory_start;
memory_start += sizeof(struct linux_sbus_device);
/* Fill it */
fill_sbus_device(prom_getchild(sbus_devs), this_dev->child);
this_dev->child->my_bus = sbus;
+ memory_start = sbus_do_child_siblings(memory_start,
+ prom_getchild(sbus_devs),
+ this_dev->child,
+ sbus);
} else {
this_dev->child = 0;
}
@@ -220,8 +320,7 @@ sbus_init(unsigned long memory_start, unsigned long memory_end)
this_dev->my_bus = sbus;
/* Is there a child node hanging off of us? */
- if(strcmp(this_dev->prom_name, "espdma")==0 ||
- strcmp(this_dev->prom_name, "ledma")==0) {
+ if(prom_getchild(sbus_devs)) {
/* Get new device struct */
this_dev->child =
(struct linux_sbus_device *) memory_start;
@@ -231,6 +330,11 @@ sbus_init(unsigned long memory_start, unsigned long memory_end)
fill_sbus_device(prom_getchild(sbus_devs),
this_dev->child);
this_dev->child->my_bus = sbus;
+ memory_start = sbus_do_child_siblings(
+ memory_start,
+ prom_getchild(sbus_devs),
+ this_dev->child,
+ sbus);
} else {
this_dev->child = 0;
}
@@ -239,9 +343,22 @@ sbus_init(unsigned long memory_start, unsigned long memory_end)
memory_start = dvma_init(sbus, memory_start);
num_sbus++;
- this_sbus = prom_getsibling(this_sbus);
- if(!this_sbus) break;
- this_sbus = prom_searchsiblings(this_sbus, "sbus");
+ if(sparc_cpu_model == sun4u) {
+ this_sbus = prom_getsibling(this_sbus);
+ if(!this_sbus)
+ break;
+ this_sbus = prom_searchsiblings(this_sbus, "sbus");
+ } else if(sparc_cpu_model == sun4d) {
+ iommund = prom_getsibling(iommund);
+ if(!iommund) break;
+ iommund = prom_searchsiblings(iommund, "io-unit");
+ if(!iommund) break;
+ this_sbus = prom_searchsiblings(prom_getchild(iommund), "sbi");
+ } else {
+ this_sbus = prom_getsibling(this_sbus);
+ if(!this_sbus) break;
+ this_sbus = prom_searchsiblings(this_sbus, "sbus");
+ }
if(this_sbus) {
sbus->next = (struct linux_sbus *) memory_start;
memory_start += sizeof(struct linux_sbus);
@@ -251,6 +368,22 @@ sbus_init(unsigned long memory_start, unsigned long memory_end)
break;
}
} /* while(this_sbus) */
- sun_console_init(); /* whee... */
+ memory_start = sun_console_init(memory_start); /* whee... */
+#ifdef CONFIG_SUN_OPENPROMIO
+ openprom_init();
+#endif
+#ifdef CONFIG_SUN_MOSTEK_RTC
+ rtc_init();
+#endif
+#ifdef CONFIG_SPARCAUDIO
+ sparcaudio_init();
+#endif
+#ifdef CONFIG_SUN_BPP
+ bpp_init();
+#endif
+#ifdef CONFIG_SUN_AUXIO
+ if (sparc_cpu_model == sun4u)
+ auxio_probe ();
+#endif
return memory_start;
}