xref: /openbmc/linux/arch/ia64/pci/pci.c (revision 71dbc487)
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/page.h>
281da177e4SLinus Torvalds #include <asm/io.h>
291da177e4SLinus Torvalds #include <asm/sal.h>
301da177e4SLinus Torvalds #include <asm/smp.h>
311da177e4SLinus Torvalds #include <asm/irq.h>
321da177e4SLinus Torvalds #include <asm/hw_irq.h>
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds /*
351da177e4SLinus Torvalds  * Low-level SAL-based PCI configuration access functions. Note that SAL
361da177e4SLinus Torvalds  * calls are already serialized (via sal_lock), so we don't need another
371da177e4SLinus Torvalds  * synchronization mechanism here.
381da177e4SLinus Torvalds  */
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds #define PCI_SAL_ADDRESS(seg, bus, devfn, reg)		\
411da177e4SLinus Torvalds 	(((u64) seg << 24) | (bus << 16) | (devfn << 8) | (reg))
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds /* SAL 3.2 adds support for extended config space. */
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds #define PCI_SAL_EXT_ADDRESS(seg, bus, devfn, reg)	\
461da177e4SLinus Torvalds 	(((u64) seg << 28) | (bus << 20) | (devfn << 12) | (reg))
471da177e4SLinus Torvalds 
raw_pci_read(unsigned int seg,unsigned int bus,unsigned int devfn,int reg,int len,u32 * value)48b6ce068aSMatthew Wilcox int raw_pci_read(unsigned int seg, unsigned int bus, unsigned int devfn,
491da177e4SLinus Torvalds 	      int reg, int len, u32 *value)
501da177e4SLinus Torvalds {
511da177e4SLinus Torvalds 	u64 addr, data = 0;
521da177e4SLinus Torvalds 	int mode, result;
531da177e4SLinus Torvalds 
541da177e4SLinus Torvalds 	if (!value || (seg > 65535) || (bus > 255) || (devfn > 255) || (reg > 4095))
551da177e4SLinus Torvalds 		return -EINVAL;
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds 	if ((seg | reg) <= 255) {
581da177e4SLinus Torvalds 		addr = PCI_SAL_ADDRESS(seg, bus, devfn, reg);
591da177e4SLinus Torvalds 		mode = 0;
60adcd7403SMatthew Wilcox 	} else if (sal_revision >= SAL_VERSION_CODE(3,2)) {
611da177e4SLinus Torvalds 		addr = PCI_SAL_EXT_ADDRESS(seg, bus, devfn, reg);
621da177e4SLinus Torvalds 		mode = 1;
63adcd7403SMatthew Wilcox 	} else {
64adcd7403SMatthew Wilcox 		return -EINVAL;
651da177e4SLinus Torvalds 	}
66adcd7403SMatthew Wilcox 
671da177e4SLinus Torvalds 	result = ia64_sal_pci_config_read(addr, mode, len, &data);
681da177e4SLinus Torvalds 	if (result != 0)
691da177e4SLinus Torvalds 		return -EINVAL;
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds 	*value = (u32) data;
721da177e4SLinus Torvalds 	return 0;
731da177e4SLinus Torvalds }
741da177e4SLinus Torvalds 
raw_pci_write(unsigned int seg,unsigned int bus,unsigned int devfn,int reg,int len,u32 value)75b6ce068aSMatthew Wilcox int raw_pci_write(unsigned int seg, unsigned int bus, unsigned int devfn,
761da177e4SLinus Torvalds 	       int reg, int len, u32 value)
771da177e4SLinus Torvalds {
781da177e4SLinus Torvalds 	u64 addr;
791da177e4SLinus Torvalds 	int mode, result;
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds 	if ((seg > 65535) || (bus > 255) || (devfn > 255) || (reg > 4095))
821da177e4SLinus Torvalds 		return -EINVAL;
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds 	if ((seg | reg) <= 255) {
851da177e4SLinus Torvalds 		addr = PCI_SAL_ADDRESS(seg, bus, devfn, reg);
861da177e4SLinus Torvalds 		mode = 0;
87adcd7403SMatthew Wilcox 	} else if (sal_revision >= SAL_VERSION_CODE(3,2)) {
881da177e4SLinus Torvalds 		addr = PCI_SAL_EXT_ADDRESS(seg, bus, devfn, reg);
891da177e4SLinus Torvalds 		mode = 1;
90adcd7403SMatthew Wilcox 	} else {
91adcd7403SMatthew Wilcox 		return -EINVAL;
921da177e4SLinus Torvalds 	}
931da177e4SLinus Torvalds 	result = ia64_sal_pci_config_write(addr, mode, len, value);
941da177e4SLinus Torvalds 	if (result != 0)
951da177e4SLinus Torvalds 		return -EINVAL;
961da177e4SLinus Torvalds 	return 0;
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds 
pci_read(struct pci_bus * bus,unsigned int devfn,int where,int size,u32 * value)99b6ce068aSMatthew Wilcox static int pci_read(struct pci_bus *bus, unsigned int devfn, int where,
100b6ce068aSMatthew Wilcox 							int size, u32 *value)
1011da177e4SLinus Torvalds {
102b6ce068aSMatthew Wilcox 	return raw_pci_read(pci_domain_nr(bus), bus->number,
1031da177e4SLinus Torvalds 				 devfn, where, size, value);
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
pci_write(struct pci_bus * bus,unsigned int devfn,int where,int size,u32 value)106b6ce068aSMatthew Wilcox static int pci_write(struct pci_bus *bus, unsigned int devfn, int where,
107b6ce068aSMatthew Wilcox 							int size, u32 value)
1081da177e4SLinus Torvalds {
109b6ce068aSMatthew Wilcox 	return raw_pci_write(pci_domain_nr(bus), bus->number,
1101da177e4SLinus Torvalds 				  devfn, where, size, value);
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds struct pci_ops pci_root_ops = {
1141da177e4SLinus Torvalds 	.read = pci_read,
1151da177e4SLinus Torvalds 	.write = pci_write,
1161da177e4SLinus Torvalds };
1171da177e4SLinus Torvalds 
1184f41d5a4SBjorn Helgaas struct pci_root_info {
11902715e86SJiang Liu 	struct acpi_pci_root_info common;
1203772aea7SJiang Liu 	struct pci_controller controller;
121c9e391cfSJiang Liu 	struct list_head io_resources;
1224f41d5a4SBjorn Helgaas };
1234f41d5a4SBjorn Helgaas 
new_space(u64 phys_base,int sparse)12402715e86SJiang Liu static unsigned int new_space(u64 phys_base, int sparse)
1251da177e4SLinus Torvalds {
1264f41d5a4SBjorn Helgaas 	u64 mmio_base;
1271da177e4SLinus Torvalds 	int i;
1281da177e4SLinus Torvalds 
1294f41d5a4SBjorn Helgaas 	if (phys_base == 0)
1304f41d5a4SBjorn Helgaas 		return 0;	/* legacy I/O port space */
1311da177e4SLinus Torvalds 
1324f41d5a4SBjorn Helgaas 	mmio_base = (u64) ioremap(phys_base, 0);
1331da177e4SLinus Torvalds 	for (i = 0; i < num_io_spaces; i++)
1344f41d5a4SBjorn Helgaas 		if (io_space[i].mmio_base == mmio_base &&
1351da177e4SLinus Torvalds 		    io_space[i].sparse == sparse)
1364f41d5a4SBjorn Helgaas 			return i;
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds 	if (num_io_spaces == MAX_IO_SPACES) {
139c4cbf6b9SYijing Wang 		pr_err("PCI: Too many IO port spaces "
1404f41d5a4SBjorn Helgaas 			"(MAX_IO_SPACES=%lu)\n", MAX_IO_SPACES);
1411da177e4SLinus Torvalds 		return ~0;
1421da177e4SLinus Torvalds 	}
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds 	i = num_io_spaces++;
1454f41d5a4SBjorn Helgaas 	io_space[i].mmio_base = mmio_base;
1461da177e4SLinus Torvalds 	io_space[i].sparse = sparse;
1471da177e4SLinus Torvalds 
1484f41d5a4SBjorn Helgaas 	return i;
1494f41d5a4SBjorn Helgaas }
1504f41d5a4SBjorn Helgaas 
add_io_space(struct device * dev,struct pci_root_info * info,struct resource_entry * entry)1513772aea7SJiang Liu static int add_io_space(struct device *dev, struct pci_root_info *info,
1523772aea7SJiang Liu 			struct resource_entry *entry)
1534f41d5a4SBjorn Helgaas {
1543f7abdefSJiang Liu 	struct resource_entry *iospace;
1553772aea7SJiang Liu 	struct resource *resource, *res = entry->res;
1564f41d5a4SBjorn Helgaas 	char *name;
157e088a4adSMatthew Wilcox 	unsigned long base, min, max, base_port;
1584f41d5a4SBjorn Helgaas 	unsigned int sparse = 0, space_nr, len;
1594f41d5a4SBjorn Helgaas 
16002715e86SJiang Liu 	len = strlen(info->common.name) + 32;
1613f7abdefSJiang Liu 	iospace = resource_list_create_entry(NULL, len);
162c9e391cfSJiang Liu 	if (!iospace) {
1633772aea7SJiang Liu 		dev_err(dev, "PCI: No memory for %s I/O port space\n",
16402715e86SJiang Liu 			info->common.name);
1653772aea7SJiang Liu 		return -ENOMEM;
1664f41d5a4SBjorn Helgaas 	}
1674f41d5a4SBjorn Helgaas 
1683772aea7SJiang Liu 	if (res->flags & IORESOURCE_IO_SPARSE)
1694f41d5a4SBjorn Helgaas 		sparse = 1;
1703772aea7SJiang Liu 	space_nr = new_space(entry->offset, sparse);
1714f41d5a4SBjorn Helgaas 	if (space_nr == ~0)
172c9e391cfSJiang Liu 		goto free_resource;
1734f41d5a4SBjorn Helgaas 
1743772aea7SJiang Liu 	name = (char *)(iospace + 1);
1753772aea7SJiang Liu 	min = res->start - entry->offset;
1763772aea7SJiang Liu 	max = res->end - entry->offset;
1774f41d5a4SBjorn Helgaas 	base = __pa(io_space[space_nr].mmio_base);
1784f41d5a4SBjorn Helgaas 	base_port = IO_SPACE_BASE(space_nr);
17902715e86SJiang Liu 	snprintf(name, len, "%s I/O Ports %08lx-%08lx", info->common.name,
1804f41d5a4SBjorn Helgaas 		 base_port + min, base_port + max);
1814f41d5a4SBjorn Helgaas 
1824f41d5a4SBjorn Helgaas 	/*
1834f41d5a4SBjorn Helgaas 	 * The SDM guarantees the legacy 0-64K space is sparse, but if the
1844f41d5a4SBjorn Helgaas 	 * mapping is done by the processor (not the bridge), ACPI may not
1854f41d5a4SBjorn Helgaas 	 * mark it as sparse.
1864f41d5a4SBjorn Helgaas 	 */
1874f41d5a4SBjorn Helgaas 	if (space_nr == 0)
1884f41d5a4SBjorn Helgaas 		sparse = 1;
1894f41d5a4SBjorn Helgaas 
1903f7abdefSJiang Liu 	resource = iospace->res;
1914f41d5a4SBjorn Helgaas 	resource->name  = name;
1924f41d5a4SBjorn Helgaas 	resource->flags = IORESOURCE_MEM;
1934f41d5a4SBjorn Helgaas 	resource->start = base + (sparse ? IO_SPACE_SPARSE_ENCODING(min) : min);
1944f41d5a4SBjorn Helgaas 	resource->end   = base + (sparse ? IO_SPACE_SPARSE_ENCODING(max) : max);
195c9e391cfSJiang Liu 	if (insert_resource(&iomem_resource, resource)) {
1963772aea7SJiang Liu 		dev_err(dev,
197c9e391cfSJiang Liu 			"can't allocate host bridge io space resource  %pR\n",
198c9e391cfSJiang Liu 			resource);
199c9e391cfSJiang Liu 		goto free_resource;
200c9e391cfSJiang Liu 	}
2014f41d5a4SBjorn Helgaas 
2023772aea7SJiang Liu 	entry->offset = base_port;
2033772aea7SJiang Liu 	res->start = min + base_port;
2043772aea7SJiang Liu 	res->end = max + base_port;
2053f7abdefSJiang Liu 	resource_list_add_tail(iospace, &info->io_resources);
2063772aea7SJiang Liu 
2073772aea7SJiang Liu 	return 0;
2084f41d5a4SBjorn Helgaas 
2094f41d5a4SBjorn Helgaas free_resource:
2103f7abdefSJiang Liu 	resource_list_free_entry(iospace);
2113772aea7SJiang Liu 	return -ENOSPC;
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds 
214463eb297SBjorn Helgaas /*
2153772aea7SJiang Liu  * An IO port or MMIO resource assigned to a PCI host bridge may be
2163772aea7SJiang Liu  * consumed by the host bridge itself or available to its child
2173772aea7SJiang Liu  * bus/devices. The ACPI specification defines a bit (Producer/Consumer)
2183772aea7SJiang Liu  * to tell whether the resource is consumed by the host bridge itself,
2193772aea7SJiang Liu  * but firmware hasn't used that bit consistently, so we can't rely on it.
2203772aea7SJiang Liu  *
2213772aea7SJiang Liu  * On x86 and IA64 platforms, all IO port and MMIO resources are assumed
2223772aea7SJiang Liu  * to be available to child bus/devices except one special case:
2233772aea7SJiang Liu  *     IO port [0xCF8-0xCFF] is consumed by the host bridge itself
2243772aea7SJiang Liu  *     to access PCI configuration space.
2253772aea7SJiang Liu  *
2263772aea7SJiang Liu  * So explicitly filter out PCI CFG IO ports[0xCF8-0xCFF].
227463eb297SBjorn Helgaas  */
resource_is_pcicfg_ioport(struct resource * res)2283772aea7SJiang Liu static bool resource_is_pcicfg_ioport(struct resource *res)
2293772aea7SJiang Liu {
2303772aea7SJiang Liu 	return (res->flags & IORESOURCE_IO) &&
2313772aea7SJiang Liu 		res->start == 0xCF8 && res->end == 0xCFF;
232463eb297SBjorn Helgaas }
233463eb297SBjorn Helgaas 
pci_acpi_root_prepare_resources(struct acpi_pci_root_info * ci)23402715e86SJiang Liu static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
2351da177e4SLinus Torvalds {
23602715e86SJiang Liu 	struct device *dev = &ci->bridge->dev;
23702715e86SJiang Liu 	struct pci_root_info *info;
23802715e86SJiang Liu 	struct resource *res;
2393772aea7SJiang Liu 	struct resource_entry *entry, *tmp;
24002715e86SJiang Liu 	int status;
2411da177e4SLinus Torvalds 
24202715e86SJiang Liu 	status = acpi_pci_probe_root_resources(ci);
24302715e86SJiang Liu 	if (status > 0) {
24402715e86SJiang Liu 		info = container_of(ci, struct pci_root_info, common);
24502715e86SJiang Liu 		resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
2463772aea7SJiang Liu 			res = entry->res;
2473772aea7SJiang Liu 			if (res->flags & IORESOURCE_MEM) {
2483772aea7SJiang Liu 				/*
24902715e86SJiang Liu 				 * HP's firmware has a hack to work around a
25002715e86SJiang Liu 				 * Windows bug. Ignore these tiny memory ranges.
2513772aea7SJiang Liu 				 */
2523772aea7SJiang Liu 				if (resource_size(res) <= 16) {
25302715e86SJiang Liu 					resource_list_del(entry);
25402715e86SJiang Liu 					insert_resource(&iomem_resource,
25502715e86SJiang Liu 							entry->res);
25602715e86SJiang Liu 					resource_list_add_tail(entry,
25702715e86SJiang Liu 							&info->io_resources);
2583772aea7SJiang Liu 				}
2593772aea7SJiang Liu 			} else if (res->flags & IORESOURCE_IO) {
26002715e86SJiang Liu 				if (resource_is_pcicfg_ioport(entry->res))
2613772aea7SJiang Liu 					resource_list_destroy_entry(entry);
26202715e86SJiang Liu 				else if (add_io_space(dev, info, entry))
2633772aea7SJiang Liu 					resource_list_destroy_entry(entry);
2643772aea7SJiang Liu 			}
2653772aea7SJiang Liu 		}
266c9e391cfSJiang Liu 	}
267c9e391cfSJiang Liu 
26802715e86SJiang Liu 	return status;
26902715e86SJiang Liu }
27002715e86SJiang Liu 
pci_acpi_root_release_info(struct acpi_pci_root_info * ci)27102715e86SJiang Liu static void pci_acpi_root_release_info(struct acpi_pci_root_info *ci)
272c9e391cfSJiang Liu {
27302715e86SJiang Liu 	struct pci_root_info *info;
27402715e86SJiang Liu 	struct resource_entry *entry, *tmp;
275c9e391cfSJiang Liu 
27602715e86SJiang Liu 	info = container_of(ci, struct pci_root_info, common);
27702715e86SJiang Liu 	resource_list_for_each_entry_safe(entry, tmp, &info->io_resources) {
2783f7abdefSJiang Liu 		release_resource(entry->res);
2793f7abdefSJiang Liu 		resource_list_destroy_entry(entry);
280c9e391cfSJiang Liu 	}
281c9e391cfSJiang Liu 	kfree(info);
282c9e391cfSJiang Liu }
283c9e391cfSJiang Liu 
28402715e86SJiang Liu static struct acpi_pci_root_ops pci_acpi_root_ops = {
28502715e86SJiang Liu 	.pci_ops = &pci_root_ops,
28602715e86SJiang Liu 	.release_info = pci_acpi_root_release_info,
28702715e86SJiang Liu 	.prepare_resources = pci_acpi_root_prepare_resources,
28802715e86SJiang Liu };
2892932239fSYijing Wang 
pci_acpi_scan_root(struct acpi_pci_root * root)2905b5e76e9SGreg Kroah-Hartman struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
2911da177e4SLinus Torvalds {
29257283776SBjorn Helgaas 	struct acpi_device *device = root->device;
2933772aea7SJiang Liu 	struct pci_root_info *info;
2941da177e4SLinus Torvalds 
295429ac099SYijing Wang 	info = kzalloc(sizeof(*info), GFP_KERNEL);
296429ac099SYijing Wang 	if (!info) {
297c4cbf6b9SYijing Wang 		dev_err(&device->dev,
298429ac099SYijing Wang 			"pci_bus %04x:%02x: ignored (out of memory)\n",
29902715e86SJiang Liu 			root->segment, (int)root->secondary.start);
3003a72af09SYijing Wang 		return NULL;
301429ac099SYijing Wang 	}
3021da177e4SLinus Torvalds 
30302715e86SJiang Liu 	info->controller.segment = root->segment;
3043772aea7SJiang Liu 	info->controller.companion = device;
3053772aea7SJiang Liu 	info->controller.node = acpi_get_node(device->handle);
3063772aea7SJiang Liu 	INIT_LIST_HEAD(&info->io_resources);
30702715e86SJiang Liu 	return acpi_pci_root_create(root, &pci_acpi_root_ops,
30802715e86SJiang Liu 				    &info->common, &info->controller);
3091da177e4SLinus Torvalds }
3101da177e4SLinus Torvalds 
pcibios_root_bridge_prepare(struct pci_host_bridge * bridge)3116c0cc950SRafael J. Wysocki int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
3126c0cc950SRafael J. Wysocki {
313dc4fdaf0SRafael J. Wysocki 	/*
314dc4fdaf0SRafael J. Wysocki 	 * We pass NULL as parent to pci_create_root_bus(), so if it is not NULL
315dc4fdaf0SRafael J. Wysocki 	 * here, pci_create_root_bus() has been called by someone else and
316dc4fdaf0SRafael J. Wysocki 	 * sysdata is likely to be different from what we expect.  Let it go in
317dc4fdaf0SRafael J. Wysocki 	 * that case.
318dc4fdaf0SRafael J. Wysocki 	 */
319dc4fdaf0SRafael J. Wysocki 	if (!bridge->dev.parent) {
3206c0cc950SRafael J. Wysocki 		struct pci_controller *controller = bridge->bus->sysdata;
3217b199811SRafael J. Wysocki 		ACPI_COMPANION_SET(&bridge->dev, controller->companion);
322dc4fdaf0SRafael J. Wysocki 	}
3236c0cc950SRafael J. Wysocki 	return 0;
3246c0cc950SRafael J. Wysocki }
3256c0cc950SRafael J. Wysocki 
pcibios_fixup_device_resources(struct pci_dev * dev)3265b5e76e9SGreg Kroah-Hartman void pcibios_fixup_device_resources(struct pci_dev *dev)
3277b9c8ba2SKenji Kaneshige {
328ce821ef0SYinghai Lu 	int idx;
329ce821ef0SYinghai Lu 
330ce821ef0SYinghai Lu 	if (!dev->bus)
331ce821ef0SYinghai Lu 		return;
332ce821ef0SYinghai Lu 
333ce821ef0SYinghai Lu 	for (idx = 0; idx < PCI_BRIDGE_RESOURCES; idx++) {
334ce821ef0SYinghai Lu 		struct resource *r = &dev->resource[idx];
335ce821ef0SYinghai Lu 
336ce821ef0SYinghai Lu 		if (!r->flags || r->parent || !r->start)
337ce821ef0SYinghai Lu 			continue;
338ce821ef0SYinghai Lu 
339ce821ef0SYinghai Lu 		pci_claim_resource(dev, idx);
340ce821ef0SYinghai Lu 	}
3417b9c8ba2SKenji Kaneshige }
3428ea6091fSJohn Keller EXPORT_SYMBOL_GPL(pcibios_fixup_device_resources);
3437b9c8ba2SKenji Kaneshige 
pcibios_fixup_bridge_resources(struct pci_dev * dev)3445b5e76e9SGreg Kroah-Hartman static void pcibios_fixup_bridge_resources(struct pci_dev *dev)
3457b9c8ba2SKenji Kaneshige {
346ce821ef0SYinghai Lu 	int idx;
347ce821ef0SYinghai Lu 
348ce821ef0SYinghai Lu 	if (!dev->bus)
349ce821ef0SYinghai Lu 		return;
350ce821ef0SYinghai Lu 
351ce821ef0SYinghai Lu 	for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) {
352ce821ef0SYinghai Lu 		struct resource *r = &dev->resource[idx];
353ce821ef0SYinghai Lu 
354ce821ef0SYinghai Lu 		if (!r->flags || r->parent || !r->start)
355ce821ef0SYinghai Lu 			continue;
356ce821ef0SYinghai Lu 
357ce821ef0SYinghai Lu 		pci_claim_bridge_resource(dev, idx);
358ce821ef0SYinghai Lu 	}
3597b9c8ba2SKenji Kaneshige }
3607b9c8ba2SKenji Kaneshige 
3611da177e4SLinus Torvalds /*
3621da177e4SLinus Torvalds  *  Called after each bus is probed, but before its children are examined.
3631da177e4SLinus Torvalds  */
pcibios_fixup_bus(struct pci_bus * b)3645b5e76e9SGreg Kroah-Hartman void pcibios_fixup_bus(struct pci_bus *b)
3651da177e4SLinus Torvalds {
3661da177e4SLinus Torvalds 	struct pci_dev *dev;
3671da177e4SLinus Torvalds 
368237865f1SBjorn Helgaas 	if (b->self) {
369237865f1SBjorn Helgaas 		pci_read_bridge_bases(b);
3707b9c8ba2SKenji Kaneshige 		pcibios_fixup_bridge_resources(b->self);
371237865f1SBjorn Helgaas 	}
3721da177e4SLinus Torvalds 	list_for_each_entry(dev, &b->devices, bus_list)
3731da177e4SLinus Torvalds 		pcibios_fixup_device_resources(dev);
3741da177e4SLinus Torvalds }
3751da177e4SLinus Torvalds 
pcibios_add_bus(struct pci_bus * bus)376b02a4a19SJiang Liu void pcibios_add_bus(struct pci_bus *bus)
377b02a4a19SJiang Liu {
378b02a4a19SJiang Liu 	acpi_pci_add_bus(bus);
379b02a4a19SJiang Liu }
380b02a4a19SJiang Liu 
pcibios_remove_bus(struct pci_bus * bus)381b02a4a19SJiang Liu void pcibios_remove_bus(struct pci_bus *bus)
382b02a4a19SJiang Liu {
383b02a4a19SJiang Liu 	acpi_pci_remove_bus(bus);
384b02a4a19SJiang Liu }
385b02a4a19SJiang Liu 
pcibios_set_master(struct pci_dev * dev)38691e86df1SMyron Stowe void pcibios_set_master (struct pci_dev *dev)
38791e86df1SMyron Stowe {
38891e86df1SMyron Stowe 	/* No special bus mastering setup handling */
38991e86df1SMyron Stowe }
39091e86df1SMyron Stowe 
3911da177e4SLinus Torvalds int
pcibios_enable_device(struct pci_dev * dev,int mask)3921da177e4SLinus Torvalds pcibios_enable_device (struct pci_dev *dev, int mask)
3931da177e4SLinus Torvalds {
3941da177e4SLinus Torvalds 	int ret;
3951da177e4SLinus Torvalds 
396d981f163SBjorn Helgaas 	ret = pci_enable_resources(dev, mask);
3971da177e4SLinus Torvalds 	if (ret < 0)
3981da177e4SLinus Torvalds 		return ret;
3991da177e4SLinus Torvalds 
4005a1e0baaSBjorn Helgaas 	if (!pci_dev_msi_enabled(dev))
4011da177e4SLinus Torvalds 		return acpi_pci_irq_enable(dev);
402bba6f6fcSEric W. Biederman 	return 0;
4031da177e4SLinus Torvalds }
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds void
pcibios_disable_device(struct pci_dev * dev)4061da177e4SLinus Torvalds pcibios_disable_device (struct pci_dev *dev)
4071da177e4SLinus Torvalds {
408c7f570a5SPeter Chubb 	BUG_ON(atomic_read(&dev->enable_cnt));
4095a1e0baaSBjorn Helgaas 	if (!pci_dev_msi_enabled(dev))
4101da177e4SLinus Torvalds 		acpi_pci_irq_disable(dev);
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds /**
41405933aacSChristoph Hellwig  * pci_get_legacy_mem - generic legacy mem routine
4151da177e4SLinus Torvalds  * @bus: bus to get legacy memory base address for
4161da177e4SLinus Torvalds  *
4171da177e4SLinus Torvalds  * Find the base of legacy memory for @bus.  This is typically the first
4181da177e4SLinus Torvalds  * megabyte of bus address space for @bus or is simply 0 on platforms whose
4191da177e4SLinus Torvalds  * chipsets support legacy I/O and memory routing.  Returns the base address
4201da177e4SLinus Torvalds  * or an error pointer if an error occurred.
4211da177e4SLinus Torvalds  *
4221da177e4SLinus Torvalds  * This is the ia64 generic version of this routine.  Other platforms
4231da177e4SLinus Torvalds  * are free to override it with a machine vector.
4241da177e4SLinus Torvalds  */
pci_get_legacy_mem(struct pci_bus * bus)42505933aacSChristoph Hellwig char *pci_get_legacy_mem(struct pci_bus *bus)
4261da177e4SLinus Torvalds {
4271da177e4SLinus Torvalds 	return (char *)__IA64_UNCACHED_OFFSET;
4281da177e4SLinus Torvalds }
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds /**
4311da177e4SLinus Torvalds  * pci_mmap_legacy_page_range - map legacy memory space to userland
4321da177e4SLinus Torvalds  * @bus: bus whose legacy space we're mapping
4331da177e4SLinus Torvalds  * @vma: vma passed in by mmap
4341da177e4SLinus Torvalds  *
4351da177e4SLinus Torvalds  * Map legacy memory space for this device back to userspace using a machine
4361da177e4SLinus Torvalds  * vector to get the base address.
4371da177e4SLinus Torvalds  */
4381da177e4SLinus Torvalds int
pci_mmap_legacy_page_range(struct pci_bus * bus,struct vm_area_struct * vma,enum pci_mmap_state mmap_state)439f19aeb1fSBenjamin Herrenschmidt pci_mmap_legacy_page_range(struct pci_bus *bus, struct vm_area_struct *vma,
440f19aeb1fSBenjamin Herrenschmidt 			   enum pci_mmap_state mmap_state)
4411da177e4SLinus Torvalds {
44232e62c63SBjorn Helgaas 	unsigned long size = vma->vm_end - vma->vm_start;
44332e62c63SBjorn Helgaas 	pgprot_t prot;
4441da177e4SLinus Torvalds 	char *addr;
4451da177e4SLinus Torvalds 
446f19aeb1fSBenjamin Herrenschmidt 	/* We only support mmap'ing of legacy memory space */
447f19aeb1fSBenjamin Herrenschmidt 	if (mmap_state != pci_mmap_mem)
448f19aeb1fSBenjamin Herrenschmidt 		return -ENOSYS;
449f19aeb1fSBenjamin Herrenschmidt 
45032e62c63SBjorn Helgaas 	/*
451*71dbc487SJonathan Corbet 	 * Avoid attribute aliasing.  See Documentation/arch/ia64/aliasing.rst
45232e62c63SBjorn Helgaas 	 * for more details.
45332e62c63SBjorn Helgaas 	 */
45406c67befSLennert Buytenhek 	if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
45532e62c63SBjorn Helgaas 		return -EINVAL;
45632e62c63SBjorn Helgaas 	prot = phys_mem_access_prot(NULL, vma->vm_pgoff, size,
45732e62c63SBjorn Helgaas 				    vma->vm_page_prot);
45832e62c63SBjorn Helgaas 
4591da177e4SLinus Torvalds 	addr = pci_get_legacy_mem(bus);
4601da177e4SLinus Torvalds 	if (IS_ERR(addr))
4611da177e4SLinus Torvalds 		return PTR_ERR(addr);
4621da177e4SLinus Torvalds 
4631da177e4SLinus Torvalds 	vma->vm_pgoff += (unsigned long)addr >> PAGE_SHIFT;
46432e62c63SBjorn Helgaas 	vma->vm_page_prot = prot;
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
46732e62c63SBjorn Helgaas 			    size, vma->vm_page_prot))
4681da177e4SLinus Torvalds 		return -EAGAIN;
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 	return 0;
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds 
4731da177e4SLinus Torvalds /**
47405933aacSChristoph Hellwig  * pci_legacy_read - read from legacy I/O space
4751da177e4SLinus Torvalds  * @bus: bus to read
4761da177e4SLinus Torvalds  * @port: legacy port value
4771da177e4SLinus Torvalds  * @val: caller allocated storage for returned value
4781da177e4SLinus Torvalds  * @size: number of bytes to read
4791da177e4SLinus Torvalds  *
4801da177e4SLinus Torvalds  * Simply reads @size bytes from @port and puts the result in @val.
4811da177e4SLinus Torvalds  *
4821da177e4SLinus Torvalds  * Again, this (and the write routine) are generic versions that can be
4831da177e4SLinus Torvalds  * overridden by the platform.  This is necessary on platforms that don't
4841da177e4SLinus Torvalds  * support legacy I/O routing or that hard fail on legacy I/O timeouts.
4851da177e4SLinus Torvalds  */
pci_legacy_read(struct pci_bus * bus,u16 port,u32 * val,u8 size)48605933aacSChristoph Hellwig int pci_legacy_read(struct pci_bus *bus, u16 port, u32 *val, u8 size)
4871da177e4SLinus Torvalds {
4881da177e4SLinus Torvalds 	int ret = size;
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	switch (size) {
4911da177e4SLinus Torvalds 	case 1:
4921da177e4SLinus Torvalds 		*val = inb(port);
4931da177e4SLinus Torvalds 		break;
4941da177e4SLinus Torvalds 	case 2:
4951da177e4SLinus Torvalds 		*val = inw(port);
4961da177e4SLinus Torvalds 		break;
4971da177e4SLinus Torvalds 	case 4:
4981da177e4SLinus Torvalds 		*val = inl(port);
4991da177e4SLinus Torvalds 		break;
5001da177e4SLinus Torvalds 	default:
5011da177e4SLinus Torvalds 		ret = -EINVAL;
5021da177e4SLinus Torvalds 		break;
5031da177e4SLinus Torvalds 	}
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds 	return ret;
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds 
5081da177e4SLinus Torvalds /**
50905933aacSChristoph Hellwig  * pci_legacy_write - perform a legacy I/O write
5101da177e4SLinus Torvalds  * @bus: bus pointer
5111da177e4SLinus Torvalds  * @port: port to write
5121da177e4SLinus Torvalds  * @val: value to write
5131da177e4SLinus Torvalds  * @size: number of bytes to write from @val
5141da177e4SLinus Torvalds  *
5151da177e4SLinus Torvalds  * Simply writes @size bytes of @val to @port.
5161da177e4SLinus Torvalds  */
pci_legacy_write(struct pci_bus * bus,u16 port,u32 val,u8 size)51705933aacSChristoph Hellwig int pci_legacy_write(struct pci_bus *bus, u16 port, u32 val, u8 size)
5181da177e4SLinus Torvalds {
519408045afSAlex Williamson 	int ret = size;
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	switch (size) {
5221da177e4SLinus Torvalds 	case 1:
5231da177e4SLinus Torvalds 		outb(val, port);
5241da177e4SLinus Torvalds 		break;
5251da177e4SLinus Torvalds 	case 2:
5261da177e4SLinus Torvalds 		outw(val, port);
5271da177e4SLinus Torvalds 		break;
5281da177e4SLinus Torvalds 	case 4:
5291da177e4SLinus Torvalds 		outl(val, port);
5301da177e4SLinus Torvalds 		break;
5311da177e4SLinus Torvalds 	default:
5321da177e4SLinus Torvalds 		ret = -EINVAL;
5331da177e4SLinus Torvalds 		break;
5341da177e4SLinus Torvalds 	}
5351da177e4SLinus Torvalds 
5361da177e4SLinus Torvalds 	return ret;
5371da177e4SLinus Torvalds }
5381da177e4SLinus Torvalds 
5391da177e4SLinus Torvalds /**
5403efe2d84SMatthew Wilcox  * set_pci_cacheline_size - determine cacheline size for PCI devices
5411da177e4SLinus Torvalds  *
5421da177e4SLinus Torvalds  * We want to use the line-size of the outer-most cache.  We assume
5431da177e4SLinus Torvalds  * that this line-size is the same for all CPUs.
5441da177e4SLinus Torvalds  *
5451da177e4SLinus Torvalds  * Code mostly taken from arch/ia64/kernel/palinfo.c:cache_info().
5461da177e4SLinus Torvalds  */
set_pci_dfl_cacheline_size(void)547ac1aa47bSJesse Barnes static void __init set_pci_dfl_cacheline_size(void)
5481da177e4SLinus Torvalds {
549e088a4adSMatthew Wilcox 	unsigned long levels, unique_caches;
550e088a4adSMatthew Wilcox 	long status;
5511da177e4SLinus Torvalds 	pal_cache_config_info_t cci;
5521da177e4SLinus Torvalds 
5531da177e4SLinus Torvalds 	status = ia64_pal_cache_summary(&levels, &unique_caches);
5541da177e4SLinus Torvalds 	if (status != 0) {
555c4cbf6b9SYijing Wang 		pr_err("%s: ia64_pal_cache_summary() failed "
556d4ed8084SHarvey Harrison 			"(status=%ld)\n", __func__, status);
5573efe2d84SMatthew Wilcox 		return;
5581da177e4SLinus Torvalds 	}
5591da177e4SLinus Torvalds 
5603efe2d84SMatthew Wilcox 	status = ia64_pal_cache_config_info(levels - 1,
5613efe2d84SMatthew Wilcox 				/* cache_type (data_or_unified)= */ 2, &cci);
5621da177e4SLinus Torvalds 	if (status != 0) {
563c4cbf6b9SYijing Wang 		pr_err("%s: ia64_pal_cache_config_info() failed "
564d4ed8084SHarvey Harrison 			"(status=%ld)\n", __func__, status);
5653efe2d84SMatthew Wilcox 		return;
5661da177e4SLinus Torvalds 	}
567ac1aa47bSJesse Barnes 	pci_dfl_cache_line_size = (1 << cci.pcci_line_size) / 4;
5681da177e4SLinus Torvalds }
5691da177e4SLinus Torvalds 
pcibios_init(void)5703efe2d84SMatthew Wilcox static int __init pcibios_init(void)
5711da177e4SLinus Torvalds {
572ac1aa47bSJesse Barnes 	set_pci_dfl_cacheline_size();
5733efe2d84SMatthew Wilcox 	return 0;
5743efe2d84SMatthew Wilcox }
5751da177e4SLinus Torvalds 
5763efe2d84SMatthew Wilcox subsys_initcall(pcibios_init);
577