xref: /openbmc/linux/drivers/pci/setup-res.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
3df62ab5eSBjorn Helgaas  * Support routines for initializing a PCI subsystem
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Extruded from code written by
61da177e4SLinus Torvalds  *      Dave Rusling (david.rusling@reo.mts.dec.com)
71da177e4SLinus Torvalds  *      David Mosberger (davidm@cs.arizona.edu)
81da177e4SLinus Torvalds  *	David Miller (davem@redhat.com)
91da177e4SLinus Torvalds  *
10df62ab5eSBjorn Helgaas  * Fixed for multiple PCI buses, 1999 Andrea Arcangeli <andrea@suse.de>
11df62ab5eSBjorn Helgaas  *
121da177e4SLinus Torvalds  * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
131da177e4SLinus Torvalds  *	     Resource sorting
141da177e4SLinus Torvalds  */
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds #include <linux/kernel.h>
17363c75dbSPaul Gortmaker #include <linux/export.h>
181da177e4SLinus Torvalds #include <linux/pci.h>
191da177e4SLinus Torvalds #include <linux/errno.h>
201da177e4SLinus Torvalds #include <linux/ioport.h>
211da177e4SLinus Torvalds #include <linux/cache.h>
221da177e4SLinus Torvalds #include <linux/slab.h>
231da177e4SLinus Torvalds #include "pci.h"
241da177e4SLinus Torvalds 
pci_std_update_resource(struct pci_dev * dev,int resno)256ffa2489SBjorn Helgaas static void pci_std_update_resource(struct pci_dev *dev, int resno)
261da177e4SLinus Torvalds {
271da177e4SLinus Torvalds 	struct pci_bus_region region;
289aac537eSBjorn Helgaas 	bool disable;
299aac537eSBjorn Helgaas 	u16 cmd;
301da177e4SLinus Torvalds 	u32 new, check, mask;
311da177e4SLinus Torvalds 	int reg;
3214add80bSYu Zhao 	struct resource *res = dev->resource + resno;
331da177e4SLinus Torvalds 
3463880b23SBjorn Helgaas 	/* Per SR-IOV spec 3.4.1.11, VF BARs are RO zero */
3563880b23SBjorn Helgaas 	if (dev->is_virtfn)
3670675e0bSWei Yang 		return;
3770675e0bSWei Yang 
38fb0f2b40SRalf Baechle 	/*
39fb0f2b40SRalf Baechle 	 * Ignore resources for unimplemented BARs and unused resource slots
40fb0f2b40SRalf Baechle 	 * for 64 bit BARs.
41fb0f2b40SRalf Baechle 	 */
42cf7bee5aSIvan Kokshaysky 	if (!res->flags)
43cf7bee5aSIvan Kokshaysky 		return;
44cf7bee5aSIvan Kokshaysky 
45cd8a4d36SBjorn Helgaas 	if (res->flags & IORESOURCE_UNSET)
46cd8a4d36SBjorn Helgaas 		return;
47cd8a4d36SBjorn Helgaas 
48fb0f2b40SRalf Baechle 	/*
49fb0f2b40SRalf Baechle 	 * Ignore non-moveable resources.  This might be legacy resources for
50fb0f2b40SRalf Baechle 	 * which no functional BAR register exists or another important
5180ccba11SBjorn Helgaas 	 * system resource we shouldn't move around.
52fb0f2b40SRalf Baechle 	 */
53fb0f2b40SRalf Baechle 	if (res->flags & IORESOURCE_PCI_FIXED)
54fb0f2b40SRalf Baechle 		return;
55fb0f2b40SRalf Baechle 
56fc279850SYinghai Lu 	pcibios_resource_to_bus(dev->bus, &region, res);
5745d004f4SBjorn Helgaas 	new = region.start;
581da177e4SLinus Torvalds 
5945d004f4SBjorn Helgaas 	if (res->flags & IORESOURCE_IO) {
601da177e4SLinus Torvalds 		mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
6145d004f4SBjorn Helgaas 		new |= res->flags & ~PCI_BASE_ADDRESS_IO_MASK;
6245d004f4SBjorn Helgaas 	} else if (resno == PCI_ROM_RESOURCE) {
6376dc5268SMatthias Kaehlcke 		mask = PCI_ROM_ADDRESS_MASK;
6445d004f4SBjorn Helgaas 	} else {
651da177e4SLinus Torvalds 		mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
6645d004f4SBjorn Helgaas 		new |= res->flags & ~PCI_BASE_ADDRESS_MEM_MASK;
6745d004f4SBjorn Helgaas 	}
681da177e4SLinus Torvalds 
69286c2378SBjorn Helgaas 	if (resno < PCI_ROM_RESOURCE) {
70286c2378SBjorn Helgaas 		reg = PCI_BASE_ADDRESS_0 + 4 * resno;
71286c2378SBjorn Helgaas 	} else if (resno == PCI_ROM_RESOURCE) {
720b457ddeSBjorn Helgaas 
730b457ddeSBjorn Helgaas 		/*
740b457ddeSBjorn Helgaas 		 * Apparently some Matrox devices have ROM BARs that read
750b457ddeSBjorn Helgaas 		 * as zero when disabled, so don't update ROM BARs unless
7616bbbc87SBjorn Helgaas 		 * they're enabled.  See
7716bbbc87SBjorn Helgaas 		 * https://lore.kernel.org/r/43147B3D.1030309@vc.cvut.cz/
78500b55b0SBjorn Helgaas 		 * But we must update ROM BAR for buggy devices where even a
79500b55b0SBjorn Helgaas 		 * disabled ROM can conflict with other BARs.
800b457ddeSBjorn Helgaas 		 */
81500b55b0SBjorn Helgaas 		if (!(res->flags & IORESOURCE_ROM_ENABLE) &&
82500b55b0SBjorn Helgaas 		    !dev->rom_bar_overlap)
83755528c8SLinus Torvalds 			return;
84286c2378SBjorn Helgaas 
85286c2378SBjorn Helgaas 		reg = dev->rom_base_reg;
86500b55b0SBjorn Helgaas 		if (res->flags & IORESOURCE_ROM_ENABLE)
87755528c8SLinus Torvalds 			new |= PCI_ROM_ADDRESS_ENABLE;
88286c2378SBjorn Helgaas 	} else
89286c2378SBjorn Helgaas 		return;
901da177e4SLinus Torvalds 
919aac537eSBjorn Helgaas 	/*
929aac537eSBjorn Helgaas 	 * We can't update a 64-bit BAR atomically, so when possible,
939aac537eSBjorn Helgaas 	 * disable decoding so that a half-updated BAR won't conflict
949aac537eSBjorn Helgaas 	 * with another device.
959aac537eSBjorn Helgaas 	 */
969aac537eSBjorn Helgaas 	disable = (res->flags & IORESOURCE_MEM_64) && !dev->mmio_always_on;
979aac537eSBjorn Helgaas 	if (disable) {
989aac537eSBjorn Helgaas 		pci_read_config_word(dev, PCI_COMMAND, &cmd);
999aac537eSBjorn Helgaas 		pci_write_config_word(dev, PCI_COMMAND,
1009aac537eSBjorn Helgaas 				      cmd & ~PCI_COMMAND_MEMORY);
1019aac537eSBjorn Helgaas 	}
1029aac537eSBjorn Helgaas 
1031da177e4SLinus Torvalds 	pci_write_config_dword(dev, reg, new);
1041da177e4SLinus Torvalds 	pci_read_config_dword(dev, reg, &check);
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds 	if ((new ^ check) & mask) {
107*62008578SBjorn Helgaas 		pci_err(dev, "BAR %d: error updating (%#010x != %#010x)\n",
10880ccba11SBjorn Helgaas 			resno, new, check);
1091da177e4SLinus Torvalds 	}
1101da177e4SLinus Torvalds 
11128c6821aSBjorn Helgaas 	if (res->flags & IORESOURCE_MEM_64) {
112cf7bee5aSIvan Kokshaysky 		new = region.start >> 16 >> 16;
1131da177e4SLinus Torvalds 		pci_write_config_dword(dev, reg + 4, new);
1141da177e4SLinus Torvalds 		pci_read_config_dword(dev, reg + 4, &check);
1151da177e4SLinus Torvalds 		if (check != new) {
116*62008578SBjorn Helgaas 			pci_err(dev, "BAR %d: error updating (high %#010x != %#010x)\n",
117227f0647SRyan Desfosses 				resno, new, check);
1181da177e4SLinus Torvalds 		}
1191da177e4SLinus Torvalds 	}
1209aac537eSBjorn Helgaas 
1219aac537eSBjorn Helgaas 	if (disable)
1229aac537eSBjorn Helgaas 		pci_write_config_word(dev, PCI_COMMAND, cmd);
1231da177e4SLinus Torvalds }
1241da177e4SLinus Torvalds 
pci_update_resource(struct pci_dev * dev,int resno)1256ffa2489SBjorn Helgaas void pci_update_resource(struct pci_dev *dev, int resno)
1266ffa2489SBjorn Helgaas {
1276ffa2489SBjorn Helgaas 	if (resno <= PCI_ROM_RESOURCE)
1286ffa2489SBjorn Helgaas 		pci_std_update_resource(dev, resno);
1296ffa2489SBjorn Helgaas #ifdef CONFIG_PCI_IOV
1306ffa2489SBjorn Helgaas 	else if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
1316ffa2489SBjorn Helgaas 		pci_iov_update_resource(dev, resno);
1326ffa2489SBjorn Helgaas #endif
1336ffa2489SBjorn Helgaas }
1346ffa2489SBjorn Helgaas 
pci_claim_resource(struct pci_dev * dev,int resource)13596bde06aSSam Ravnborg int pci_claim_resource(struct pci_dev *dev, int resource)
1361da177e4SLinus Torvalds {
1371da177e4SLinus Torvalds 	struct resource *res = &dev->resource[resource];
138966f3a75SBjorn Helgaas 	struct resource *root, *conflict;
1391da177e4SLinus Torvalds 
14029003bebSBjorn Helgaas 	if (res->flags & IORESOURCE_UNSET) {
1417506dc79SFrederick Lawler 		pci_info(dev, "can't claim BAR %d %pR: no address assigned\n",
14229003bebSBjorn Helgaas 			 resource, res);
14329003bebSBjorn Helgaas 		return -EINVAL;
14429003bebSBjorn Helgaas 	}
14529003bebSBjorn Helgaas 
14616d917b1SBjorn Helgaas 	/*
14716d917b1SBjorn Helgaas 	 * If we have a shadow copy in RAM, the PCI device doesn't respond
14816d917b1SBjorn Helgaas 	 * to the shadow range, so we don't need to claim it, and upstream
14916d917b1SBjorn Helgaas 	 * bridges don't need to route the range to the device.
15016d917b1SBjorn Helgaas 	 */
15116d917b1SBjorn Helgaas 	if (res->flags & IORESOURCE_ROM_SHADOW)
15216d917b1SBjorn Helgaas 		return 0;
15316d917b1SBjorn Helgaas 
154cebd78a8SMatthew Wilcox 	root = pci_find_parent_resource(dev, res);
155865df576SBjorn Helgaas 	if (!root) {
1567506dc79SFrederick Lawler 		pci_info(dev, "can't claim BAR %d %pR: no compatible bridge window\n",
15729003bebSBjorn Helgaas 			 resource, res);
158c770cb4cSBjorn Helgaas 		res->flags |= IORESOURCE_UNSET;
159865df576SBjorn Helgaas 		return -EINVAL;
1601da177e4SLinus Torvalds 	}
1611da177e4SLinus Torvalds 
162966f3a75SBjorn Helgaas 	conflict = request_resource_conflict(root, res);
163966f3a75SBjorn Helgaas 	if (conflict) {
1647506dc79SFrederick Lawler 		pci_info(dev, "can't claim BAR %d %pR: address conflict with %s %pR\n",
16529003bebSBjorn Helgaas 			 resource, res, conflict->name, conflict);
166c770cb4cSBjorn Helgaas 		res->flags |= IORESOURCE_UNSET;
167966f3a75SBjorn Helgaas 		return -EBUSY;
168966f3a75SBjorn Helgaas 	}
169865df576SBjorn Helgaas 
170966f3a75SBjorn Helgaas 	return 0;
1711da177e4SLinus Torvalds }
172eaa959dfSJesse Barnes EXPORT_SYMBOL(pci_claim_resource);
1731da177e4SLinus Torvalds 
pci_disable_bridge_window(struct pci_dev * dev)17432a9a682SYuji Shimada void pci_disable_bridge_window(struct pci_dev *dev)
17532a9a682SYuji Shimada {
17632a9a682SYuji Shimada 	/* MMIO Base/Limit */
17732a9a682SYuji Shimada 	pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0);
17832a9a682SYuji Shimada 
17932a9a682SYuji Shimada 	/* Prefetchable MMIO Base/Limit */
18032a9a682SYuji Shimada 	pci_write_config_dword(dev, PCI_PREF_LIMIT_UPPER32, 0);
18132a9a682SYuji Shimada 	pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0);
18232a9a682SYuji Shimada 	pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff);
18332a9a682SYuji Shimada }
1842bbc6942SRam Pai 
1856535943fSMyron Stowe /*
1866535943fSMyron Stowe  * Generic function that returns a value indicating that the device's
1876535943fSMyron Stowe  * original BIOS BAR address was not saved and so is not available for
1886535943fSMyron Stowe  * reinstatement.
1896535943fSMyron Stowe  *
1906535943fSMyron Stowe  * Can be over-ridden by architecture specific code that implements
1916535943fSMyron Stowe  * reinstatement functionality rather than leaving it disabled when
1926535943fSMyron Stowe  * normal allocation attempts fail.
1936535943fSMyron Stowe  */
pcibios_retrieve_fw_addr(struct pci_dev * dev,int idx)1946535943fSMyron Stowe resource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx)
1956535943fSMyron Stowe {
1966535943fSMyron Stowe 	return 0;
1976535943fSMyron Stowe }
1986535943fSMyron Stowe 
pci_revert_fw_address(struct resource * res,struct pci_dev * dev,int resno,resource_size_t size)1992bbc6942SRam Pai static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
2002bbc6942SRam Pai 		int resno, resource_size_t size)
2012bbc6942SRam Pai {
20258c84edaSBjorn Helgaas 	struct resource *root, *conflict;
2036535943fSMyron Stowe 	resource_size_t fw_addr, start, end;
20458c84edaSBjorn Helgaas 
2056535943fSMyron Stowe 	fw_addr = pcibios_retrieve_fw_addr(dev, resno);
2066535943fSMyron Stowe 	if (!fw_addr)
20794778835SBjorn Helgaas 		return -ENOMEM;
2086535943fSMyron Stowe 
20958c84edaSBjorn Helgaas 	start = res->start;
21058c84edaSBjorn Helgaas 	end = res->end;
2116535943fSMyron Stowe 	res->start = fw_addr;
21258c84edaSBjorn Helgaas 	res->end = res->start + size - 1;
2130b26cd69SBjorn Helgaas 	res->flags &= ~IORESOURCE_UNSET;
214351fc6d1SMyron Stowe 
215351fc6d1SMyron Stowe 	root = pci_find_parent_resource(dev, res);
216351fc6d1SMyron Stowe 	if (!root) {
2170e328183SMaciej W. Rozycki 		/*
2180e328183SMaciej W. Rozycki 		 * If dev is behind a bridge, accesses will only reach it
2190e328183SMaciej W. Rozycki 		 * if res is inside the relevant bridge window.
2200e328183SMaciej W. Rozycki 		 */
2210e328183SMaciej W. Rozycki 		if (pci_upstream_bridge(dev))
2220e328183SMaciej W. Rozycki 			return -ENXIO;
2230e328183SMaciej W. Rozycki 
2240e328183SMaciej W. Rozycki 		/*
2250e328183SMaciej W. Rozycki 		 * On the root bus, assume the host bridge will forward
2260e328183SMaciej W. Rozycki 		 * everything.
2270e328183SMaciej W. Rozycki 		 */
228351fc6d1SMyron Stowe 		if (res->flags & IORESOURCE_IO)
229351fc6d1SMyron Stowe 			root = &ioport_resource;
230351fc6d1SMyron Stowe 		else
231351fc6d1SMyron Stowe 			root = &iomem_resource;
232351fc6d1SMyron Stowe 	}
233351fc6d1SMyron Stowe 
2347506dc79SFrederick Lawler 	pci_info(dev, "BAR %d: trying firmware assignment %pR\n",
23558c84edaSBjorn Helgaas 		 resno, res);
23658c84edaSBjorn Helgaas 	conflict = request_resource_conflict(root, res);
23758c84edaSBjorn Helgaas 	if (conflict) {
2387506dc79SFrederick Lawler 		pci_info(dev, "BAR %d: %pR conflicts with %s %pR\n",
23994778835SBjorn Helgaas 			 resno, res, conflict->name, conflict);
24058c84edaSBjorn Helgaas 		res->start = start;
24158c84edaSBjorn Helgaas 		res->end = end;
2420b26cd69SBjorn Helgaas 		res->flags |= IORESOURCE_UNSET;
24394778835SBjorn Helgaas 		return -EBUSY;
24458c84edaSBjorn Helgaas 	}
24594778835SBjorn Helgaas 	return 0;
2461da177e4SLinus Torvalds }
2471da177e4SLinus Torvalds 
248ecf677c8SPalmer Dabbelt /*
249ecf677c8SPalmer Dabbelt  * We don't have to worry about legacy ISA devices, so nothing to do here.
250ecf677c8SPalmer Dabbelt  * This is marked as __weak because multiple architectures define it; it should
251ecf677c8SPalmer Dabbelt  * eventually go away.
252ecf677c8SPalmer Dabbelt  */
pcibios_align_resource(void * data,const struct resource * res,resource_size_t size,resource_size_t align)253ecf677c8SPalmer Dabbelt resource_size_t __weak pcibios_align_resource(void *data,
254ecf677c8SPalmer Dabbelt 					      const struct resource *res,
255ecf677c8SPalmer Dabbelt 					      resource_size_t size,
256ecf677c8SPalmer Dabbelt 					      resource_size_t align)
257ecf677c8SPalmer Dabbelt {
258ecf677c8SPalmer Dabbelt        return res->start;
259ecf677c8SPalmer Dabbelt }
260ecf677c8SPalmer Dabbelt 
__pci_assign_resource(struct pci_bus * bus,struct pci_dev * dev,int resno,resource_size_t size,resource_size_t align)261fe6dacdbSBjorn Helgaas static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
262fe6dacdbSBjorn Helgaas 		int resno, resource_size_t size, resource_size_t align)
263fe6dacdbSBjorn Helgaas {
264fe6dacdbSBjorn Helgaas 	struct resource *res = dev->resource + resno;
265fe6dacdbSBjorn Helgaas 	resource_size_t min;
266fe6dacdbSBjorn Helgaas 	int ret;
267fe6dacdbSBjorn Helgaas 
268fe6dacdbSBjorn Helgaas 	min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
269fe6dacdbSBjorn Helgaas 
27067d29b5cSBjorn Helgaas 	/*
27167d29b5cSBjorn Helgaas 	 * First, try exact prefetching match.  Even if a 64-bit
27267d29b5cSBjorn Helgaas 	 * prefetchable bridge window is below 4GB, we can't put a 32-bit
27367d29b5cSBjorn Helgaas 	 * prefetchable resource in it because pbus_size_mem() assumes a
27467d29b5cSBjorn Helgaas 	 * 64-bit window will contain no 32-bit resources.  If we assign
27567d29b5cSBjorn Helgaas 	 * things differently than they were sized, not everything will fit.
27667d29b5cSBjorn Helgaas 	 */
277fe6dacdbSBjorn Helgaas 	ret = pci_bus_alloc_resource(bus, res, size, align, min,
2785b285415SYinghai Lu 				     IORESOURCE_PREFETCH | IORESOURCE_MEM_64,
279fe6dacdbSBjorn Helgaas 				     pcibios_align_resource, dev);
280d3689df0SBjorn Helgaas 	if (ret == 0)
281d3689df0SBjorn Helgaas 		return 0;
282fe6dacdbSBjorn Helgaas 
28367d29b5cSBjorn Helgaas 	/*
28467d29b5cSBjorn Helgaas 	 * If the prefetchable window is only 32 bits wide, we can put
28567d29b5cSBjorn Helgaas 	 * 64-bit prefetchable resources in it.
28667d29b5cSBjorn Helgaas 	 */
287d3689df0SBjorn Helgaas 	if ((res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) ==
2885b285415SYinghai Lu 	     (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) {
289fe6dacdbSBjorn Helgaas 		ret = pci_bus_alloc_resource(bus, res, size, align, min,
290fe6dacdbSBjorn Helgaas 					     IORESOURCE_PREFETCH,
291fe6dacdbSBjorn Helgaas 					     pcibios_align_resource, dev);
292d3689df0SBjorn Helgaas 		if (ret == 0)
293d3689df0SBjorn Helgaas 			return 0;
2945b285415SYinghai Lu 	}
295fe6dacdbSBjorn Helgaas 
296fe6dacdbSBjorn Helgaas 	/*
29767d29b5cSBjorn Helgaas 	 * If we didn't find a better match, we can put any memory resource
29867d29b5cSBjorn Helgaas 	 * in a non-prefetchable window.  If this resource is 32 bits and
29967d29b5cSBjorn Helgaas 	 * non-prefetchable, the first call already tried the only possibility
30067d29b5cSBjorn Helgaas 	 * so we don't need to try again.
301fe6dacdbSBjorn Helgaas 	 */
30267d29b5cSBjorn Helgaas 	if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64))
303fe6dacdbSBjorn Helgaas 		ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
304fe6dacdbSBjorn Helgaas 					     pcibios_align_resource, dev);
30567d29b5cSBjorn Helgaas 
306fe6dacdbSBjorn Helgaas 	return ret;
307fe6dacdbSBjorn Helgaas }
308fe6dacdbSBjorn Helgaas 
_pci_assign_resource(struct pci_dev * dev,int resno,resource_size_t size,resource_size_t min_align)309d6776e6dSNikhil P Rao static int _pci_assign_resource(struct pci_dev *dev, int resno,
310d6776e6dSNikhil P Rao 				resource_size_t size, resource_size_t min_align)
311d09ee968SYinghai Lu {
312d09ee968SYinghai Lu 	struct pci_bus *bus;
313d09ee968SYinghai Lu 	int ret;
314d09ee968SYinghai Lu 
315d09ee968SYinghai Lu 	bus = dev->bus;
3162bbc6942SRam Pai 	while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) {
3172bbc6942SRam Pai 		if (!bus->parent || !bus->self->transparent)
318d09ee968SYinghai Lu 			break;
3192bbc6942SRam Pai 		bus = bus->parent;
320d09ee968SYinghai Lu 	}
321d09ee968SYinghai Lu 
322d09ee968SYinghai Lu 	return ret;
323d09ee968SYinghai Lu }
324d09ee968SYinghai Lu 
pci_assign_resource(struct pci_dev * dev,int resno)3252bbc6942SRam Pai int pci_assign_resource(struct pci_dev *dev, int resno)
3262bbc6942SRam Pai {
3272bbc6942SRam Pai 	struct resource *res = dev->resource + resno;
3282bbc6942SRam Pai 	resource_size_t align, size;
3292bbc6942SRam Pai 	int ret;
3302bbc6942SRam Pai 
3312ea4adf7SBjorn Helgaas 	if (res->flags & IORESOURCE_PCI_FIXED)
3322ea4adf7SBjorn Helgaas 		return 0;
3332ea4adf7SBjorn Helgaas 
334bd064f0aSBjorn Helgaas 	res->flags |= IORESOURCE_UNSET;
3352bbc6942SRam Pai 	align = pci_resource_alignment(dev, res);
3362bbc6942SRam Pai 	if (!align) {
3377506dc79SFrederick Lawler 		pci_info(dev, "BAR %d: can't assign %pR (bogus alignment)\n",
338227f0647SRyan Desfosses 			 resno, res);
3392bbc6942SRam Pai 		return -EINVAL;
3402bbc6942SRam Pai 	}
3412bbc6942SRam Pai 
3422bbc6942SRam Pai 	size = resource_size(res);
3432bbc6942SRam Pai 	ret = _pci_assign_resource(dev, resno, size, align);
3442bbc6942SRam Pai 
3452bbc6942SRam Pai 	/*
3462bbc6942SRam Pai 	 * If we failed to assign anything, let's try the address
3472bbc6942SRam Pai 	 * where firmware left it.  That at least has a chance of
3482bbc6942SRam Pai 	 * working, which is better than just leaving it disabled.
3492bbc6942SRam Pai 	 */
35064da465eSBjorn Helgaas 	if (ret < 0) {
3517506dc79SFrederick Lawler 		pci_info(dev, "BAR %d: no space for %pR\n", resno, res);
3522bbc6942SRam Pai 		ret = pci_revert_fw_address(res, dev, resno, size);
35364da465eSBjorn Helgaas 	}
3542bbc6942SRam Pai 
35564da465eSBjorn Helgaas 	if (ret < 0) {
3567506dc79SFrederick Lawler 		pci_info(dev, "BAR %d: failed to assign %pR\n", resno, res);
35728f6dbe2SBjorn Helgaas 		return ret;
35864da465eSBjorn Helgaas 	}
35928f6dbe2SBjorn Helgaas 
360bd064f0aSBjorn Helgaas 	res->flags &= ~IORESOURCE_UNSET;
3612bbc6942SRam Pai 	res->flags &= ~IORESOURCE_STARTALIGN;
3627506dc79SFrederick Lawler 	pci_info(dev, "BAR %d: assigned %pR\n", resno, res);
3632bbc6942SRam Pai 	if (resno < PCI_BRIDGE_RESOURCES)
3642bbc6942SRam Pai 		pci_update_resource(dev, resno);
36528f6dbe2SBjorn Helgaas 
36628f6dbe2SBjorn Helgaas 	return 0;
3672bbc6942SRam Pai }
368b7fe9434SRyan Desfosses EXPORT_SYMBOL(pci_assign_resource);
3692bbc6942SRam Pai 
pci_reassign_resource(struct pci_dev * dev,int resno,resource_size_t addsize,resource_size_t min_align)370fe6dacdbSBjorn Helgaas int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize,
371fe6dacdbSBjorn Helgaas 			resource_size_t min_align)
372fe6dacdbSBjorn Helgaas {
373fe6dacdbSBjorn Helgaas 	struct resource *res = dev->resource + resno;
374c3337708SGuo Chao 	unsigned long flags;
375fe6dacdbSBjorn Helgaas 	resource_size_t new_size;
376fe6dacdbSBjorn Helgaas 	int ret;
377fe6dacdbSBjorn Helgaas 
3782ea4adf7SBjorn Helgaas 	if (res->flags & IORESOURCE_PCI_FIXED)
3792ea4adf7SBjorn Helgaas 		return 0;
3802ea4adf7SBjorn Helgaas 
381c3337708SGuo Chao 	flags = res->flags;
382bd064f0aSBjorn Helgaas 	res->flags |= IORESOURCE_UNSET;
383fe6dacdbSBjorn Helgaas 	if (!res->parent) {
3847506dc79SFrederick Lawler 		pci_info(dev, "BAR %d: can't reassign an unassigned resource %pR\n",
385227f0647SRyan Desfosses 			 resno, res);
386fe6dacdbSBjorn Helgaas 		return -EINVAL;
387fe6dacdbSBjorn Helgaas 	}
388fe6dacdbSBjorn Helgaas 
389fe6dacdbSBjorn Helgaas 	/* already aligned with min_align */
390fe6dacdbSBjorn Helgaas 	new_size = resource_size(res) + addsize;
391fe6dacdbSBjorn Helgaas 	ret = _pci_assign_resource(dev, resno, new_size, min_align);
39228f6dbe2SBjorn Helgaas 	if (ret) {
39328f6dbe2SBjorn Helgaas 		res->flags = flags;
3947506dc79SFrederick Lawler 		pci_info(dev, "BAR %d: %pR (failed to expand by %#llx)\n",
39528f6dbe2SBjorn Helgaas 			 resno, res, (unsigned long long) addsize);
39628f6dbe2SBjorn Helgaas 		return ret;
39728f6dbe2SBjorn Helgaas 	}
39828f6dbe2SBjorn Helgaas 
399bd064f0aSBjorn Helgaas 	res->flags &= ~IORESOURCE_UNSET;
400fe6dacdbSBjorn Helgaas 	res->flags &= ~IORESOURCE_STARTALIGN;
4017506dc79SFrederick Lawler 	pci_info(dev, "BAR %d: reassigned %pR (expanded by %#llx)\n",
40264da465eSBjorn Helgaas 		 resno, res, (unsigned long long) addsize);
403fe6dacdbSBjorn Helgaas 	if (resno < PCI_BRIDGE_RESOURCES)
404fe6dacdbSBjorn Helgaas 		pci_update_resource(dev, resno);
405c3337708SGuo Chao 
40628f6dbe2SBjorn Helgaas 	return 0;
407fe6dacdbSBjorn Helgaas }
408fe6dacdbSBjorn Helgaas 
pci_release_resource(struct pci_dev * dev,int resno)4098bb705e3SChristian König void pci_release_resource(struct pci_dev *dev, int resno)
4108bb705e3SChristian König {
4118bb705e3SChristian König 	struct resource *res = dev->resource + resno;
4128bb705e3SChristian König 
4137506dc79SFrederick Lawler 	pci_info(dev, "BAR %d: releasing %pR\n", resno, res);
414c37406e0SChristian König 
415c37406e0SChristian König 	if (!res->parent)
416c37406e0SChristian König 		return;
417c37406e0SChristian König 
4188bb705e3SChristian König 	release_resource(res);
4198bb705e3SChristian König 	res->end = resource_size(res) - 1;
4208bb705e3SChristian König 	res->start = 0;
4218bb705e3SChristian König 	res->flags |= IORESOURCE_UNSET;
4228bb705e3SChristian König }
4238bb705e3SChristian König EXPORT_SYMBOL(pci_release_resource);
4248bb705e3SChristian König 
pci_resize_resource(struct pci_dev * dev,int resno,int size)4258bb705e3SChristian König int pci_resize_resource(struct pci_dev *dev, int resno, int size)
4268bb705e3SChristian König {
4278bb705e3SChristian König 	struct resource *res = dev->resource + resno;
428729e3a66SArd Biesheuvel 	struct pci_host_bridge *host;
4298bb705e3SChristian König 	int old, ret;
4308bb705e3SChristian König 	u32 sizes;
4318bb705e3SChristian König 	u16 cmd;
4328bb705e3SChristian König 
433729e3a66SArd Biesheuvel 	/* Check if we must preserve the firmware's resource assignment */
434729e3a66SArd Biesheuvel 	host = pci_find_host_bridge(dev->bus);
435729e3a66SArd Biesheuvel 	if (host->preserve_config)
436729e3a66SArd Biesheuvel 		return -ENOTSUPP;
437729e3a66SArd Biesheuvel 
4388bb705e3SChristian König 	/* Make sure the resource isn't assigned before resizing it. */
4398bb705e3SChristian König 	if (!(res->flags & IORESOURCE_UNSET))
4408bb705e3SChristian König 		return -EBUSY;
4418bb705e3SChristian König 
4428bb705e3SChristian König 	pci_read_config_word(dev, PCI_COMMAND, &cmd);
4438bb705e3SChristian König 	if (cmd & PCI_COMMAND_MEMORY)
4448bb705e3SChristian König 		return -EBUSY;
4458bb705e3SChristian König 
4468bb705e3SChristian König 	sizes = pci_rebar_get_possible_sizes(dev, resno);
4478bb705e3SChristian König 	if (!sizes)
4488bb705e3SChristian König 		return -ENOTSUPP;
4498bb705e3SChristian König 
4508bb705e3SChristian König 	if (!(sizes & BIT(size)))
4518bb705e3SChristian König 		return -EINVAL;
4528bb705e3SChristian König 
4538bb705e3SChristian König 	old = pci_rebar_get_current_size(dev, resno);
4548bb705e3SChristian König 	if (old < 0)
4558bb705e3SChristian König 		return old;
4568bb705e3SChristian König 
4578bb705e3SChristian König 	ret = pci_rebar_set_size(dev, resno, size);
4588bb705e3SChristian König 	if (ret)
4598bb705e3SChristian König 		return ret;
4608bb705e3SChristian König 
4618bb705e3SChristian König 	res->end = res->start + pci_rebar_size_to_bytes(size) - 1;
4628bb705e3SChristian König 
4638bb705e3SChristian König 	/* Check if the new config works by trying to assign everything. */
464d09ddd81SArd Biesheuvel 	if (dev->bus->self) {
4658bb705e3SChristian König 		ret = pci_reassign_bridge_resources(dev->bus->self, res->flags);
4668bb705e3SChristian König 		if (ret)
4678bb705e3SChristian König 			goto error_resize;
468d09ddd81SArd Biesheuvel 	}
4698bb705e3SChristian König 	return 0;
4708bb705e3SChristian König 
4718bb705e3SChristian König error_resize:
4728bb705e3SChristian König 	pci_rebar_set_size(dev, resno, old);
4738bb705e3SChristian König 	res->end = res->start + pci_rebar_size_to_bytes(old) - 1;
4748bb705e3SChristian König 	return ret;
4758bb705e3SChristian König }
4768bb705e3SChristian König EXPORT_SYMBOL(pci_resize_resource);
4778bb705e3SChristian König 
pci_enable_resources(struct pci_dev * dev,int mask)478842de40dSBjorn Helgaas int pci_enable_resources(struct pci_dev *dev, int mask)
479842de40dSBjorn Helgaas {
480842de40dSBjorn Helgaas 	u16 cmd, old_cmd;
481842de40dSBjorn Helgaas 	int i;
482842de40dSBjorn Helgaas 	struct resource *r;
483842de40dSBjorn Helgaas 
484842de40dSBjorn Helgaas 	pci_read_config_word(dev, PCI_COMMAND, &cmd);
485842de40dSBjorn Helgaas 	old_cmd = cmd;
486842de40dSBjorn Helgaas 
48709cc9006SMika Westerberg 	pci_dev_for_each_resource(dev, r, i) {
488842de40dSBjorn Helgaas 		if (!(mask & (1 << i)))
489842de40dSBjorn Helgaas 			continue;
490842de40dSBjorn Helgaas 
491842de40dSBjorn Helgaas 		if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
492842de40dSBjorn Helgaas 			continue;
493842de40dSBjorn Helgaas 		if ((i == PCI_ROM_RESOURCE) &&
494842de40dSBjorn Helgaas 				(!(r->flags & IORESOURCE_ROM_ENABLE)))
495842de40dSBjorn Helgaas 			continue;
496842de40dSBjorn Helgaas 
4973cedcc36SBjorn Helgaas 		if (r->flags & IORESOURCE_UNSET) {
4987506dc79SFrederick Lawler 			pci_err(dev, "can't enable device: BAR %d %pR not assigned\n",
4993cedcc36SBjorn Helgaas 				i, r);
5003cedcc36SBjorn Helgaas 			return -EINVAL;
5013cedcc36SBjorn Helgaas 		}
5023cedcc36SBjorn Helgaas 
503842de40dSBjorn Helgaas 		if (!r->parent) {
5047506dc79SFrederick Lawler 			pci_err(dev, "can't enable device: BAR %d %pR not claimed\n",
5053cedcc36SBjorn Helgaas 				i, r);
506842de40dSBjorn Helgaas 			return -EINVAL;
507842de40dSBjorn Helgaas 		}
508842de40dSBjorn Helgaas 
509842de40dSBjorn Helgaas 		if (r->flags & IORESOURCE_IO)
510842de40dSBjorn Helgaas 			cmd |= PCI_COMMAND_IO;
511842de40dSBjorn Helgaas 		if (r->flags & IORESOURCE_MEM)
512842de40dSBjorn Helgaas 			cmd |= PCI_COMMAND_MEMORY;
513842de40dSBjorn Helgaas 	}
514842de40dSBjorn Helgaas 
515842de40dSBjorn Helgaas 	if (cmd != old_cmd) {
5167506dc79SFrederick Lawler 		pci_info(dev, "enabling device (%04x -> %04x)\n", old_cmd, cmd);
517842de40dSBjorn Helgaas 		pci_write_config_word(dev, PCI_COMMAND, cmd);
518842de40dSBjorn Helgaas 	}
519842de40dSBjorn Helgaas 	return 0;
520842de40dSBjorn Helgaas }
521