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