| #include <linux/kernel.h> | 
 | #include <linux/init.h> | 
 | #include <linux/pci.h> | 
 | #include <linux/slab.h> | 
 | #include <asm/oplib.h> | 
 | #include <asm/isa.h> | 
 |  | 
 | struct sparc_isa_bridge *isa_chain; | 
 |  | 
 | static void __init fatal_err(const char *reason) | 
 | { | 
 | 	prom_printf("ISA: fatal error, %s.\n", reason); | 
 | } | 
 |  | 
 | static void __init report_dev(struct sparc_isa_device *isa_dev, int child) | 
 | { | 
 | 	if (child) | 
 | 		printk(" (%s)", isa_dev->prom_name); | 
 | 	else | 
 | 		printk(" [%s", isa_dev->prom_name); | 
 | } | 
 |  | 
 | static void __init isa_dev_get_resource(struct sparc_isa_device *isa_dev, | 
 | 					struct linux_prom_registers *pregs, | 
 | 					int pregs_size) | 
 | { | 
 | 	unsigned long base, len; | 
 | 	int prop_len; | 
 |  | 
 | 	prop_len = prom_getproperty(isa_dev->prom_node, "reg", | 
 | 				    (char *) pregs, pregs_size); | 
 |  | 
 | 	if (prop_len <= 0) | 
 | 		return; | 
 |  | 
 | 	/* Only the first one is interesting. */ | 
 | 	len = pregs[0].reg_size; | 
 | 	base = (((unsigned long)pregs[0].which_io << 32) | | 
 | 		(unsigned long)pregs[0].phys_addr); | 
 | 	base += isa_dev->bus->parent->io_space.start; | 
 |  | 
 | 	isa_dev->resource.start = base; | 
 | 	isa_dev->resource.end   = (base + len - 1UL); | 
 | 	isa_dev->resource.flags = IORESOURCE_IO; | 
 | 	isa_dev->resource.name  = isa_dev->prom_name; | 
 |  | 
 | 	request_resource(&isa_dev->bus->parent->io_space, | 
 | 			 &isa_dev->resource); | 
 | } | 
 |  | 
 | /* I can't believe they didn't put a real INO in the isa device | 
 |  * interrupts property.  The whole point of the OBP properties | 
 |  * is to shield the kernel from IRQ routing details. | 
 |  * | 
 |  * The P1275 standard for ISA devices seems to also have been | 
 |  * totally ignored. | 
 |  * | 
 |  * On later systems, an interrupt-map and interrupt-map-mask scheme | 
 |  * akin to EBUS is used. | 
 |  */ | 
 | static struct { | 
 | 	int	obp_irq; | 
 | 	int	pci_ino; | 
 | } grover_irq_table[] = { | 
 | 	{ 1, 0x00 },	/* dma, unknown ino at this point */ | 
 | 	{ 2, 0x27 },	/* floppy */ | 
 | 	{ 3, 0x22 },	/* parallel */ | 
 | 	{ 4, 0x2b },	/* serial */ | 
 | 	{ 5, 0x25 },	/* acpi power management */ | 
 |  | 
 | 	{ 0, 0x00 }	/* end of table */ | 
 | }; | 
 |  | 
 | static int __init isa_dev_get_irq_using_imap(struct sparc_isa_device *isa_dev, | 
 | 					     struct sparc_isa_bridge *isa_br, | 
 | 					     int *interrupt, | 
 | 					     struct linux_prom_registers *pregs) | 
 | { | 
 | 	unsigned int hi, lo, irq; | 
 | 	int i; | 
 |  | 
 | 	hi = pregs->which_io & isa_br->isa_intmask.phys_hi; | 
 | 	lo = pregs->phys_addr & isa_br->isa_intmask.phys_lo; | 
 | 	irq = *interrupt & isa_br->isa_intmask.interrupt; | 
 | 	for (i = 0; i < isa_br->num_isa_intmap; i++) { | 
 | 		if ((isa_br->isa_intmap[i].phys_hi == hi) && | 
 | 		    (isa_br->isa_intmap[i].phys_lo == lo) && | 
 | 		    (isa_br->isa_intmap[i].interrupt == irq)) { | 
 | 			*interrupt = isa_br->isa_intmap[i].cinterrupt; | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 | 	return -1; | 
 | } | 
 |  | 
 | static void __init isa_dev_get_irq(struct sparc_isa_device *isa_dev, | 
 | 				   struct linux_prom_registers *pregs) | 
 | { | 
 | 	int irq_prop; | 
 |  | 
 | 	irq_prop = prom_getintdefault(isa_dev->prom_node, | 
 | 				      "interrupts", -1); | 
 | 	if (irq_prop <= 0) { | 
 | 		goto no_irq; | 
 | 	} else { | 
 | 		struct pci_controller_info *pcic; | 
 | 		struct pci_pbm_info *pbm; | 
 | 		int i; | 
 |  | 
 | 		if (isa_dev->bus->num_isa_intmap) { | 
 | 			if (!isa_dev_get_irq_using_imap(isa_dev, | 
 | 							isa_dev->bus, | 
 | 							&irq_prop, | 
 | 							pregs)) | 
 | 				goto route_irq; | 
 | 		} | 
 |  | 
 | 		for (i = 0; grover_irq_table[i].obp_irq != 0; i++) { | 
 | 			if (grover_irq_table[i].obp_irq == irq_prop) { | 
 | 				int ino = grover_irq_table[i].pci_ino; | 
 |  | 
 | 				if (ino == 0) | 
 | 					goto no_irq; | 
 |   | 
 | 				irq_prop = ino; | 
 | 				goto route_irq; | 
 | 			} | 
 | 		} | 
 | 		goto no_irq; | 
 |  | 
 | route_irq: | 
 | 		pbm = isa_dev->bus->parent; | 
 | 		pcic = pbm->parent; | 
 | 		isa_dev->irq = pcic->irq_build(pbm, NULL, irq_prop); | 
 | 		return; | 
 | 	} | 
 |  | 
 | no_irq: | 
 | 	isa_dev->irq = PCI_IRQ_NONE; | 
 | } | 
 |  | 
 | static void __init isa_fill_children(struct sparc_isa_device *parent_isa_dev) | 
 | { | 
 | 	int node = prom_getchild(parent_isa_dev->prom_node); | 
 |  | 
 | 	if (node == 0) | 
 | 		return; | 
 |  | 
 | 	printk(" ->"); | 
 | 	while (node != 0) { | 
 | 		struct linux_prom_registers regs[PROMREG_MAX]; | 
 | 		struct sparc_isa_device *isa_dev; | 
 | 		int prop_len; | 
 |  | 
 | 		isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL); | 
 | 		if (!isa_dev) { | 
 | 			fatal_err("cannot allocate child isa_dev"); | 
 | 			prom_halt(); | 
 | 		} | 
 |  | 
 | 		memset(isa_dev, 0, sizeof(*isa_dev)); | 
 |  | 
 | 		/* Link it in to parent. */ | 
 | 		isa_dev->next = parent_isa_dev->child; | 
 | 		parent_isa_dev->child = isa_dev; | 
 |  | 
 | 		isa_dev->bus = parent_isa_dev->bus; | 
 | 		isa_dev->prom_node = node; | 
 | 		prop_len = prom_getproperty(node, "name", | 
 | 					    (char *) isa_dev->prom_name, | 
 | 					    sizeof(isa_dev->prom_name)); | 
 | 		if (prop_len <= 0) { | 
 | 			fatal_err("cannot get child isa_dev OBP node name"); | 
 | 			prom_halt(); | 
 | 		} | 
 |  | 
 | 		prop_len = prom_getproperty(node, "compatible", | 
 | 					    (char *) isa_dev->compatible, | 
 | 					    sizeof(isa_dev->compatible)); | 
 |  | 
 | 		/* Not having this is OK. */ | 
 | 		if (prop_len <= 0) | 
 | 			isa_dev->compatible[0] = '\0'; | 
 |  | 
 | 		isa_dev_get_resource(isa_dev, regs, sizeof(regs)); | 
 | 		isa_dev_get_irq(isa_dev, regs); | 
 |  | 
 | 		report_dev(isa_dev, 1); | 
 |  | 
 | 		node = prom_getsibling(node); | 
 | 	} | 
 | } | 
 |  | 
 | static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br) | 
 | { | 
 | 	int node = prom_getchild(isa_br->prom_node); | 
 |  | 
 | 	while (node != 0) { | 
 | 		struct linux_prom_registers regs[PROMREG_MAX]; | 
 | 		struct sparc_isa_device *isa_dev; | 
 | 		int prop_len; | 
 |  | 
 | 		isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL); | 
 | 		if (!isa_dev) { | 
 | 			fatal_err("cannot allocate isa_dev"); | 
 | 			prom_halt(); | 
 | 		} | 
 |  | 
 | 		memset(isa_dev, 0, sizeof(*isa_dev)); | 
 |  | 
 | 		/* Link it in. */ | 
 | 		isa_dev->next = NULL; | 
 | 		if (isa_br->devices == NULL) { | 
 | 			isa_br->devices = isa_dev; | 
 | 		} else { | 
 | 			struct sparc_isa_device *tmp = isa_br->devices; | 
 |  | 
 | 			while (tmp->next) | 
 | 				tmp = tmp->next; | 
 |  | 
 | 			tmp->next = isa_dev; | 
 | 		} | 
 |  | 
 | 		isa_dev->bus = isa_br; | 
 | 		isa_dev->prom_node = node; | 
 | 		prop_len = prom_getproperty(node, "name", | 
 | 					    (char *) isa_dev->prom_name, | 
 | 					    sizeof(isa_dev->prom_name)); | 
 | 		if (prop_len <= 0) { | 
 | 			fatal_err("cannot get isa_dev OBP node name"); | 
 | 			prom_halt(); | 
 | 		} | 
 |  | 
 | 		prop_len = prom_getproperty(node, "compatible", | 
 | 					    (char *) isa_dev->compatible, | 
 | 					    sizeof(isa_dev->compatible)); | 
 |  | 
 | 		/* Not having this is OK. */ | 
 | 		if (prop_len <= 0) | 
 | 			isa_dev->compatible[0] = '\0'; | 
 |  | 
 | 		isa_dev_get_resource(isa_dev, regs, sizeof(regs)); | 
 | 		isa_dev_get_irq(isa_dev, regs); | 
 |  | 
 | 		report_dev(isa_dev, 0); | 
 |  | 
 | 		isa_fill_children(isa_dev); | 
 |  | 
 | 		printk("]"); | 
 |  | 
 | 		node = prom_getsibling(node); | 
 | 	} | 
 | } | 
 |  | 
 | void __init isa_init(void) | 
 | { | 
 | 	struct pci_dev *pdev; | 
 | 	unsigned short vendor, device; | 
 | 	int index = 0; | 
 |  | 
 | 	vendor = PCI_VENDOR_ID_AL; | 
 | 	device = PCI_DEVICE_ID_AL_M1533; | 
 |  | 
 | 	pdev = NULL; | 
 | 	while ((pdev = pci_get_device(vendor, device, pdev)) != NULL) { | 
 | 		struct pcidev_cookie *pdev_cookie; | 
 | 		struct pci_pbm_info *pbm; | 
 | 		struct sparc_isa_bridge *isa_br; | 
 | 		int prop_len; | 
 |  | 
 | 		pdev_cookie = pdev->sysdata; | 
 | 		if (!pdev_cookie) { | 
 | 			printk("ISA: Warning, ISA bridge ignored due to " | 
 | 			       "lack of OBP data.\n"); | 
 | 			continue; | 
 | 		} | 
 | 		pbm = pdev_cookie->pbm; | 
 |  | 
 | 		isa_br = kmalloc(sizeof(*isa_br), GFP_KERNEL); | 
 | 		if (!isa_br) { | 
 | 			fatal_err("cannot allocate sparc_isa_bridge"); | 
 | 			prom_halt(); | 
 | 		} | 
 |  | 
 | 		memset(isa_br, 0, sizeof(*isa_br)); | 
 |  | 
 | 		/* Link it in. */ | 
 | 		isa_br->next = isa_chain; | 
 | 		isa_chain = isa_br; | 
 |  | 
 | 		isa_br->parent = pbm; | 
 | 		isa_br->self = pdev; | 
 | 		isa_br->index = index++; | 
 | 		isa_br->prom_node = pdev_cookie->prom_node; | 
 | 		strncpy(isa_br->prom_name, pdev_cookie->prom_name, | 
 | 			sizeof(isa_br->prom_name)); | 
 |  | 
 | 		prop_len = prom_getproperty(isa_br->prom_node, | 
 | 					    "ranges", | 
 | 					    (char *) isa_br->isa_ranges, | 
 | 					    sizeof(isa_br->isa_ranges)); | 
 | 		if (prop_len <= 0) | 
 | 			isa_br->num_isa_ranges = 0; | 
 | 		else | 
 | 			isa_br->num_isa_ranges = | 
 | 				(prop_len / sizeof(struct linux_prom_isa_ranges)); | 
 |  | 
 | 		prop_len = prom_getproperty(isa_br->prom_node, | 
 | 					    "interrupt-map", | 
 | 					    (char *) isa_br->isa_intmap, | 
 | 					    sizeof(isa_br->isa_intmap)); | 
 | 		if (prop_len <= 0) | 
 | 			isa_br->num_isa_intmap = 0; | 
 | 		else | 
 | 			isa_br->num_isa_intmap = | 
 | 				(prop_len / sizeof(struct linux_prom_isa_intmap)); | 
 |  | 
 | 		prop_len = prom_getproperty(isa_br->prom_node, | 
 | 					    "interrupt-map-mask", | 
 | 					    (char *) &(isa_br->isa_intmask), | 
 | 					    sizeof(isa_br->isa_intmask)); | 
 |  | 
 | 		printk("isa%d:", isa_br->index); | 
 |  | 
 | 		isa_fill_devices(isa_br); | 
 |  | 
 | 		printk("\n"); | 
 | 	} | 
 | } |