xref: /openbmc/linux/arch/powerpc/kernel/isa-bridge.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23d5134eeSBenjamin Herrenschmidt /*
33d5134eeSBenjamin Herrenschmidt  * Routines for tracking a legacy ISA bridge
43d5134eeSBenjamin Herrenschmidt  *
53d5134eeSBenjamin Herrenschmidt  * Copyrigh 2007 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
63d5134eeSBenjamin Herrenschmidt  *
73d5134eeSBenjamin Herrenschmidt  * Some bits and pieces moved over from pci_64.c
83d5134eeSBenjamin Herrenschmidt  *
93d5134eeSBenjamin Herrenschmidt  * Copyrigh 2003 Anton Blanchard <anton@au.ibm.com>, IBM Corp.
103d5134eeSBenjamin Herrenschmidt  */
113d5134eeSBenjamin Herrenschmidt 
123d5134eeSBenjamin Herrenschmidt #define DEBUG
133d5134eeSBenjamin Herrenschmidt 
143d5134eeSBenjamin Herrenschmidt #include <linux/kernel.h>
153d5134eeSBenjamin Herrenschmidt #include <linux/pci.h>
163d5134eeSBenjamin Herrenschmidt #include <linux/string.h>
1766b15db6SPaul Gortmaker #include <linux/export.h>
183d5134eeSBenjamin Herrenschmidt #include <linux/init.h>
193d5134eeSBenjamin Herrenschmidt #include <linux/mm.h>
203d5134eeSBenjamin Herrenschmidt #include <linux/notifier.h>
21e6f6390aSChristophe Leroy #include <linux/of_address.h>
2291f03f29SChristoph Hellwig #include <linux/vmalloc.h>
233d5134eeSBenjamin Herrenschmidt 
243d5134eeSBenjamin Herrenschmidt #include <asm/processor.h>
253d5134eeSBenjamin Herrenschmidt #include <asm/io.h>
263d5134eeSBenjamin Herrenschmidt #include <asm/pci-bridge.h>
273d5134eeSBenjamin Herrenschmidt #include <asm/machdep.h>
283d5134eeSBenjamin Herrenschmidt #include <asm/ppc-pci.h>
2938e9d36bSBenjamin Herrenschmidt #include <asm/isa-bridge.h>
303d5134eeSBenjamin Herrenschmidt 
313d5134eeSBenjamin Herrenschmidt unsigned long isa_io_base;	/* NULL if no ISA bus */
323d5134eeSBenjamin Herrenschmidt EXPORT_SYMBOL(isa_io_base);
333d5134eeSBenjamin Herrenschmidt 
343d5134eeSBenjamin Herrenschmidt /* Cached ISA bridge dev. */
353d5134eeSBenjamin Herrenschmidt static struct device_node *isa_bridge_devnode;
363d5134eeSBenjamin Herrenschmidt struct pci_dev *isa_bridge_pcidev;
373d5134eeSBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(isa_bridge_pcidev);
383d5134eeSBenjamin Herrenschmidt 
393d5134eeSBenjamin Herrenschmidt #define ISA_SPACE_MASK 0x1
403d5134eeSBenjamin Herrenschmidt #define ISA_SPACE_IO 0x1
413d5134eeSBenjamin Herrenschmidt 
remap_isa_base(phys_addr_t pa,unsigned long size)4291f03f29SChristoph Hellwig static void remap_isa_base(phys_addr_t pa, unsigned long size)
4391f03f29SChristoph Hellwig {
4491f03f29SChristoph Hellwig 	WARN_ON_ONCE(ISA_IO_BASE & ~PAGE_MASK);
4591f03f29SChristoph Hellwig 	WARN_ON_ONCE(pa & ~PAGE_MASK);
4691f03f29SChristoph Hellwig 	WARN_ON_ONCE(size & ~PAGE_MASK);
4791f03f29SChristoph Hellwig 
4891f03f29SChristoph Hellwig 	if (slab_is_available()) {
4991f03f29SChristoph Hellwig 		if (ioremap_page_range(ISA_IO_BASE, ISA_IO_BASE + size, pa,
5091f03f29SChristoph Hellwig 				pgprot_noncached(PAGE_KERNEL)))
514ad0ae8cSNicholas Piggin 			vunmap_range(ISA_IO_BASE, ISA_IO_BASE + size);
5291f03f29SChristoph Hellwig 	} else {
5391f03f29SChristoph Hellwig 		early_ioremap_range(ISA_IO_BASE, pa, size,
5491f03f29SChristoph Hellwig 				pgprot_noncached(PAGE_KERNEL));
5591f03f29SChristoph Hellwig 	}
5691f03f29SChristoph Hellwig }
5791f03f29SChristoph Hellwig 
process_ISA_OF_ranges(struct device_node * isa_node,unsigned long phb_io_base_phys)58e4ab08beSRob Herring static int process_ISA_OF_ranges(struct device_node *isa_node,
593d5134eeSBenjamin Herrenschmidt 				 unsigned long phb_io_base_phys)
603d5134eeSBenjamin Herrenschmidt {
613d5134eeSBenjamin Herrenschmidt 	unsigned int size;
62e4ab08beSRob Herring 	struct of_range_parser parser;
63e4ab08beSRob Herring 	struct of_range range;
643d5134eeSBenjamin Herrenschmidt 
65e4ab08beSRob Herring 	if (of_range_parser_init(&parser, isa_node))
663d5134eeSBenjamin Herrenschmidt 		goto inval_range;
673d5134eeSBenjamin Herrenschmidt 
68e4ab08beSRob Herring 	for_each_of_range(&parser, &range) {
69e4ab08beSRob Herring 		if ((range.flags & ISA_SPACE_MASK) != ISA_SPACE_IO)
70e4ab08beSRob Herring 			continue;
71e4ab08beSRob Herring 
72e4ab08beSRob Herring 		if (range.cpu_addr == OF_BAD_ADDR) {
73e4ab08beSRob Herring 			pr_err("ISA: Bad CPU mapping: %s\n", __func__);
74e4ab08beSRob Herring 			return -EINVAL;
753d5134eeSBenjamin Herrenschmidt 		}
763d5134eeSBenjamin Herrenschmidt 
77e4ab08beSRob Herring 		/* We need page alignment */
78e4ab08beSRob Herring 		if ((range.bus_addr & ~PAGE_MASK) || (range.cpu_addr & ~PAGE_MASK)) {
79e4ab08beSRob Herring 			pr_warn("ISA: bridge %pOF has non aligned IO range\n", isa_node);
80e4ab08beSRob Herring 			return -EINVAL;
813d5134eeSBenjamin Herrenschmidt 		}
823d5134eeSBenjamin Herrenschmidt 
833d5134eeSBenjamin Herrenschmidt 		/* Align size and make sure it's cropped to 64K */
84e4ab08beSRob Herring 		size = PAGE_ALIGN(range.size);
853d5134eeSBenjamin Herrenschmidt 		if (size > 0x10000)
863d5134eeSBenjamin Herrenschmidt 			size = 0x10000;
873d5134eeSBenjamin Herrenschmidt 
88e4ab08beSRob Herring 		if (!phb_io_base_phys)
89e4ab08beSRob Herring 			phb_io_base_phys = range.cpu_addr;
90e4ab08beSRob Herring 
9191f03f29SChristoph Hellwig 		remap_isa_base(phb_io_base_phys, size);
92e4ab08beSRob Herring 		return 0;
93e4ab08beSRob Herring 	}
943d5134eeSBenjamin Herrenschmidt 
953d5134eeSBenjamin Herrenschmidt inval_range:
96*79de3604SRob Herring 	if (phb_io_base_phys) {
97e4ab08beSRob Herring 		pr_err("no ISA IO ranges or unexpected isa range, mapping 64k\n");
9891f03f29SChristoph Hellwig 		remap_isa_base(phb_io_base_phys, 0x10000);
99e4ab08beSRob Herring 		return 0;
100e4ab08beSRob Herring 	}
101*79de3604SRob Herring 	return -EINVAL;
102*79de3604SRob Herring }
1033d5134eeSBenjamin Herrenschmidt 
1043d5134eeSBenjamin Herrenschmidt 
1053d5134eeSBenjamin Herrenschmidt /**
1063d5134eeSBenjamin Herrenschmidt  * isa_bridge_find_early - Find and map the ISA IO space early before
1073d5134eeSBenjamin Herrenschmidt  *                         main PCI discovery. This is optionally called by
1083d5134eeSBenjamin Herrenschmidt  *                         the arch code when adding PCI PHBs to get early
1093d5134eeSBenjamin Herrenschmidt  *                         access to ISA IO ports
1103d5134eeSBenjamin Herrenschmidt  */
isa_bridge_find_early(struct pci_controller * hose)1113d5134eeSBenjamin Herrenschmidt void __init isa_bridge_find_early(struct pci_controller *hose)
1123d5134eeSBenjamin Herrenschmidt {
1133d5134eeSBenjamin Herrenschmidt 	struct device_node *np, *parent = NULL, *tmp;
1143d5134eeSBenjamin Herrenschmidt 
1153d5134eeSBenjamin Herrenschmidt 	/* If we already have an ISA bridge, bail off */
1163d5134eeSBenjamin Herrenschmidt 	if (isa_bridge_devnode != NULL)
1173d5134eeSBenjamin Herrenschmidt 		return;
1183d5134eeSBenjamin Herrenschmidt 
1193d5134eeSBenjamin Herrenschmidt 	/* For each "isa" node in the system. Note : we do a search by
1203d5134eeSBenjamin Herrenschmidt 	 * type and not by name. It might be better to do by name but that's
1213d5134eeSBenjamin Herrenschmidt 	 * what the code used to do and I don't want to break too much at
1223d5134eeSBenjamin Herrenschmidt 	 * once. We can look into changing that separately
1233d5134eeSBenjamin Herrenschmidt 	 */
1243d5134eeSBenjamin Herrenschmidt 	for_each_node_by_type(np, "isa") {
1253d5134eeSBenjamin Herrenschmidt 		/* Look for our hose being a parent */
1263d5134eeSBenjamin Herrenschmidt 		for (parent = of_get_parent(np); parent;) {
12744ef3390SStephen Rothwell 			if (parent == hose->dn) {
1283d5134eeSBenjamin Herrenschmidt 				of_node_put(parent);
1293d5134eeSBenjamin Herrenschmidt 				break;
1303d5134eeSBenjamin Herrenschmidt 			}
1313d5134eeSBenjamin Herrenschmidt 			tmp = parent;
1323d5134eeSBenjamin Herrenschmidt 			parent = of_get_parent(parent);
1333d5134eeSBenjamin Herrenschmidt 			of_node_put(tmp);
1343d5134eeSBenjamin Herrenschmidt 		}
1353d5134eeSBenjamin Herrenschmidt 		if (parent != NULL)
1363d5134eeSBenjamin Herrenschmidt 			break;
1373d5134eeSBenjamin Herrenschmidt 	}
1383d5134eeSBenjamin Herrenschmidt 	if (np == NULL)
1393d5134eeSBenjamin Herrenschmidt 		return;
1403d5134eeSBenjamin Herrenschmidt 	isa_bridge_devnode = np;
1413d5134eeSBenjamin Herrenschmidt 
1423d5134eeSBenjamin Herrenschmidt 	/* Now parse the "ranges" property and setup the ISA mapping */
143e4ab08beSRob Herring 	process_ISA_OF_ranges(np, hose->io_base_phys);
1443d5134eeSBenjamin Herrenschmidt 
1453d5134eeSBenjamin Herrenschmidt 	/* Set the global ISA io base to indicate we have an ISA bridge */
1463d5134eeSBenjamin Herrenschmidt 	isa_io_base = ISA_IO_BASE;
1473d5134eeSBenjamin Herrenschmidt 
148b7c670d6SRob Herring 	pr_debug("ISA bridge (early) is %pOF\n", np);
1493d5134eeSBenjamin Herrenschmidt }
1503d5134eeSBenjamin Herrenschmidt 
1513d5134eeSBenjamin Herrenschmidt /**
152b3c711a9SBenjamin Herrenschmidt  * isa_bridge_find_early - Find and map the ISA IO space early before
153b3c711a9SBenjamin Herrenschmidt  *                         main PCI discovery. This is optionally called by
154b3c711a9SBenjamin Herrenschmidt  *                         the arch code when adding PCI PHBs to get early
155b3c711a9SBenjamin Herrenschmidt  *                         access to ISA IO ports
156b3c711a9SBenjamin Herrenschmidt  */
isa_bridge_init_non_pci(struct device_node * np)157b3c711a9SBenjamin Herrenschmidt void __init isa_bridge_init_non_pci(struct device_node *np)
158b3c711a9SBenjamin Herrenschmidt {
159e4ab08beSRob Herring 	int ret;
160b3c711a9SBenjamin Herrenschmidt 
161b3c711a9SBenjamin Herrenschmidt 	/* If we already have an ISA bridge, bail off */
162b3c711a9SBenjamin Herrenschmidt 	if (isa_bridge_devnode != NULL)
163b3c711a9SBenjamin Herrenschmidt 		return;
164b3c711a9SBenjamin Herrenschmidt 
165e4ab08beSRob Herring 	ret = process_ISA_OF_ranges(np, 0);
166e4ab08beSRob Herring 	if (ret)
167b3c711a9SBenjamin Herrenschmidt 		return;
168b3c711a9SBenjamin Herrenschmidt 
169b3c711a9SBenjamin Herrenschmidt 	/* Got it */
170b3c711a9SBenjamin Herrenschmidt 	isa_bridge_devnode = np;
171b3c711a9SBenjamin Herrenschmidt 
172b3c711a9SBenjamin Herrenschmidt 	/* Set the global ISA io base to indicate we have an ISA bridge
173b3c711a9SBenjamin Herrenschmidt 	 * and map it
174b3c711a9SBenjamin Herrenschmidt 	 */
175b3c711a9SBenjamin Herrenschmidt 	isa_io_base = ISA_IO_BASE;
176b3c711a9SBenjamin Herrenschmidt 
177b7c670d6SRob Herring 	pr_debug("ISA: Non-PCI bridge is %pOF\n", np);
178b3c711a9SBenjamin Herrenschmidt }
179b3c711a9SBenjamin Herrenschmidt 
180b3c711a9SBenjamin Herrenschmidt /**
1813d5134eeSBenjamin Herrenschmidt  * isa_bridge_find_late - Find and map the ISA IO space upon discovery of
1823d5134eeSBenjamin Herrenschmidt  *                        a new ISA bridge
1833d5134eeSBenjamin Herrenschmidt  */
isa_bridge_find_late(struct pci_dev * pdev,struct device_node * devnode)184cad5cef6SGreg Kroah-Hartman static void isa_bridge_find_late(struct pci_dev *pdev,
1853d5134eeSBenjamin Herrenschmidt 				 struct device_node *devnode)
1863d5134eeSBenjamin Herrenschmidt {
1873d5134eeSBenjamin Herrenschmidt 	struct pci_controller *hose = pci_bus_to_host(pdev->bus);
1883d5134eeSBenjamin Herrenschmidt 
1893d5134eeSBenjamin Herrenschmidt 	/* Store ISA device node and PCI device */
1903d5134eeSBenjamin Herrenschmidt 	isa_bridge_devnode = of_node_get(devnode);
1913d5134eeSBenjamin Herrenschmidt 	isa_bridge_pcidev = pdev;
1923d5134eeSBenjamin Herrenschmidt 
1933d5134eeSBenjamin Herrenschmidt 	/* Now parse the "ranges" property and setup the ISA mapping */
194e4ab08beSRob Herring 	process_ISA_OF_ranges(devnode, hose->io_base_phys);
1953d5134eeSBenjamin Herrenschmidt 
1963d5134eeSBenjamin Herrenschmidt 	/* Set the global ISA io base to indicate we have an ISA bridge */
1973d5134eeSBenjamin Herrenschmidt 	isa_io_base = ISA_IO_BASE;
1983d5134eeSBenjamin Herrenschmidt 
199b7c670d6SRob Herring 	pr_debug("ISA bridge (late) is %pOF on %s\n",
200b7c670d6SRob Herring 		 devnode, pci_name(pdev));
2013d5134eeSBenjamin Herrenschmidt }
2023d5134eeSBenjamin Herrenschmidt 
2033d5134eeSBenjamin Herrenschmidt /**
2043d5134eeSBenjamin Herrenschmidt  * isa_bridge_remove - Remove/unmap an ISA bridge
2053d5134eeSBenjamin Herrenschmidt  */
isa_bridge_remove(void)2063d5134eeSBenjamin Herrenschmidt static void isa_bridge_remove(void)
2073d5134eeSBenjamin Herrenschmidt {
2083d5134eeSBenjamin Herrenschmidt 	pr_debug("ISA bridge removed !\n");
2093d5134eeSBenjamin Herrenschmidt 
2103d5134eeSBenjamin Herrenschmidt 	/* Clear the global ISA io base to indicate that we have no more
2113d5134eeSBenjamin Herrenschmidt 	 * ISA bridge. Note that drivers don't quite handle that, though
2123d5134eeSBenjamin Herrenschmidt 	 * we should probably do something about it. But do we ever really
2133d5134eeSBenjamin Herrenschmidt 	 * have ISA bridges being removed on machines using legacy devices ?
2143d5134eeSBenjamin Herrenschmidt 	 */
2153d5134eeSBenjamin Herrenschmidt 	isa_io_base = ISA_IO_BASE;
2163d5134eeSBenjamin Herrenschmidt 
2173d5134eeSBenjamin Herrenschmidt 	/* Clear references to the bridge */
2183d5134eeSBenjamin Herrenschmidt 	of_node_put(isa_bridge_devnode);
2193d5134eeSBenjamin Herrenschmidt 	isa_bridge_devnode = NULL;
2203d5134eeSBenjamin Herrenschmidt 	isa_bridge_pcidev = NULL;
2213d5134eeSBenjamin Herrenschmidt 
2223d5134eeSBenjamin Herrenschmidt 	/* Unmap the ISA area */
2234ad0ae8cSNicholas Piggin 	vunmap_range(ISA_IO_BASE, ISA_IO_BASE + 0x10000);
2243d5134eeSBenjamin Herrenschmidt }
2253d5134eeSBenjamin Herrenschmidt 
2263d5134eeSBenjamin Herrenschmidt /**
2273d5134eeSBenjamin Herrenschmidt  * isa_bridge_notify - Get notified of PCI devices addition/removal
2283d5134eeSBenjamin Herrenschmidt  */
isa_bridge_notify(struct notifier_block * nb,unsigned long action,void * data)229cad5cef6SGreg Kroah-Hartman static int isa_bridge_notify(struct notifier_block *nb, unsigned long action,
230cad5cef6SGreg Kroah-Hartman 			     void *data)
2313d5134eeSBenjamin Herrenschmidt {
2323d5134eeSBenjamin Herrenschmidt 	struct device *dev = data;
2333d5134eeSBenjamin Herrenschmidt 	struct pci_dev *pdev = to_pci_dev(dev);
2343d5134eeSBenjamin Herrenschmidt 	struct device_node *devnode = pci_device_to_OF_node(pdev);
2353d5134eeSBenjamin Herrenschmidt 
2363d5134eeSBenjamin Herrenschmidt 	switch(action) {
2373d5134eeSBenjamin Herrenschmidt 	case BUS_NOTIFY_ADD_DEVICE:
2383d5134eeSBenjamin Herrenschmidt 		/* Check if we have an early ISA device, without PCI dev */
2393d5134eeSBenjamin Herrenschmidt 		if (isa_bridge_devnode && isa_bridge_devnode == devnode &&
2403d5134eeSBenjamin Herrenschmidt 		    !isa_bridge_pcidev) {
2413d5134eeSBenjamin Herrenschmidt 			pr_debug("ISA bridge PCI attached: %s\n",
2423d5134eeSBenjamin Herrenschmidt 				 pci_name(pdev));
2433d5134eeSBenjamin Herrenschmidt 			isa_bridge_pcidev = pdev;
2443d5134eeSBenjamin Herrenschmidt 		}
2453d5134eeSBenjamin Herrenschmidt 
2463d5134eeSBenjamin Herrenschmidt 		/* Check if we have no ISA device, and this happens to be one,
2473d5134eeSBenjamin Herrenschmidt 		 * register it as such if it has an OF device
2483d5134eeSBenjamin Herrenschmidt 		 */
249e5480bdcSRob Herring 		if (!isa_bridge_devnode && of_node_is_type(devnode, "isa"))
2503d5134eeSBenjamin Herrenschmidt 			isa_bridge_find_late(pdev, devnode);
2513d5134eeSBenjamin Herrenschmidt 
2523d5134eeSBenjamin Herrenschmidt 		return 0;
2533d5134eeSBenjamin Herrenschmidt 	case BUS_NOTIFY_DEL_DEVICE:
2543d5134eeSBenjamin Herrenschmidt 		/* Check if this our existing ISA device */
2553d5134eeSBenjamin Herrenschmidt 		if (pdev == isa_bridge_pcidev ||
2563d5134eeSBenjamin Herrenschmidt 		    (devnode && devnode == isa_bridge_devnode))
2573d5134eeSBenjamin Herrenschmidt 			isa_bridge_remove();
2583d5134eeSBenjamin Herrenschmidt 		return 0;
2593d5134eeSBenjamin Herrenschmidt 	}
2603d5134eeSBenjamin Herrenschmidt 	return 0;
2613d5134eeSBenjamin Herrenschmidt }
2623d5134eeSBenjamin Herrenschmidt 
2633d5134eeSBenjamin Herrenschmidt static struct notifier_block isa_bridge_notifier = {
2643d5134eeSBenjamin Herrenschmidt 	.notifier_call = isa_bridge_notify
2653d5134eeSBenjamin Herrenschmidt };
2663d5134eeSBenjamin Herrenschmidt 
2673d5134eeSBenjamin Herrenschmidt /**
2683d5134eeSBenjamin Herrenschmidt  * isa_bridge_init - register to be notified of ISA bridge addition/removal
2693d5134eeSBenjamin Herrenschmidt  *
2703d5134eeSBenjamin Herrenschmidt  */
isa_bridge_init(void)2713d5134eeSBenjamin Herrenschmidt static int __init isa_bridge_init(void)
2723d5134eeSBenjamin Herrenschmidt {
2733d5134eeSBenjamin Herrenschmidt 	bus_register_notifier(&pci_bus_type, &isa_bridge_notifier);
2743d5134eeSBenjamin Herrenschmidt 	return 0;
2753d5134eeSBenjamin Herrenschmidt }
2763d5134eeSBenjamin Herrenschmidt arch_initcall(isa_bridge_init);
277