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, ®ion, 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