xref: /openbmc/linux/arch/ia64/pci/pci.c (revision 05933aac)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * pci.c - Low-Level PCI Access in IA-64
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Derived from bios32.c of i386 tree.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * (c) Copyright 2002, 2005 Hewlett-Packard Development Company, L.P.
81da177e4SLinus Torvalds  *	David Mosberger-Tang <davidm@hpl.hp.com>
91da177e4SLinus Torvalds  *	Bjorn Helgaas <bjorn.helgaas@hp.com>
101da177e4SLinus Torvalds  * Copyright (C) 2004 Silicon Graphics, Inc.
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * Note: Above list of copyright holders is incomplete...
131da177e4SLinus Torvalds  */
141da177e4SLinus Torvalds 
151da177e4SLinus Torvalds #include <linux/acpi.h>
161da177e4SLinus Torvalds #include <linux/types.h>
171da177e4SLinus Torvalds #include <linux/kernel.h>
181da177e4SLinus Torvalds #include <linux/pci.h>
19b02a4a19SJiang Liu #include <linux/pci-acpi.h>
201da177e4SLinus Torvalds #include <linux/init.h>
211da177e4SLinus Torvalds #include <linux/ioport.h>
221da177e4SLinus Torvalds #include <linux/slab.h>
231da177e4SLinus Torvalds #include <linux/spinlock.h>
2457c8a661SMike Rapoport #include <linux/memblock.h>
25bd3ff194SPaul Gortmaker #include <linux/export.h>
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds #include <asm/machvec.h>
281da177e4SLinus Torvalds #include <asm/page.h>
291da177e4SLinus Torvalds #include <asm/io.h>
301da177e4SLinus Torvalds #include <asm/sal.h>
311da177e4SLinus Torvalds #include <asm/smp.h>
321da177e4SLinus Torvalds #include <asm/irq.h>
331da177e4SLinus Torvalds #include <asm/hw_irq.h>
341da177e4SLinus Torvalds 
351da177e4SLinus Torvalds /*
361da177e4SLinus Torvalds  * Low-level SAL-based PCI configuration access functions. Note that SAL
371da177e4SLinus Torvalds  * calls are already serialized (via sal_lock), so we don't need another
381da177e4SLinus Torvalds  * synchronization mechanism here.
391da177e4SLinus Torvalds  */
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds #define PCI_SAL_ADDRESS(seg, bus, devfn, reg)		\
421da177e4SLinus Torvalds 	(((u64) seg << 24) | (bus << 16) | (devfn << 8) | (reg))
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds /* SAL 3.2 adds support for extended config space. */
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds #define PCI_SAL_EXT_ADDRESS(seg, bus, devfn, reg)	\
471da177e4SLinus Torvalds 	(((u64) seg << 28) | (bus << 20) | (devfn << 12) | (reg))
481da177e4SLinus Torvalds 
49b6ce068aSMatthew Wilcox int raw_pci_read(unsigned int seg, unsigned int bus, unsigned int devfn,
501da177e4SLinus Torvalds 	      int reg, int len, u32 *value)
511da177e4SLinus Torvalds {
521da177e4SLinus Torvalds 	u64 addr, data = 0;
531da177e4SLinus Torvalds 	int mode, result;
541da177e4SLinus Torvalds 
551da177e4SLinus Torvalds 	if (!value || (seg > 65535) || (bus > 255) || (devfn > 255) || (reg > 4095))
561da177e4SLinus Torvalds 		return -EINVAL;
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds 	if ((seg | reg) <= 255) {
591da177e4SLinus Torvalds 		addr = PCI_SAL_ADDRESS(seg, bus, devfn, reg);
601da177e4SLinus Torvalds 		mode = 0;
61adcd7403SMatthew Wilcox 	} else if (sal_revision >= SAL_VERSION_CODE(3,2)) {
621da177e4SLinus Torvalds 		addr = PCI_SAL_EXT_ADDRESS(seg, bus, devfn, reg);
631da177e4SLinus Torvalds 		mode = 1;
64adcd7403SMatthew Wilcox 	} else {
65adcd7403SMatthew Wilcox 		return -EINVAL;
661da177e4SLinus Torvalds 	}
67adcd7403SMatthew Wilcox 
681da177e4SLinus Torvalds 	result = ia64_sal_pci_config_read(addr, mode, len, &data);
691da177e4SLinus Torvalds 	if (result != 0)
701da177e4SLinus Torvalds 		return -EINVAL;
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds 	*value = (u32) data;
731da177e4SLinus Torvalds 	return 0;
741da177e4SLinus Torvalds }
751da177e4SLinus Torvalds 
76b6ce068aSMatthew Wilcox int raw_pci_write(unsigned int seg, unsigned int bus, unsigned int devfn,
771da177e4SLinus Torvalds 	       int reg, int len, u32 value)
781da177e4SLinus Torvalds {
791da177e4SLinus Torvalds 	u64 addr;
801da177e4SLinus Torvalds 	int mode, result;
811da177e4SLinus Torvalds 
821da177e4SLinus Torvalds 	if ((seg > 65535) || (bus > 255) || (devfn > 255) || (reg > 4095))
831da177e4SLinus Torvalds 		return -EINVAL;
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds 	if ((seg | reg) <= 255) {
861da177e4SLinus Torvalds 		addr = PCI_SAL_ADDRESS(seg, bus, devfn, reg);
871da177e4SLinus Torvalds 		mode = 0;
88adcd7403SMatthew Wilcox 	} else if (sal_revision >= SAL_VERSION_CODE(3,2)) {
891da177e4SLinus Torvalds 		addr = PCI_SAL_EXT_ADDRESS(seg, bus, devfn, reg);
901da177e4SLinus Torvalds 		mode = 1;
91adcd7403SMatthew Wilcox 	} else {
92adcd7403SMatthew Wilcox 		return -EINVAL;
931da177e4SLinus Torvalds 	}
941da177e4SLinus Torvalds 	result = ia64_sal_pci_config_write(addr, mode, len, value);
951da177e4SLinus Torvalds 	if (result != 0)
961da177e4SLinus Torvalds 		return -EINVAL;
971da177e4SLinus Torvalds 	return 0;
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds 
100b6ce068aSMatthew Wilcox static int pci_read(struct pci_bus *bus, unsigned int devfn, int where,
101b6ce068aSMatthew Wilcox 							int size, u32 *value)
1021da177e4SLinus Torvalds {
103b6ce068aSMatthew Wilcox 	return raw_pci_read(pci_domain_nr(bus), bus->number,
1041da177e4SLinus Torvalds 				 devfn, where, size, value);
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds 
107b6ce068aSMatthew Wilcox static int pci_write(struct pci_bus *bus, unsigned int devfn, int where,
108b6ce068aSMatthew Wilcox 							int size, u32 value)
1091da177e4SLinus Torvalds {
110b6ce068aSMatthew Wilcox 	return raw_pci_write(pci_domain_nr(bus), bus->number,
1111da177e4SLinus Torvalds 				  devfn, where, size, value);
1121da177e4SLinus Torvalds }
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds struct pci_ops pci_root_ops = {
1151da177e4SLinus Torvalds 	.read = pci_read,
1161da177e4SLinus Torvalds 	.write = pci_write,
1171da177e4SLinus Torvalds };
1181da177e4SLinus Torvalds 
1194f41d5a4SBjorn Helgaas struct pci_root_info {
12002715e86SJiang Liu 	struct acpi_pci_root_info common;
1213772aea7SJiang Liu 	struct pci_controller controller;
122c9e391cfSJiang Liu 	struct list_head io_resources;
1234f41d5a4SBjorn Helgaas };
1244f41d5a4SBjorn Helgaas 
12502715e86SJiang Liu static unsigned int new_space(u64 phys_base, int sparse)
1261da177e4SLinus Torvalds {
1274f41d5a4SBjorn Helgaas 	u64 mmio_base;
1281da177e4SLinus Torvalds 	int i;
1291da177e4SLinus Torvalds 
1304f41d5a4SBjorn Helgaas 	if (phys_base == 0)
1314f41d5a4SBjorn Helgaas 		return 0;	/* legacy I/O port space */
1321da177e4SLinus Torvalds 
1334f41d5a4SBjorn Helgaas 	mmio_base = (u64) ioremap(phys_base, 0);
1341da177e4SLinus Torvalds 	for (i = 0; i < num_io_spaces; i++)
1354f41d5a4SBjorn Helgaas 		if (io_space[i].mmio_base == mmio_base &&
1361da177e4SLinus Torvalds 		    io_space[i].sparse == sparse)
1374f41d5a4SBjorn Helgaas 			return i;
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds 	if (num_io_spaces == MAX_IO_SPACES) {
140c4cbf6b9SYijing Wang 		pr_err("PCI: Too many IO port spaces "
1414f41d5a4SBjorn Helgaas 			"(MAX_IO_SPACES=%lu)\n", MAX_IO_SPACES);
1421da177e4SLinus Torvalds 		return ~0;
1431da177e4SLinus Torvalds 	}
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 	i = num_io_spaces++;
1464f41d5a4SBjorn Helgaas 	io_space[i].mmio_base = mmio_base;
1471da177e4SLinus Torvalds 	io_space[i].sparse = sparse;
1481da177e4SLinus Torvalds 
1494f41d5a4SBjorn Helgaas 	return i;
1504f41d5a4SBjorn Helgaas }
1514f41d5a4SBjorn Helgaas 
1523772aea7SJiang Liu static int add_io_space(struct device *dev, struct pci_root_info *info,
1533772aea7SJiang Liu 			struct resource_entry *entry)
1544f41d5a4SBjorn Helgaas {
1553f7abdefSJiang Liu 	struct resource_entry *iospace;
1563772aea7SJiang Liu 	struct resource *resource, *res = entry->res;
1574f41d5a4SBjorn Helgaas 	char *name;
158e088a4adSMatthew Wilcox 	unsigned long base, min, max, base_port;
1594f41d5a4SBjorn Helgaas 	unsigned int sparse = 0, space_nr, len;
1604f41d5a4SBjorn Helgaas 
16102715e86SJiang Liu 	len = strlen(info->common.name) + 32;
1623f7abdefSJiang Liu 	iospace = resource_list_create_entry(NULL, len);
163c9e391cfSJiang Liu 	if (!iospace) {
1643772aea7SJiang Liu 		dev_err(dev, "PCI: No memory for %s I/O port space\n",
16502715e86SJiang Liu 			info->common.name);
1663772aea7SJiang Liu 		return -ENOMEM;
1674f41d5a4SBjorn Helgaas 	}
1684f41d5a4SBjorn Helgaas 
1693772aea7SJiang Liu 	if (res->flags & IORESOURCE_IO_SPARSE)
1704f41d5a4SBjorn Helgaas 		sparse = 1;
1713772aea7SJiang Liu 	space_nr = new_space(entry->offset, sparse);
1724f41d5a4SBjorn Helgaas 	if (space_nr == ~0)
173c9e391cfSJiang Liu 		goto free_resource;
1744f41d5a4SBjorn Helgaas 
1753772aea7SJiang Liu 	name = (char *)(iospace + 1);
1763772aea7SJiang Liu 	min = res->start - entry->offset;
1773772aea7SJiang Liu 	max = res->end - entry->offset;
1784f41d5a4SBjorn Helgaas 	base = __pa(io_space[space_nr].mmio_base);
1794f41d5a4SBjorn Helgaas 	base_port = IO_SPACE_BASE(space_nr);
18002715e86SJiang Liu 	snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->common.name,
1814f41d5a4SBjorn Helgaas 		 base_port + min, base_port + max);
1824f41d5a4SBjorn Helgaas 
1834f41d5a4SBjorn Helgaas 	/*
1844f41d5a4SBjorn Helgaas 	 * The SDM guarantees the legacy 0-64K space is sparse, but if the
1854f41d5a4SBjorn Helgaas 	 * mapping is done by the processor (not the bridge), ACPI may not
1864f41d5a4SBjorn Helgaas 	 * mark it as sparse.
1874f41d5a4SBjorn Helgaas 	 */
1884f41d5a4SBjorn Helgaas 	if (space_nr == 0)
1894f41d5a4SBjorn Helgaas 		sparse = 1;
1904f41d5a4SBjorn Helgaas 
1913f7abdefSJiang Liu 	resource = iospace->res;
1924f41d5a4SBjorn Helgaas 	resource->name  = name;
1934f41d5a4SBjorn Helgaas 	resource->flags = IORESOURCE_MEM;
1944f41d5a4SBjorn Helgaas 	resource->start = base + (sparse ? IO_SPACE_SPARSE_ENCODING(min) : min);
1954f41d5a4SBjorn Helgaas 	resource->end   = base + (sparse ? IO_SPACE_SPARSE_ENCODING(max) : max);
196c9e391cfSJiang Liu 	if (insert_resource(&iomem_resource, resource)) {
1973772aea7SJiang Liu 		dev_err(dev,
198c9e391cfSJiang Liu 			"can't allocate host bridge io space resource  %pR\n",
199c9e391cfSJiang Liu 			resource);
200c9e391cfSJiang Liu 		goto free_resource;
201c9e391cfSJiang Liu 	}
2024f41d5a4SBjorn Helgaas 
2033772aea7SJiang Liu 	entry->offset = base_port;
2043772aea7SJiang Liu 	res->start = min + base_port;
2053772aea7SJiang Liu 	res->end = max + base_port;
2063f7abdefSJiang Liu 	resource_list_add_tail(iospace, &info->io_resources);
2073772aea7SJiang Liu 
2083772aea7SJiang Liu 	return 0;
2094f41d5a4SBjorn Helgaas 
2104f41d5a4SBjorn Helgaas free_resource:
2113f7abdefSJiang Liu 	resource_list_free_entry(iospace);
2123772aea7SJiang Liu 	return -ENOSPC;
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds 
215463eb297SBjorn Helgaas /*
2163772aea7SJiang Liu  * An IO port or MMIO resource assigned to a PCI host bridge may be
2173772aea7SJiang Liu  * consumed by the host bridge itself or available to its child
2183772aea7SJiang Liu  * bus/devices. The ACPI specification defines a bit (Producer/Consumer)
2193772aea7SJiang Liu  * to tell whether the resource is consumed by the host bridge itself,
2203772aea7SJiang Liu  * but firmware hasn't used that bit consistently, so we can't rely on it.
2213772aea7SJiang Liu  *
2223772aea7SJiang Liu  * On x86 and IA64 platforms, all IO port and MMIO resources are assumed
2233772aea7SJiang Liu  * to be available to child bus/devices except one special case:
2243772aea7SJiang Liu  *     IO port [0xCF8-0xCFF] is consumed by the host bridge itself
2253772aea7SJiang Liu  *     to access PCI configuration space.
2263772aea7SJiang Liu  *
2273772aea7SJiang Liu  * So explicitly filter out PCI CFG IO ports[0xCF8-0xCFF].
228463eb297SBjorn Helgaas  */
2293772aea7SJiang Liu static bool resource_is_pcicfg_ioport(struct resource *res)
2303772aea7SJiang Liu {
2313772aea7SJiang Liu 	return (res->flags & IORESOURCE_IO) &&
2323772aea7SJiang Liu 		res->start == 0xCF8 && res->end == 0xCFF;
233463eb297SBjorn Helgaas }
234463eb297SBjorn Helgaas 
23502715e86SJiang Liu static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
2361da177e4SLinus Torvalds {
23702715e86SJiang Liu 	struct device *dev = &ci->bridge->dev;
23802715e86SJiang Liu 	struct pci_root_info *info;
23902715e86SJiang Liu 	struct resource *res;
2403772aea7SJiang Liu 	struct resource_entry *entry, *tmp;
24102715e86SJiang Liu 	int status;
2421da177e4SLinus Torvalds 
24302715e86SJiang Liu 	status = acpi_pci_probe_root_resources(ci);
24402715e86SJiang Liu 	if (status > 0) {
24502715e86SJiang Liu 		info = container_of(ci, struct pci_root_info, common);
24602715e86SJiang Liu 		resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
2473772aea7SJiang Liu 			res = entry->res;
2483772aea7SJiang Liu 			if (res->flags & IORESOURCE_MEM) {
2493772aea7SJiang Liu 				/*
25002715e86SJiang Liu 				 * HP's firmware has a hack to work around a
25102715e86SJiang Liu 				 * Windows bug. Ignore these tiny memory ranges.
2523772aea7SJiang Liu 				 */
2533772aea7SJiang Liu 				if (resource_size(res) <= 16) {
25402715e86SJiang Liu 					resource_list_del(entry);
25502715e86SJiang Liu 					insert_resource(&iomem_resource,
25602715e86SJiang Liu 							entry->res);
25702715e86SJiang Liu 					resource_list_add_tail(entry,
25802715e86SJiang Liu 							&info->io_resources);
2593772aea7SJiang Liu 				}
2603772aea7SJiang Liu 			} else if (res->flags & IORESOURCE_IO) {
26102715e86SJiang Liu 				if (resource_is_pcicfg_ioport(entry->res))
2623772aea7SJiang Liu 					resource_list_destroy_entry(entry);
26302715e86SJiang Liu 				else if (add_io_space(dev, info, entry))
2643772aea7SJiang Liu 					resource_list_destroy_entry(entry);
2653772aea7SJiang Liu 			}
2663772aea7SJiang Liu 		}
267c9e391cfSJiang Liu 	}
268c9e391cfSJiang Liu 
26902715e86SJiang Liu 	return status;
27002715e86SJiang Liu }
27102715e86SJiang Liu 
27202715e86SJiang Liu static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci)
273c9e391cfSJiang Liu {
27402715e86SJiang Liu 	struct pci_root_info *info;
27502715e86SJiang Liu 	struct resource_entry *entry, *tmp;
276c9e391cfSJiang Liu 
27702715e86SJiang Liu 	info = container_of(ci, struct pci_root_info, common);
27802715e86SJiang Liu 	resource_list_for_each_entry_safe(entry, tmp, &info->io_resources) {
2793f7abdefSJiang Liu 		release_resource(entry->res);
2803f7abdefSJiang Liu 		resource_list_destroy_entry(entry);
281c9e391cfSJiang Liu 	}
282c9e391cfSJiang Liu 	kfree(info);
283c9e391cfSJiang Liu }
284c9e391cfSJiang Liu 
28502715e86SJiang Liu static struct acpi_pci_root_ops pci_acpi_root_ops = {
28602715e86SJiang Liu 	.pci_ops = &pci_root_ops,
28702715e86SJiang Liu 	.release_info = pci_acpi_root_release_info,
28802715e86SJiang Liu 	.prepare_resources = pci_acpi_root_prepare_resources,
28902715e86SJiang Liu };
2902932239fSYijing Wang 
2915b5e76e9SGreg Kroah-Hartman struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
2921da177e4SLinus Torvalds {
29357283776SBjorn Helgaas 	struct acpi_device *device = root->device;
2943772aea7SJiang Liu 	struct pci_root_info *info;
2951da177e4SLinus Torvalds 
296429ac099SYijing Wang 	info = kzalloc(sizeof(*info), GFP_KERNEL);
297429ac099SYijing Wang 	if (!info) {
298c4cbf6b9SYijing Wang 		dev_err(&device->dev,
299429ac099SYijing Wang 			"pci_bus %04x:%02x: ignored (out of memory)\n",
30002715e86SJiang Liu 			root->segment, (int)root->secondary.start);
3013a72af09SYijing Wang 		return NULL;
302429ac099SYijing Wang 	}
3031da177e4SLinus Torvalds 
30402715e86SJiang Liu 	info->controller.segment = root->segment;
3053772aea7SJiang Liu 	info->controller.companion = device;
3063772aea7SJiang Liu 	info->controller.node = acpi_get_node(device->handle);
3073772aea7SJiang Liu 	INIT_LIST_HEAD(&info->io_resources);
30802715e86SJiang Liu 	return acpi_pci_root_create(root, &pci_acpi_root_ops,
30902715e86SJiang Liu 				    &info->common, &info->controller);
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds 
3126c0cc950SRafael J. Wysocki int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
3136c0cc950SRafael J. Wysocki {
314dc4fdaf0SRafael J. Wysocki 	/*
315dc4fdaf0SRafael J. Wysocki 	 * We pass NULL as parent to pci_create_root_bus(), so if it is not NULL
316dc4fdaf0SRafael J. Wysocki 	 * here, pci_create_root_bus() has been called by someone else and
317dc4fdaf0SRafael J. Wysocki 	 * sysdata is likely to be different from what we expect.  Let it go in
318dc4fdaf0SRafael J. Wysocki 	 * that case.
319dc4fdaf0SRafael J. Wysocki 	 */
320dc4fdaf0SRafael J. Wysocki 	if (!bridge->dev.parent) {
3216c0cc950SRafael J. Wysocki 		struct pci_controller *controller = bridge->bus->sysdata;
3227b199811SRafael J. Wysocki 		ACPI_COMPANION_SET(&bridge->dev, controller->companion);
323dc4fdaf0SRafael J. Wysocki 	}
3246c0cc950SRafael J. Wysocki 	return 0;
3256c0cc950SRafael J. Wysocki }
3266c0cc950SRafael J. Wysocki 
3275b5e76e9SGreg Kroah-Hartman void pcibios_fixup_device_resources(struct pci_dev *dev)
3287b9c8ba2SKenji Kaneshige {
329ce821ef0SYinghai Lu 	int idx;
330ce821ef0SYinghai Lu 
331ce821ef0SYinghai Lu 	if (!dev->bus)
332ce821ef0SYinghai Lu 		return;
333ce821ef0SYinghai Lu 
334ce821ef0SYinghai Lu 	for (idx = 0; idx < PCI_BRIDGE_RESOURCES; idx++) {
335ce821ef0SYinghai Lu 		struct resource *r = &dev->resource[idx];
336ce821ef0SYinghai Lu 
337ce821ef0SYinghai Lu 		if (!r->flags || r->parent || !r->start)
338ce821ef0SYinghai Lu 			continue;
339ce821ef0SYinghai Lu 
340ce821ef0SYinghai Lu 		pci_claim_resource(dev, idx);
341ce821ef0SYinghai Lu 	}
3427b9c8ba2SKenji Kaneshige }
3438ea6091fSJohn Keller EXPORT_SYMBOL_GPL(pcibios_fixup_device_resources);
3447b9c8ba2SKenji Kaneshige 
3455b5e76e9SGreg Kroah-Hartman static void pcibios_fixup_bridge_resources(struct pci_dev *dev)
3467b9c8ba2SKenji Kaneshige {
347ce821ef0SYinghai Lu 	int idx;
348ce821ef0SYinghai Lu 
349ce821ef0SYinghai Lu 	if (!dev->bus)
350ce821ef0SYinghai Lu 		return;
351ce821ef0SYinghai Lu 
352ce821ef0SYinghai Lu 	for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) {
353ce821ef0SYinghai Lu 		struct resource *r = &dev->resource[idx];
354ce821ef0SYinghai Lu 
355ce821ef0SYinghai Lu 		if (!r->flags || r->parent || !r->start)
356ce821ef0SYinghai Lu 			continue;
357ce821ef0SYinghai Lu 
358ce821ef0SYinghai Lu 		pci_claim_bridge_resource(dev, idx);
359ce821ef0SYinghai Lu 	}
3607b9c8ba2SKenji Kaneshige }
3617b9c8ba2SKenji Kaneshige 
3621da177e4SLinus Torvalds /*
3631da177e4SLinus Torvalds  *  Called after each bus is probed, but before its children are examined.
3641da177e4SLinus Torvalds  */
3655b5e76e9SGreg Kroah-Hartman void pcibios_fixup_bus(struct pci_bus *b)
3661da177e4SLinus Torvalds {
3671da177e4SLinus Torvalds 	struct pci_dev *dev;
3681da177e4SLinus Torvalds 
369237865f1SBjorn Helgaas 	if (b->self) {
370237865f1SBjorn Helgaas 		pci_read_bridge_bases(b);
3717b9c8ba2SKenji Kaneshige 		pcibios_fixup_bridge_resources(b->self);
372237865f1SBjorn Helgaas 	}
3731da177e4SLinus Torvalds 	list_for_each_entry(dev, &b->devices, bus_list)
3741da177e4SLinus Torvalds 		pcibios_fixup_device_resources(dev);
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds 
377b02a4a19SJiang Liu void pcibios_add_bus(struct pci_bus *bus)
378b02a4a19SJiang Liu {
379b02a4a19SJiang Liu 	acpi_pci_add_bus(bus);
380b02a4a19SJiang Liu }
381b02a4a19SJiang Liu 
382b02a4a19SJiang Liu void pcibios_remove_bus(struct pci_bus *bus)
383b02a4a19SJiang Liu {
384b02a4a19SJiang Liu 	acpi_pci_remove_bus(bus);
385b02a4a19SJiang Liu }
386b02a4a19SJiang Liu 
38791e86df1SMyron Stowe void pcibios_set_master (struct pci_dev *dev)
38891e86df1SMyron Stowe {
38991e86df1SMyron Stowe 	/* No special bus mastering setup handling */
39091e86df1SMyron Stowe }
39191e86df1SMyron Stowe 
3921da177e4SLinus Torvalds int
3931da177e4SLinus Torvalds pcibios_enable_device (struct pci_dev *dev, int mask)
3941da177e4SLinus Torvalds {
3951da177e4SLinus Torvalds 	int ret;
3961da177e4SLinus Torvalds 
397d981f163SBjorn Helgaas 	ret = pci_enable_resources(dev, mask);
3981da177e4SLinus Torvalds 	if (ret < 0)
3991da177e4SLinus Torvalds 		return ret;
4001da177e4SLinus Torvalds 
4015a1e0baaSBjorn Helgaas 	if (!pci_dev_msi_enabled(dev))
4021da177e4SLinus Torvalds 		return acpi_pci_irq_enable(dev);
403bba6f6fcSEric W. Biederman 	return 0;
4041da177e4SLinus Torvalds }
4051da177e4SLinus Torvalds 
4061da177e4SLinus Torvalds void
4071da177e4SLinus Torvalds pcibios_disable_device (struct pci_dev *dev)
4081da177e4SLinus Torvalds {
409c7f570a5SPeter Chubb 	BUG_ON(atomic_read(&dev->enable_cnt));
4105a1e0baaSBjorn Helgaas 	if (!pci_dev_msi_enabled(dev))
4111da177e4SLinus Torvalds 		acpi_pci_irq_disable(dev);
4121da177e4SLinus Torvalds }
4131da177e4SLinus Torvalds 
4141da177e4SLinus Torvalds /**
41505933aacSChristoph Hellwig  * pci_get_legacy_mem - generic legacy mem routine
4161da177e4SLinus Torvalds  * @bus: bus to get legacy memory base address for
4171da177e4SLinus Torvalds  *
4181da177e4SLinus Torvalds  * Find the base of legacy memory for @bus.  This is typically the first
4191da177e4SLinus Torvalds  * megabyte of bus address space for @bus or is simply 0 on platforms whose
4201da177e4SLinus Torvalds  * chipsets support legacy I/O and memory routing.  Returns the base address
4211da177e4SLinus Torvalds  * or an error pointer if an error occurred.
4221da177e4SLinus Torvalds  *
4231da177e4SLinus Torvalds  * This is the ia64 generic version of this routine.  Other platforms
4241da177e4SLinus Torvalds  * are free to override it with a machine vector.
4251da177e4SLinus Torvalds  */
42605933aacSChristoph Hellwig char *pci_get_legacy_mem(struct pci_bus *bus)
4271da177e4SLinus Torvalds {
4281da177e4SLinus Torvalds 	return (char *)__IA64_UNCACHED_OFFSET;
4291da177e4SLinus Torvalds }
4301da177e4SLinus Torvalds 
4311da177e4SLinus Torvalds /**
4321da177e4SLinus Torvalds  * pci_mmap_legacy_page_range - map legacy memory space to userland
4331da177e4SLinus Torvalds  * @bus: bus whose legacy space we're mapping
4341da177e4SLinus Torvalds  * @vma: vma passed in by mmap
4351da177e4SLinus Torvalds  *
4361da177e4SLinus Torvalds  * Map legacy memory space for this device back to userspace using a machine
4371da177e4SLinus Torvalds  * vector to get the base address.
4381da177e4SLinus Torvalds  */
4391da177e4SLinus Torvalds int
440f19aeb1fSBenjamin Herrenschmidt pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma,
441f19aeb1fSBenjamin Herrenschmidt 			   enum pci_mmap_state mmap_state)
4421da177e4SLinus Torvalds {
44332e62c63SBjorn Helgaas 	unsigned long size = vma->vm_end - vma->vm_start;
44432e62c63SBjorn Helgaas 	pgprot_t prot;
4451da177e4SLinus Torvalds 	char *addr;
4461da177e4SLinus Torvalds 
447f19aeb1fSBenjamin Herrenschmidt 	/* We only support mmap'ing of legacy memory space */
448f19aeb1fSBenjamin Herrenschmidt 	if (mmap_state != pci_mmap_mem)
449f19aeb1fSBenjamin Herrenschmidt 		return -ENOSYS;
450f19aeb1fSBenjamin Herrenschmidt 
45132e62c63SBjorn Helgaas 	/*
452db9a0975SMauro Carvalho Chehab 	 * Avoid attribute aliasing.  See Documentation/ia64/aliasing.rst
45332e62c63SBjorn Helgaas 	 * for more details.
45432e62c63SBjorn Helgaas 	 */
45506c67befSLennert Buytenhek 	if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
45632e62c63SBjorn Helgaas 		return -EINVAL;
45732e62c63SBjorn Helgaas 	prot = phys_mem_access_prot(NULL, vma->vm_pgoff, size,
45832e62c63SBjorn Helgaas 				    vma->vm_page_prot);
45932e62c63SBjorn Helgaas 
4601da177e4SLinus Torvalds 	addr = pci_get_legacy_mem(bus);
4611da177e4SLinus Torvalds 	if (IS_ERR(addr))
4621da177e4SLinus Torvalds 		return PTR_ERR(addr);
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 	vma->vm_pgoff += (unsigned long)addr >> PAGE_SHIFT;
46532e62c63SBjorn Helgaas 	vma->vm_page_prot = prot;
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds 	if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
46832e62c63SBjorn Helgaas 			    size, vma->vm_page_prot))
4691da177e4SLinus Torvalds 		return -EAGAIN;
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds 	return 0;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds /**
47505933aacSChristoph Hellwig  * pci_legacy_read - read from legacy I/O space
4761da177e4SLinus Torvalds  * @bus: bus to read
4771da177e4SLinus Torvalds  * @port: legacy port value
4781da177e4SLinus Torvalds  * @val: caller allocated storage for returned value
4791da177e4SLinus Torvalds  * @size: number of bytes to read
4801da177e4SLinus Torvalds  *
4811da177e4SLinus Torvalds  * Simply reads @size bytes from @port and puts the result in @val.
4821da177e4SLinus Torvalds  *
4831da177e4SLinus Torvalds  * Again, this (and the write routine) are generic versions that can be
4841da177e4SLinus Torvalds  * overridden by the platform.  This is necessary on platforms that don't
4851da177e4SLinus Torvalds  * support legacy I/O routing or that hard fail on legacy I/O timeouts.
4861da177e4SLinus Torvalds  */
48705933aacSChristoph Hellwig int pci_legacy_read(struct pci_bus *bus, u16 port, u32 *val, u8 size)
4881da177e4SLinus Torvalds {
4891da177e4SLinus Torvalds 	int ret = size;
4901da177e4SLinus Torvalds 
4911da177e4SLinus Torvalds 	switch (size) {
4921da177e4SLinus Torvalds 	case 1:
4931da177e4SLinus Torvalds 		*val = inb(port);
4941da177e4SLinus Torvalds 		break;
4951da177e4SLinus Torvalds 	case 2:
4961da177e4SLinus Torvalds 		*val = inw(port);
4971da177e4SLinus Torvalds 		break;
4981da177e4SLinus Torvalds 	case 4:
4991da177e4SLinus Torvalds 		*val = inl(port);
5001da177e4SLinus Torvalds 		break;
5011da177e4SLinus Torvalds 	default:
5021da177e4SLinus Torvalds 		ret = -EINVAL;
5031da177e4SLinus Torvalds 		break;
5041da177e4SLinus Torvalds 	}
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds 	return ret;
5071da177e4SLinus Torvalds }
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds /**
51005933aacSChristoph Hellwig  * pci_legacy_write - perform a legacy I/O write
5111da177e4SLinus Torvalds  * @bus: bus pointer
5121da177e4SLinus Torvalds  * @port: port to write
5131da177e4SLinus Torvalds  * @val: value to write
5141da177e4SLinus Torvalds  * @size: number of bytes to write from @val
5151da177e4SLinus Torvalds  *
5161da177e4SLinus Torvalds  * Simply writes @size bytes of @val to @port.
5171da177e4SLinus Torvalds  */
51805933aacSChristoph Hellwig int pci_legacy_write(struct pci_bus *bus, u16 port, u32 val, u8 size)
5191da177e4SLinus Torvalds {
520408045afSAlex Williamson 	int ret = size;
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds 	switch (size) {
5231da177e4SLinus Torvalds 	case 1:
5241da177e4SLinus Torvalds 		outb(val, port);
5251da177e4SLinus Torvalds 		break;
5261da177e4SLinus Torvalds 	case 2:
5271da177e4SLinus Torvalds 		outw(val, port);
5281da177e4SLinus Torvalds 		break;
5291da177e4SLinus Torvalds 	case 4:
5301da177e4SLinus Torvalds 		outl(val, port);
5311da177e4SLinus Torvalds 		break;
5321da177e4SLinus Torvalds 	default:
5331da177e4SLinus Torvalds 		ret = -EINVAL;
5341da177e4SLinus Torvalds 		break;
5351da177e4SLinus Torvalds 	}
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 	return ret;
5381da177e4SLinus Torvalds }
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds /**
5413efe2d84SMatthew Wilcox  * set_pci_cacheline_size - determine cacheline size for PCI devices
5421da177e4SLinus Torvalds  *
5431da177e4SLinus Torvalds  * We want to use the line-size of the outer-most cache.  We assume
5441da177e4SLinus Torvalds  * that this line-size is the same for all CPUs.
5451da177e4SLinus Torvalds  *
5461da177e4SLinus Torvalds  * Code mostly taken from arch/ia64/kernel/palinfo.c:cache_info().
5471da177e4SLinus Torvalds  */
548ac1aa47bSJesse Barnes static void __init set_pci_dfl_cacheline_size(void)
5491da177e4SLinus Torvalds {
550e088a4adSMatthew Wilcox 	unsigned long levels, unique_caches;
551e088a4adSMatthew Wilcox 	long status;
5521da177e4SLinus Torvalds 	pal_cache_config_info_t cci;
5531da177e4SLinus Torvalds 
5541da177e4SLinus Torvalds 	status = ia64_pal_cache_summary(&levels, &unique_caches);
5551da177e4SLinus Torvalds 	if (status != 0) {
556c4cbf6b9SYijing Wang 		pr_err("%s: ia64_pal_cache_summary() failed "
557d4ed8084SHarvey Harrison 			"(status=%ld)\n", __func__, status);
5583efe2d84SMatthew Wilcox 		return;
5591da177e4SLinus Torvalds 	}
5601da177e4SLinus Torvalds 
5613efe2d84SMatthew Wilcox 	status = ia64_pal_cache_config_info(levels - 1,
5623efe2d84SMatthew Wilcox 				/* cache_type (data_or_unified)= */ 2, &cci);
5631da177e4SLinus Torvalds 	if (status != 0) {
564c4cbf6b9SYijing Wang 		pr_err("%s: ia64_pal_cache_config_info() failed "
565d4ed8084SHarvey Harrison 			"(status=%ld)\n", __func__, status);
5663efe2d84SMatthew Wilcox 		return;
5671da177e4SLinus Torvalds 	}
568ac1aa47bSJesse Barnes 	pci_dfl_cache_line_size = (1 << cci.pcci_line_size) / 4;
5691da177e4SLinus Torvalds }
5701da177e4SLinus Torvalds 
5713efe2d84SMatthew Wilcox static int __init pcibios_init(void)
5721da177e4SLinus Torvalds {
573ac1aa47bSJesse Barnes 	set_pci_dfl_cacheline_size();
5743efe2d84SMatthew Wilcox 	return 0;
5753efe2d84SMatthew Wilcox }
5761da177e4SLinus Torvalds 
5773efe2d84SMatthew Wilcox subsys_initcall(pcibios_init);
578