| /* $Id: sbus.c,v 1.100 2002/01/24 15:36:24 davem Exp $ | 
 |  * sbus.c:  SBus support routines. | 
 |  * | 
 |  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) | 
 |  */ | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/slab.h> | 
 | #include <linux/config.h> | 
 | #include <linux/init.h> | 
 | #include <linux/pci.h> | 
 |  | 
 | #include <asm/system.h> | 
 | #include <asm/sbus.h> | 
 | #include <asm/dma.h> | 
 | #include <asm/oplib.h> | 
 | #include <asm/bpp.h> | 
 | #include <asm/irq.h> | 
 |  | 
 | struct sbus_bus *sbus_root = NULL; | 
 |  | 
 | static struct linux_prom_irqs irqs[PROMINTR_MAX] __initdata = { { 0 } }; | 
 | #ifdef CONFIG_SPARC32 | 
 | static int interrupts[PROMINTR_MAX] __initdata = { 0 }; | 
 | #endif | 
 |  | 
 | #ifdef CONFIG_PCI | 
 | extern int pcic_present(void); | 
 | #endif | 
 |  | 
 | /* 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. | 
 |  */ | 
 |  | 
 | /* We call this for each SBus device, and fill the structure based | 
 |  * upon the prom device tree.  We return the start of memory after | 
 |  * the things we have allocated. | 
 |  */ | 
 |  | 
 | /* #define DEBUG_FILL */ | 
 |  | 
 | static void __init fill_sbus_device(int prom_node, struct sbus_dev *sdev) | 
 | { | 
 | 	unsigned long address, base; | 
 | 	int len; | 
 |  | 
 | 	sdev->prom_node = prom_node; | 
 | 	prom_getstring(prom_node, "name", | 
 | 		       sdev->prom_name, sizeof(sdev->prom_name)); | 
 | 	address = prom_getint(prom_node, "address"); | 
 | 	len = prom_getproperty(prom_node, "reg", | 
 | 			       (char *) sdev->reg_addrs, | 
 | 			       sizeof(sdev->reg_addrs)); | 
 | 	if (len == -1) { | 
 | 		sdev->num_registers = 0; | 
 | 		goto no_regs; | 
 | 	} | 
 |  | 
 | 	if (len % sizeof(struct linux_prom_registers)) { | 
 | 		prom_printf("fill_sbus_device: proplen for regs of %s " | 
 | 			    " was %d, need multiple of %d\n", | 
 | 			    sdev->prom_name, len, | 
 | 			    (int) sizeof(struct linux_prom_registers)); | 
 | 		prom_halt(); | 
 | 	} | 
 | 	if (len > (sizeof(struct linux_prom_registers) * PROMREG_MAX)) { | 
 | 		prom_printf("fill_sbus_device: Too many register properties " | 
 | 			    "for device %s, len=%d\n", | 
 | 			    sdev->prom_name, len); | 
 | 		prom_halt(); | 
 | 	} | 
 | 	sdev->num_registers = len / sizeof(struct linux_prom_registers); | 
 | 	sdev->ranges_applied = 0; | 
 |  | 
 | 	base = (unsigned long) sdev->reg_addrs[0].phys_addr; | 
 |  | 
 | 	/* Compute the slot number. */ | 
 | 	if (base >= SUN_SBUS_BVADDR && sparc_cpu_model == sun4m) { | 
 | 		sdev->slot = sbus_dev_slot(base); | 
 | 	} else { | 
 | 		sdev->slot = sdev->reg_addrs[0].which_io; | 
 | 	} | 
 |  | 
 | no_regs: | 
 | 	len = prom_getproperty(prom_node, "ranges", | 
 | 			       (char *)sdev->device_ranges, | 
 | 			       sizeof(sdev->device_ranges)); | 
 | 	if (len == -1) { | 
 | 		sdev->num_device_ranges = 0; | 
 | 		goto no_ranges; | 
 | 	} | 
 | 	if (len % sizeof(struct linux_prom_ranges)) { | 
 | 		prom_printf("fill_sbus_device: proplen for ranges of %s " | 
 | 			    " was %d, need multiple of %d\n", | 
 | 			    sdev->prom_name, len, | 
 | 			    (int) sizeof(struct linux_prom_ranges)); | 
 | 		prom_halt(); | 
 | 	} | 
 | 	if (len > (sizeof(struct linux_prom_ranges) * PROMREG_MAX)) { | 
 | 		prom_printf("fill_sbus_device: Too many range properties " | 
 | 			    "for device %s, len=%d\n", | 
 | 			    sdev->prom_name, len); | 
 | 		prom_halt(); | 
 | 	} | 
 | 	sdev->num_device_ranges = | 
 | 		len / sizeof(struct linux_prom_ranges); | 
 |  | 
 | no_ranges: | 
 | 	/* XXX Unfortunately, IRQ issues are very arch specific. | 
 | 	 * XXX Pull this crud out into an arch specific area | 
 | 	 * XXX at some point. -DaveM | 
 | 	 */ | 
 | #ifdef CONFIG_SPARC64 | 
 | 	len = prom_getproperty(prom_node, "interrupts", | 
 | 			       (char *) irqs, sizeof(irqs)); | 
 | 	if (len == -1 || len == 0) { | 
 | 		sdev->irqs[0] = 0; | 
 | 		sdev->num_irqs = 0; | 
 | 	} else { | 
 | 		unsigned int pri = irqs[0].pri; | 
 |  | 
 | 		sdev->num_irqs = 1; | 
 | 		if (pri < 0x20) | 
 | 			pri += sdev->slot * 8; | 
 |  | 
 | 		sdev->irqs[0] =	sbus_build_irq(sdev->bus, pri); | 
 | 	} | 
 | #endif /* CONFIG_SPARC64 */ | 
 |  | 
 | #ifdef CONFIG_SPARC32 | 
 | 	len = prom_getproperty(prom_node, "intr", | 
 | 			       (char *)irqs, sizeof(irqs)); | 
 | 	if (len != -1) { | 
 | 		sdev->num_irqs = len / 8; | 
 | 		if (sdev->num_irqs == 0) { | 
 | 			sdev->irqs[0] = 0; | 
 | 		} else if (sparc_cpu_model == sun4d) { | 
 | 			extern unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq); | 
 |  | 
 | 			for (len = 0; len < sdev->num_irqs; len++) | 
 | 				sdev->irqs[len] = sun4d_build_irq(sdev, irqs[len].pri); | 
 | 		} else { | 
 | 			for (len = 0; len < sdev->num_irqs; len++) | 
 | 				sdev->irqs[len] = irqs[len].pri; | 
 | 		} | 
 | 	} else { | 
 | 		/* No "intr" node found-- check for "interrupts" node. | 
 | 		 * This node contains SBus interrupt levels, not IPLs | 
 | 		 * as in "intr", and no vector values.  We convert  | 
 | 		 * SBus interrupt levels to PILs (platform specific). | 
 | 		 */ | 
 | 		len = prom_getproperty(prom_node, "interrupts",  | 
 | 					(char *)interrupts, sizeof(interrupts)); | 
 | 		if (len == -1) { | 
 | 			sdev->irqs[0] = 0; | 
 | 			sdev->num_irqs = 0; | 
 | 		} else { | 
 | 			sdev->num_irqs = len / sizeof(int); | 
 | 			for (len = 0; len < sdev->num_irqs; len++) { | 
 | 				sdev->irqs[len] = sbint_to_irq(sdev, interrupts[len]); | 
 | 			} | 
 | 		} | 
 | 	}  | 
 | #endif /* CONFIG_SPARC32 */ | 
 | } | 
 |  | 
 | /* This routine gets called from whoever needs the sbus first, to scan | 
 |  * the SBus device tree.  Currently it just prints out the devices | 
 |  * found on the bus and builds trees of SBUS structs and attached | 
 |  * devices. | 
 |  */ | 
 |  | 
 | extern void iommu_init(int iommu_node, struct sbus_bus *sbus); | 
 | extern void iounit_init(int sbi_node, int iounit_node, struct sbus_bus *sbus); | 
 | void sun4_init(void); | 
 | #ifdef CONFIG_SUN_AUXIO | 
 | extern void auxio_probe(void); | 
 | #endif | 
 |  | 
 | static void __init sbus_do_child_siblings(int start_node, | 
 | 					  struct sbus_dev *child, | 
 | 					  struct sbus_dev *parent, | 
 | 					  struct sbus_bus *sbus) | 
 | { | 
 | 	struct sbus_dev *this_dev = child; | 
 | 	int this_node = start_node; | 
 |  | 
 | 	/* Child already filled in, just need to traverse siblings. */ | 
 | 	child->child = NULL; | 
 | 	child->parent = parent; | 
 | 	while((this_node = prom_getsibling(this_node)) != 0) { | 
 | 		this_dev->next = kmalloc(sizeof(struct sbus_dev), GFP_ATOMIC); | 
 | 		this_dev = this_dev->next; | 
 | 		this_dev->next = NULL; | 
 | 		this_dev->parent = parent; | 
 |  | 
 | 		this_dev->bus = sbus; | 
 | 		fill_sbus_device(this_node, this_dev); | 
 |  | 
 | 		if(prom_getchild(this_node)) { | 
 | 			this_dev->child = kmalloc(sizeof(struct sbus_dev), | 
 | 						  GFP_ATOMIC); | 
 | 			this_dev->child->bus = sbus; | 
 | 			this_dev->child->next = NULL; | 
 | 			fill_sbus_device(prom_getchild(this_node), this_dev->child); | 
 | 			sbus_do_child_siblings(prom_getchild(this_node), | 
 | 					       this_dev->child, this_dev, sbus); | 
 | 		} else { | 
 | 			this_dev->child = NULL; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * XXX This functions appears to be a distorted version of | 
 |  * prom_sbus_ranges_init(), with all sun4d stuff cut away. | 
 |  * Ask DaveM what is going on here, how is sun4d supposed to work... XXX | 
 |  */ | 
 | /* added back sun4d patch from Thomas Bogendoerfer - should be OK (crn) */ | 
 |  | 
 | static void __init sbus_bus_ranges_init(int parent_node, struct sbus_bus *sbus) | 
 | { | 
 | 	int len; | 
 |  | 
 | 	len = prom_getproperty(sbus->prom_node, "ranges", | 
 | 			       (char *) sbus->sbus_ranges, | 
 | 			       sizeof(sbus->sbus_ranges)); | 
 | 	if (len == -1 || len == 0) { | 
 | 		sbus->num_sbus_ranges = 0; | 
 | 		return; | 
 | 	} | 
 | 	sbus->num_sbus_ranges = len / sizeof(struct linux_prom_ranges); | 
 | #ifdef CONFIG_SPARC32 | 
 | 	if (sparc_cpu_model == sun4d) { | 
 | 		struct linux_prom_ranges iounit_ranges[PROMREG_MAX]; | 
 | 		int num_iounit_ranges; | 
 |  | 
 | 		len = prom_getproperty(parent_node, "ranges", | 
 | 				       (char *) iounit_ranges, | 
 | 				       sizeof (iounit_ranges)); | 
 | 		if (len != -1) { | 
 | 			num_iounit_ranges = (len/sizeof(struct linux_prom_ranges)); | 
 | 			prom_adjust_ranges (sbus->sbus_ranges, sbus->num_sbus_ranges, iounit_ranges, num_iounit_ranges); | 
 | 		} | 
 | 	} | 
 | #endif | 
 | } | 
 |  | 
 | static void __init __apply_ranges_to_regs(struct linux_prom_ranges *ranges, | 
 | 					  int num_ranges, | 
 | 					  struct linux_prom_registers *regs, | 
 | 					  int num_regs) | 
 | { | 
 | 	if (num_ranges) { | 
 | 		int regnum; | 
 |  | 
 | 		for (regnum = 0; regnum < num_regs; regnum++) { | 
 | 			int rngnum; | 
 |  | 
 | 			for (rngnum = 0; rngnum < num_ranges; rngnum++) { | 
 | 				if (regs[regnum].which_io == ranges[rngnum].ot_child_space) | 
 | 					break; | 
 | 			} | 
 | 			if (rngnum == num_ranges) { | 
 | 				/* We used to flag this as an error.  Actually | 
 | 				 * some devices do not report the regs as we expect. | 
 | 				 * For example, see SUNW,pln device.  In that case | 
 | 				 * the reg property is in a format internal to that | 
 | 				 * node, ie. it is not in the SBUS register space | 
 | 				 * per se. -DaveM | 
 | 				 */ | 
 | 				return; | 
 | 			} | 
 | 			regs[regnum].which_io = ranges[rngnum].ot_parent_space; | 
 | 			regs[regnum].phys_addr -= ranges[rngnum].ot_child_base; | 
 | 			regs[regnum].phys_addr += ranges[rngnum].ot_parent_base; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static void __init __fixup_regs_sdev(struct sbus_dev *sdev) | 
 | { | 
 | 	if (sdev->num_registers != 0) { | 
 | 		struct sbus_dev *parent = sdev->parent; | 
 | 		int i; | 
 |  | 
 | 		while (parent != NULL) { | 
 | 			__apply_ranges_to_regs(parent->device_ranges, | 
 | 					       parent->num_device_ranges, | 
 | 					       sdev->reg_addrs, | 
 | 					       sdev->num_registers); | 
 |  | 
 | 			parent = parent->parent; | 
 | 		} | 
 |  | 
 | 		__apply_ranges_to_regs(sdev->bus->sbus_ranges, | 
 | 				       sdev->bus->num_sbus_ranges, | 
 | 				       sdev->reg_addrs, | 
 | 				       sdev->num_registers); | 
 |  | 
 | 		for (i = 0; i < sdev->num_registers; i++) { | 
 | 			struct resource *res = &sdev->resource[i]; | 
 |  | 
 | 			res->start = sdev->reg_addrs[i].phys_addr; | 
 | 			res->end = (res->start + | 
 | 				    (unsigned long)sdev->reg_addrs[i].reg_size - 1UL); | 
 | 			res->flags = IORESOURCE_IO | | 
 | 				(sdev->reg_addrs[i].which_io & 0xff); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static void __init sbus_fixup_all_regs(struct sbus_dev *first_sdev) | 
 | { | 
 | 	struct sbus_dev *sdev; | 
 |  | 
 | 	for (sdev = first_sdev; sdev; sdev = sdev->next) { | 
 | 		if (sdev->child) | 
 | 			sbus_fixup_all_regs(sdev->child); | 
 | 		__fixup_regs_sdev(sdev); | 
 | 	} | 
 | } | 
 |  | 
 | extern void register_proc_sparc_ioport(void); | 
 | extern void firetruck_init(void); | 
 |  | 
 | #ifdef CONFIG_SUN4 | 
 | extern void sun4_dvma_init(void); | 
 | #endif | 
 |  | 
 | static int __init sbus_init(void) | 
 | { | 
 | 	int nd, this_sbus, sbus_devs, topnd, iommund; | 
 | 	unsigned int sbus_clock; | 
 | 	struct sbus_bus *sbus; | 
 | 	struct sbus_dev *this_dev; | 
 | 	int num_sbus = 0;  /* How many did we find? */ | 
 |  | 
 | #ifdef CONFIG_SPARC32 | 
 | 	register_proc_sparc_ioport(); | 
 | #endif | 
 |  | 
 | #ifdef CONFIG_SUN4 | 
 | 	sun4_dvma_init(); | 
 | 	return 0; | 
 | #endif | 
 |  | 
 | 	topnd = prom_getchild(prom_root_node); | 
 | 	 | 
 | 	/* Finding the first sbus is a special case... */ | 
 | 	iommund = 0; | 
 | 	if(sparc_cpu_model == sun4u) { | 
 | 		nd = prom_searchsiblings(topnd, "sbus"); | 
 | 		if(nd == 0) { | 
 | #ifdef CONFIG_PCI | 
 | 			if (!pcic_present()) { | 
 | 				prom_printf("Neither SBUS nor PCI found.\n"); | 
 | 				prom_halt(); | 
 | 			} else { | 
 | #ifdef CONFIG_SPARC64 | 
 | 				firetruck_init(); | 
 | #endif | 
 | 			} | 
 | 			return 0; | 
 | #else | 
 | 			prom_printf("YEEE, UltraSparc sbus not found\n"); | 
 | 			prom_halt(); | 
 | #endif | 
 | 		} | 
 | 	} 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) { | 
 | #ifdef CONFIG_PCI | 
 |                         if (!pcic_present()) { | 
 |                                 prom_printf("Neither SBUS nor PCI found.\n"); | 
 |                                 prom_halt(); | 
 |                         } | 
 |                         return 0; | 
 | #else | 
 | 			/* No reason to run further - the data access trap will occur. */ | 
 | 			panic("sbus not found"); | 
 | #endif | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Ok, we've found the first one, allocate first SBus struct | 
 | 	 * and place in chain. | 
 | 	 */ | 
 | 	sbus = sbus_root = kmalloc(sizeof(struct sbus_bus), GFP_ATOMIC); | 
 | 	sbus->next = NULL; | 
 | 	sbus->prom_node = nd; | 
 | 	this_sbus = nd; | 
 |  | 
 | 	if(iommund && sparc_cpu_model != sun4u && sparc_cpu_model != sun4d) | 
 | 		iommu_init(iommund, sbus); | 
 |  | 
 | 	/* Loop until we find no more SBUS's */ | 
 | 	while(this_sbus) { | 
 | #ifdef CONFIG_SPARC64 | 
 | 		/* IOMMU hides inside SBUS/SYSIO prom node on Ultra. */ | 
 | 		if(sparc_cpu_model == sun4u) { | 
 | 			extern void sbus_iommu_init(int prom_node, struct sbus_bus *sbus); | 
 |  | 
 | 			sbus_iommu_init(this_sbus, sbus); | 
 | 		} | 
 | #endif /* CONFIG_SPARC64 */ | 
 |  | 
 | #ifdef CONFIG_SPARC32 | 
 | 		if (sparc_cpu_model == sun4d) | 
 | 			iounit_init(this_sbus, iommund, sbus); | 
 | #endif /* CONFIG_SPARC32 */ | 
 | 		printk("sbus%d: ", num_sbus); | 
 | 		sbus_clock = prom_getint(this_sbus, "clock-frequency"); | 
 | 		if(sbus_clock == -1) | 
 | 			sbus_clock = (25*1000*1000); | 
 | 		printk("Clock %d.%d MHz\n", (int) ((sbus_clock/1000)/1000), | 
 | 		       (int) (((sbus_clock/1000)%1000 != 0) ?  | 
 | 			      (((sbus_clock/1000)%1000) + 1000) : 0)); | 
 |  | 
 | 		prom_getstring(this_sbus, "name", | 
 | 			       sbus->prom_name, sizeof(sbus->prom_name)); | 
 | 		sbus->clock_freq = sbus_clock; | 
 | #ifdef CONFIG_SPARC32 | 
 | 		if (sparc_cpu_model == sun4d) { | 
 | 			sbus->devid = prom_getint(iommund, "device-id"); | 
 | 			sbus->board = prom_getint(iommund, "board#"); | 
 | 		} | 
 | #endif | 
 | 		 | 
 | 		sbus_bus_ranges_init(iommund, sbus); | 
 |  | 
 | 		sbus_devs = prom_getchild(this_sbus); | 
 | 		if (!sbus_devs) { | 
 | 			sbus->devices = NULL; | 
 | 			goto next_bus; | 
 | 		} | 
 |  | 
 | 		sbus->devices = kmalloc(sizeof(struct sbus_dev), GFP_ATOMIC); | 
 |  | 
 | 		this_dev = sbus->devices; | 
 | 		this_dev->next = NULL; | 
 |  | 
 | 		this_dev->bus = sbus; | 
 | 		this_dev->parent = NULL; | 
 | 		fill_sbus_device(sbus_devs, this_dev); | 
 |  | 
 | 		/* Should we traverse for children? */ | 
 | 		if(prom_getchild(sbus_devs)) { | 
 | 			/* Allocate device node */ | 
 | 			this_dev->child = kmalloc(sizeof(struct sbus_dev), | 
 | 						  GFP_ATOMIC); | 
 | 			/* Fill it */ | 
 | 			this_dev->child->bus = sbus; | 
 | 			this_dev->child->next = NULL; | 
 | 			fill_sbus_device(prom_getchild(sbus_devs), | 
 | 					 this_dev->child); | 
 | 			sbus_do_child_siblings(prom_getchild(sbus_devs), | 
 | 					       this_dev->child, | 
 | 					       this_dev, | 
 | 					       sbus); | 
 | 		} else { | 
 | 			this_dev->child = NULL; | 
 | 		} | 
 |  | 
 | 		while((sbus_devs = prom_getsibling(sbus_devs)) != 0) { | 
 | 			/* Allocate device node */ | 
 | 			this_dev->next = kmalloc(sizeof(struct sbus_dev), | 
 | 						 GFP_ATOMIC); | 
 | 			this_dev = this_dev->next; | 
 | 			this_dev->next = NULL; | 
 |  | 
 | 			/* Fill it */ | 
 | 			this_dev->bus = sbus; | 
 | 			this_dev->parent = NULL; | 
 | 			fill_sbus_device(sbus_devs, this_dev); | 
 |  | 
 | 			/* Is there a child node hanging off of us? */ | 
 | 			if(prom_getchild(sbus_devs)) { | 
 | 				/* Get new device struct */ | 
 | 				this_dev->child = kmalloc(sizeof(struct sbus_dev), | 
 | 							  GFP_ATOMIC); | 
 | 				/* Fill it */ | 
 | 				this_dev->child->bus = sbus; | 
 | 				this_dev->child->next = NULL; | 
 | 				fill_sbus_device(prom_getchild(sbus_devs), | 
 | 						 this_dev->child); | 
 | 				sbus_do_child_siblings(prom_getchild(sbus_devs), | 
 | 						       this_dev->child, | 
 | 						       this_dev, | 
 | 						       sbus); | 
 | 			} else { | 
 | 				this_dev->child = NULL; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/* Walk all devices and apply parent ranges. */ | 
 | 		sbus_fixup_all_regs(sbus->devices); | 
 |  | 
 | 		dvma_init(sbus); | 
 | 	next_bus: | 
 | 		num_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 = kmalloc(sizeof(struct sbus_bus), GFP_ATOMIC); | 
 | 			sbus = sbus->next; | 
 | 			sbus->next = NULL; | 
 | 			sbus->prom_node = this_sbus; | 
 | 		} else { | 
 | 			break; | 
 | 		} | 
 | 	} /* while(this_sbus) */ | 
 |  | 
 | 	if (sparc_cpu_model == sun4d) { | 
 | 		extern void sun4d_init_sbi_irq(void); | 
 | 		sun4d_init_sbi_irq(); | 
 | 	} | 
 | 	 | 
 | #ifdef CONFIG_SPARC64 | 
 | 	if (sparc_cpu_model == sun4u) { | 
 | 		firetruck_init(); | 
 | 	} | 
 | #endif | 
 | #ifdef CONFIG_SUN_AUXIO | 
 | 	if (sparc_cpu_model == sun4u) | 
 | 		auxio_probe (); | 
 | #endif | 
 | #ifdef CONFIG_SPARC64 | 
 | 	if (sparc_cpu_model == sun4u) { | 
 | 		extern void clock_probe(void); | 
 |  | 
 | 		clock_probe(); | 
 | 	} | 
 | #endif | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | subsys_initcall(sbus_init); |