2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* drivers/pci/setup-res.c
|
|
|
|
*
|
|
|
|
* Extruded from code written by
|
|
|
|
* Dave Rusling (david.rusling@reo.mts.dec.com)
|
|
|
|
* David Mosberger (davidm@cs.arizona.edu)
|
|
|
|
* David Miller (davem@redhat.com)
|
|
|
|
*
|
|
|
|
* Support routines for initializing a PCI subsystem.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* fixed for multiple pci buses, 1999 Andrea Arcangeli <andrea@suse.de> */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
|
|
|
|
* Resource sorting
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
2011-05-27 13:37:25 +00:00
|
|
|
#include <linux/export.h>
|
2005-04-16 22:20:36 +00:00
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/cache.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include "pci.h"
|
|
|
|
|
|
|
|
|
2008-11-21 18:38:52 +00:00
|
|
|
void pci_update_resource(struct pci_dev *dev, int resno)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct pci_bus_region region;
|
2012-07-10 01:49:37 +00:00
|
|
|
bool disable;
|
|
|
|
u16 cmd;
|
2005-04-16 22:20:36 +00:00
|
|
|
u32 new, check, mask;
|
|
|
|
int reg;
|
2008-11-21 18:41:27 +00:00
|
|
|
enum pci_bar_type type;
|
2008-11-21 18:38:52 +00:00
|
|
|
struct resource *res = dev->resource + resno;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2006-12-19 21:12:08 +00:00
|
|
|
/*
|
|
|
|
* Ignore resources for unimplemented BARs and unused resource slots
|
|
|
|
* for 64 bit BARs.
|
|
|
|
*/
|
2005-08-07 09:49:59 +00:00
|
|
|
if (!res->flags)
|
|
|
|
return;
|
|
|
|
|
2014-02-26 18:25:59 +00:00
|
|
|
if (res->flags & IORESOURCE_UNSET)
|
|
|
|
return;
|
|
|
|
|
2006-12-19 21:12:08 +00:00
|
|
|
/*
|
|
|
|
* Ignore non-moveable resources. This might be legacy resources for
|
|
|
|
* which no functional BAR register exists or another important
|
2008-06-13 16:52:11 +00:00
|
|
|
* system resource we shouldn't move around.
|
2006-12-19 21:12:08 +00:00
|
|
|
*/
|
|
|
|
if (res->flags & IORESOURCE_PCI_FIXED)
|
|
|
|
return;
|
|
|
|
|
PCI: Convert pcibios_resource_to_bus() to take a pci_bus, not a pci_dev
These interfaces:
pcibios_resource_to_bus(struct pci_dev *dev, *bus_region, *resource)
pcibios_bus_to_resource(struct pci_dev *dev, *resource, *bus_region)
took a pci_dev, but they really depend only on the pci_bus. And we want to
use them in resource allocation paths where we have the bus but not a
device, so this patch converts them to take the pci_bus instead of the
pci_dev:
pcibios_resource_to_bus(struct pci_bus *bus, *bus_region, *resource)
pcibios_bus_to_resource(struct pci_bus *bus, *resource, *bus_region)
In fact, with standard PCI-PCI bridges, they only depend on the host
bridge, because that's the only place address translation occurs, but
we aren't going that far yet.
[bhelgaas: changelog]
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2013-12-10 06:54:40 +00:00
|
|
|
pcibios_resource_to_bus(dev->bus, ®ion, res);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
|
|
|
|
if (res->flags & IORESOURCE_IO)
|
|
|
|
mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
|
|
|
|
else
|
|
|
|
mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
|
|
|
|
|
2008-11-21 18:41:27 +00:00
|
|
|
reg = pci_resource_bar(dev, resno, &type);
|
|
|
|
if (!reg)
|
|
|
|
return;
|
|
|
|
if (type != pci_bar_unknown) {
|
2005-08-26 17:49:22 +00:00
|
|
|
if (!(res->flags & IORESOURCE_ROM_ENABLE))
|
|
|
|
return;
|
|
|
|
new |= PCI_ROM_ADDRESS_ENABLE;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2012-07-10 01:49:37 +00:00
|
|
|
/*
|
|
|
|
* We can't update a 64-bit BAR atomically, so when possible,
|
|
|
|
* disable decoding so that a half-updated BAR won't conflict
|
|
|
|
* with another device.
|
|
|
|
*/
|
|
|
|
disable = (res->flags & IORESOURCE_MEM_64) && !dev->mmio_always_on;
|
|
|
|
if (disable) {
|
|
|
|
pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
|
|
|
pci_write_config_word(dev, PCI_COMMAND,
|
|
|
|
cmd & ~PCI_COMMAND_MEMORY);
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
pci_write_config_dword(dev, reg, new);
|
|
|
|
pci_read_config_dword(dev, reg, &check);
|
|
|
|
|
|
|
|
if ((new ^ check) & mask) {
|
2008-06-13 16:52:11 +00:00
|
|
|
dev_err(&dev->dev, "BAR %d: error updating (%#08x != %#08x)\n",
|
|
|
|
resno, new, check);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2011-06-14 19:04:35 +00:00
|
|
|
if (res->flags & IORESOURCE_MEM_64) {
|
2005-08-07 09:49:59 +00:00
|
|
|
new = region.start >> 16 >> 16;
|
2005-04-16 22:20:36 +00:00
|
|
|
pci_write_config_dword(dev, reg + 4, new);
|
|
|
|
pci_read_config_dword(dev, reg + 4, &check);
|
|
|
|
if (check != new) {
|
2014-04-19 00:13:50 +00:00
|
|
|
dev_err(&dev->dev, "BAR %d: error updating (high %#08x != %#08x)\n",
|
|
|
|
resno, new, check);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2012-07-10 01:49:37 +00:00
|
|
|
|
|
|
|
if (disable)
|
|
|
|
pci_write_config_word(dev, PCI_COMMAND, cmd);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2007-03-27 05:53:30 +00:00
|
|
|
int pci_claim_resource(struct pci_dev *dev, int resource)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
struct resource *res = &dev->resource[resource];
|
2010-03-12 00:01:19 +00:00
|
|
|
struct resource *root, *conflict;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2014-02-26 18:25:59 +00:00
|
|
|
if (res->flags & IORESOURCE_UNSET) {
|
|
|
|
dev_info(&dev->dev, "can't claim BAR %d %pR: no address assigned\n",
|
|
|
|
resource, res);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2009-06-17 20:33:33 +00:00
|
|
|
root = pci_find_parent_resource(dev, res);
|
2009-11-04 17:32:57 +00:00
|
|
|
if (!root) {
|
2014-02-26 18:25:59 +00:00
|
|
|
dev_info(&dev->dev, "can't claim BAR %d %pR: no compatible bridge window\n",
|
|
|
|
resource, res);
|
2015-03-12 17:30:06 +00:00
|
|
|
res->flags |= IORESOURCE_UNSET;
|
2009-11-04 17:32:57 +00:00
|
|
|
return -EINVAL;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2010-03-12 00:01:19 +00:00
|
|
|
conflict = request_resource_conflict(root, res);
|
|
|
|
if (conflict) {
|
2014-02-26 18:25:59 +00:00
|
|
|
dev_info(&dev->dev, "can't claim BAR %d %pR: address conflict with %s %pR\n",
|
|
|
|
resource, res, conflict->name, conflict);
|
2015-03-12 17:30:06 +00:00
|
|
|
res->flags |= IORESOURCE_UNSET;
|
2010-03-12 00:01:19 +00:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
2009-11-04 17:32:57 +00:00
|
|
|
|
2010-03-12 00:01:19 +00:00
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2009-07-01 04:45:44 +00:00
|
|
|
EXPORT_SYMBOL(pci_claim_resource);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2009-03-16 08:13:39 +00:00
|
|
|
void pci_disable_bridge_window(struct pci_dev *dev)
|
|
|
|
{
|
2009-11-04 17:32:57 +00:00
|
|
|
dev_info(&dev->dev, "disabling bridge mem windows\n");
|
2009-03-16 08:13:39 +00:00
|
|
|
|
|
|
|
/* MMIO Base/Limit */
|
|
|
|
pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0);
|
|
|
|
|
|
|
|
/* Prefetchable MMIO Base/Limit */
|
|
|
|
pci_write_config_dword(dev, PCI_PREF_LIMIT_UPPER32, 0);
|
|
|
|
pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0);
|
|
|
|
pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff);
|
|
|
|
}
|
2011-07-25 20:08:39 +00:00
|
|
|
|
2011-11-21 18:54:19 +00:00
|
|
|
/*
|
|
|
|
* Generic function that returns a value indicating that the device's
|
|
|
|
* original BIOS BAR address was not saved and so is not available for
|
|
|
|
* reinstatement.
|
|
|
|
*
|
|
|
|
* Can be over-ridden by architecture specific code that implements
|
|
|
|
* reinstatement functionality rather than leaving it disabled when
|
|
|
|
* normal allocation attempts fail.
|
|
|
|
*/
|
|
|
|
resource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-14 18:28:18 +00:00
|
|
|
static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
|
2011-07-25 20:08:39 +00:00
|
|
|
int resno, resource_size_t size)
|
|
|
|
{
|
|
|
|
struct resource *root, *conflict;
|
2011-11-21 18:54:19 +00:00
|
|
|
resource_size_t fw_addr, start, end;
|
PCI: fall back to original BIOS BAR addresses
If we fail to assign resources to a PCI BAR, this patch makes us try the
original address from BIOS rather than leaving it disabled.
Linux tries to make sure all PCI device BARs are inside the upstream
PCI host bridge or P2P bridge apertures, reassigning BARs if necessary.
Windows does similar reassignment.
Before this patch, if we could not move a BAR into an aperture, we left
the resource unassigned, i.e., at address zero. Windows leaves such BARs
at the original BIOS addresses, and this patch makes Linux do the same.
This is a bit ugly because we disable the resource long before we try to
reassign it, so we have to keep track of the BIOS BAR address somewhere.
For lack of a better place, I put it in the struct pci_dev.
I think it would be cleaner to attempt the assignment immediately when the
claim fails, so we could easily remember the original address. But we
currently claim motherboard resources in the middle, after attempting to
claim PCI resources and before assigning new PCI resources, and changing
that is a fairly big job.
Addresses https://bugzilla.kernel.org/show_bug.cgi?id=16263
Reported-by: Andrew <nitr0@seti.kr.ua>
Tested-by: Andrew <nitr0@seti.kr.ua>
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2010-07-15 15:41:42 +00:00
|
|
|
|
2011-11-21 18:54:19 +00:00
|
|
|
fw_addr = pcibios_retrieve_fw_addr(dev, resno);
|
|
|
|
if (!fw_addr)
|
2014-07-08 22:00:42 +00:00
|
|
|
return -ENOMEM;
|
2011-11-21 18:54:19 +00:00
|
|
|
|
2011-07-25 20:08:39 +00:00
|
|
|
start = res->start;
|
|
|
|
end = res->end;
|
2011-11-21 18:54:19 +00:00
|
|
|
res->start = fw_addr;
|
2011-07-25 20:08:39 +00:00
|
|
|
res->end = res->start + size - 1;
|
2011-11-21 18:54:07 +00:00
|
|
|
|
|
|
|
root = pci_find_parent_resource(dev, res);
|
|
|
|
if (!root) {
|
|
|
|
if (res->flags & IORESOURCE_IO)
|
|
|
|
root = &ioport_resource;
|
|
|
|
else
|
|
|
|
root = &iomem_resource;
|
|
|
|
}
|
|
|
|
|
2011-07-25 20:08:39 +00:00
|
|
|
dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n",
|
|
|
|
resno, res);
|
|
|
|
conflict = request_resource_conflict(root, res);
|
|
|
|
if (conflict) {
|
2014-07-08 22:00:42 +00:00
|
|
|
dev_info(&dev->dev, "BAR %d: %pR conflicts with %s %pR\n",
|
|
|
|
resno, res, conflict->name, conflict);
|
2011-07-25 20:08:39 +00:00
|
|
|
res->start = start;
|
|
|
|
res->end = end;
|
2014-07-08 22:00:42 +00:00
|
|
|
return -EBUSY;
|
2011-07-25 20:08:39 +00:00
|
|
|
}
|
2014-07-08 22:00:42 +00:00
|
|
|
return 0;
|
2011-07-25 20:08:39 +00:00
|
|
|
}
|
|
|
|
|
2012-07-11 23:05:43 +00:00
|
|
|
static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
|
|
|
|
int resno, resource_size_t size, resource_size_t align)
|
|
|
|
{
|
|
|
|
struct resource *res = dev->resource + resno;
|
|
|
|
resource_size_t min;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
|
|
|
|
|
2014-05-20 00:32:18 +00:00
|
|
|
/*
|
|
|
|
* First, try exact prefetching match. Even if a 64-bit
|
|
|
|
* prefetchable bridge window is below 4GB, we can't put a 32-bit
|
|
|
|
* prefetchable resource in it because pbus_size_mem() assumes a
|
|
|
|
* 64-bit window will contain no 32-bit resources. If we assign
|
|
|
|
* things differently than they were sized, not everything will fit.
|
|
|
|
*/
|
2012-07-11 23:05:43 +00:00
|
|
|
ret = pci_bus_alloc_resource(bus, res, size, align, min,
|
PCI: Restrict 64-bit prefetchable bridge windows to 64-bit resources
This patch changes the way we handle 64-bit prefetchable bridge windows to
make it more likely that we can assign space to all devices.
Previously we put all prefetchable resources in the prefetchable bridge
window. If any of those resources was 32-bit only, we restricted the
window to be below 4GB.
After this patch, we only put 64-bit prefetchable resources in a 64-bit
prefetchable window. We put all 32-bit prefetchable resources in the
non-prefetchable window, even if there are no 64-bit prefetchable
resources.
With the previous approach, if there was a 32-bit prefetchable resource
behind a bridge, we forced the bridge's prefetchable window below 4GB,
which meant that even if there was plenty of space above 4GB available, we
couldn't use it, and assignment of large 64-bit resources could fail, as
in the bugzilla below.
The new strategy is:
1) If the prefetchable window is 64 bits wide, we put only 64-bit
prefetchable resources in it. Any 32-bit prefetchable resources go in
the non-prefetchable window.
2) If the prefetchable window is 32 bits wide, we put both 32- and 64-bit
prefetchable resources in it.
3) If there is no prefetchable window, all MMIO resources go in the
non-prefetchable window.
This reduces performance for 32-bit prefetchable resources below a bridge
with a 64-bit prefetchable window. We previously assigned prefetchable
space, but now we'll assign non-prefetchable space. This is the case even
if there are no 64-bit prefetchable resources, or if they would all fit
below 4GB. In those cases, the old strategy would work and would have
better performance.
[bhelgaas: write changelog, add bugzilla link, fold in mem64_mask removal]
Link: https://bugzilla.kernel.org/show_bug.cgi?id=74151
Tested-by: Guo Chao <yan@linux.vnet.ibm.com>
Tested-by: Wei Yang <weiyang@linux.vnet.ibm.com>
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2014-05-19 23:01:55 +00:00
|
|
|
IORESOURCE_PREFETCH | IORESOURCE_MEM_64,
|
2012-07-11 23:05:43 +00:00
|
|
|
pcibios_align_resource, dev);
|
2014-05-20 00:39:07 +00:00
|
|
|
if (ret == 0)
|
|
|
|
return 0;
|
2012-07-11 23:05:43 +00:00
|
|
|
|
2014-05-20 00:32:18 +00:00
|
|
|
/*
|
|
|
|
* If the prefetchable window is only 32 bits wide, we can put
|
|
|
|
* 64-bit prefetchable resources in it.
|
|
|
|
*/
|
2014-05-20 00:39:07 +00:00
|
|
|
if ((res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) ==
|
PCI: Restrict 64-bit prefetchable bridge windows to 64-bit resources
This patch changes the way we handle 64-bit prefetchable bridge windows to
make it more likely that we can assign space to all devices.
Previously we put all prefetchable resources in the prefetchable bridge
window. If any of those resources was 32-bit only, we restricted the
window to be below 4GB.
After this patch, we only put 64-bit prefetchable resources in a 64-bit
prefetchable window. We put all 32-bit prefetchable resources in the
non-prefetchable window, even if there are no 64-bit prefetchable
resources.
With the previous approach, if there was a 32-bit prefetchable resource
behind a bridge, we forced the bridge's prefetchable window below 4GB,
which meant that even if there was plenty of space above 4GB available, we
couldn't use it, and assignment of large 64-bit resources could fail, as
in the bugzilla below.
The new strategy is:
1) If the prefetchable window is 64 bits wide, we put only 64-bit
prefetchable resources in it. Any 32-bit prefetchable resources go in
the non-prefetchable window.
2) If the prefetchable window is 32 bits wide, we put both 32- and 64-bit
prefetchable resources in it.
3) If there is no prefetchable window, all MMIO resources go in the
non-prefetchable window.
This reduces performance for 32-bit prefetchable resources below a bridge
with a 64-bit prefetchable window. We previously assigned prefetchable
space, but now we'll assign non-prefetchable space. This is the case even
if there are no 64-bit prefetchable resources, or if they would all fit
below 4GB. In those cases, the old strategy would work and would have
better performance.
[bhelgaas: write changelog, add bugzilla link, fold in mem64_mask removal]
Link: https://bugzilla.kernel.org/show_bug.cgi?id=74151
Tested-by: Guo Chao <yan@linux.vnet.ibm.com>
Tested-by: Wei Yang <weiyang@linux.vnet.ibm.com>
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2014-05-19 23:01:55 +00:00
|
|
|
(IORESOURCE_PREFETCH | IORESOURCE_MEM_64)) {
|
|
|
|
ret = pci_bus_alloc_resource(bus, res, size, align, min,
|
|
|
|
IORESOURCE_PREFETCH,
|
2012-07-11 23:05:43 +00:00
|
|
|
pcibios_align_resource, dev);
|
2014-05-20 00:39:07 +00:00
|
|
|
if (ret == 0)
|
|
|
|
return 0;
|
2012-07-11 23:05:43 +00:00
|
|
|
}
|
PCI: Restrict 64-bit prefetchable bridge windows to 64-bit resources
This patch changes the way we handle 64-bit prefetchable bridge windows to
make it more likely that we can assign space to all devices.
Previously we put all prefetchable resources in the prefetchable bridge
window. If any of those resources was 32-bit only, we restricted the
window to be below 4GB.
After this patch, we only put 64-bit prefetchable resources in a 64-bit
prefetchable window. We put all 32-bit prefetchable resources in the
non-prefetchable window, even if there are no 64-bit prefetchable
resources.
With the previous approach, if there was a 32-bit prefetchable resource
behind a bridge, we forced the bridge's prefetchable window below 4GB,
which meant that even if there was plenty of space above 4GB available, we
couldn't use it, and assignment of large 64-bit resources could fail, as
in the bugzilla below.
The new strategy is:
1) If the prefetchable window is 64 bits wide, we put only 64-bit
prefetchable resources in it. Any 32-bit prefetchable resources go in
the non-prefetchable window.
2) If the prefetchable window is 32 bits wide, we put both 32- and 64-bit
prefetchable resources in it.
3) If there is no prefetchable window, all MMIO resources go in the
non-prefetchable window.
This reduces performance for 32-bit prefetchable resources below a bridge
with a 64-bit prefetchable window. We previously assigned prefetchable
space, but now we'll assign non-prefetchable space. This is the case even
if there are no 64-bit prefetchable resources, or if they would all fit
below 4GB. In those cases, the old strategy would work and would have
better performance.
[bhelgaas: write changelog, add bugzilla link, fold in mem64_mask removal]
Link: https://bugzilla.kernel.org/show_bug.cgi?id=74151
Tested-by: Guo Chao <yan@linux.vnet.ibm.com>
Tested-by: Wei Yang <weiyang@linux.vnet.ibm.com>
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2014-05-19 23:01:55 +00:00
|
|
|
|
2014-05-20 00:32:18 +00:00
|
|
|
/*
|
|
|
|
* If we didn't find a better match, we can put any memory resource
|
|
|
|
* in a non-prefetchable window. If this resource is 32 bits and
|
|
|
|
* non-prefetchable, the first call already tried the only possibility
|
|
|
|
* so we don't need to try again.
|
|
|
|
*/
|
|
|
|
if (res->flags & (IORESOURCE_PREFETCH | IORESOURCE_MEM_64))
|
2012-07-11 23:05:43 +00:00
|
|
|
ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
|
|
|
|
pcibios_align_resource, dev);
|
2014-05-20 00:32:18 +00:00
|
|
|
|
2012-07-11 23:05:43 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-06-20 19:56:00 +00:00
|
|
|
static int _pci_assign_resource(struct pci_dev *dev, int resno,
|
|
|
|
resource_size_t size, resource_size_t min_align)
|
2011-07-25 20:08:39 +00:00
|
|
|
{
|
|
|
|
struct pci_bus *bus;
|
|
|
|
int ret;
|
PCI: fall back to original BIOS BAR addresses
If we fail to assign resources to a PCI BAR, this patch makes us try the
original address from BIOS rather than leaving it disabled.
Linux tries to make sure all PCI device BARs are inside the upstream
PCI host bridge or P2P bridge apertures, reassigning BARs if necessary.
Windows does similar reassignment.
Before this patch, if we could not move a BAR into an aperture, we left
the resource unassigned, i.e., at address zero. Windows leaves such BARs
at the original BIOS addresses, and this patch makes Linux do the same.
This is a bit ugly because we disable the resource long before we try to
reassign it, so we have to keep track of the BIOS BAR address somewhere.
For lack of a better place, I put it in the struct pci_dev.
I think it would be cleaner to attempt the assignment immediately when the
claim fails, so we could easily remember the original address. But we
currently claim motherboard resources in the middle, after attempting to
claim PCI resources and before assigning new PCI resources, and changing
that is a fairly big job.
Addresses https://bugzilla.kernel.org/show_bug.cgi?id=16263
Reported-by: Andrew <nitr0@seti.kr.ua>
Tested-by: Andrew <nitr0@seti.kr.ua>
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
2010-07-15 15:41:42 +00:00
|
|
|
|
2011-07-25 20:08:39 +00:00
|
|
|
bus = dev->bus;
|
|
|
|
while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) {
|
|
|
|
if (!bus->parent || !bus->self->transparent)
|
|
|
|
break;
|
|
|
|
bus = bus->parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-04-24 03:49:25 +00:00
|
|
|
int pci_assign_resource(struct pci_dev *dev, int resno)
|
|
|
|
{
|
|
|
|
struct resource *res = dev->resource + resno;
|
2011-07-25 20:08:39 +00:00
|
|
|
resource_size_t align, size;
|
2009-04-24 03:49:25 +00:00
|
|
|
int ret;
|
|
|
|
|
2014-02-26 18:25:58 +00:00
|
|
|
res->flags |= IORESOURCE_UNSET;
|
2009-08-28 20:00:06 +00:00
|
|
|
align = pci_resource_alignment(dev, res);
|
2009-04-24 03:49:25 +00:00
|
|
|
if (!align) {
|
2014-04-19 00:13:50 +00:00
|
|
|
dev_info(&dev->dev, "BAR %d: can't assign %pR (bogus alignment)\n",
|
|
|
|
resno, res);
|
2009-04-24 03:49:25 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2011-07-25 20:08:39 +00:00
|
|
|
size = resource_size(res);
|
|
|
|
ret = _pci_assign_resource(dev, resno, size, align);
|
2009-04-24 03:49:25 +00:00
|
|
|
|
2011-07-25 20:08:39 +00:00
|
|
|
/*
|
|
|
|
* If we failed to assign anything, let's try the address
|
|
|
|
* where firmware left it. That at least has a chance of
|
|
|
|
* working, which is better than just leaving it disabled.
|
|
|
|
*/
|
2014-07-08 22:04:22 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
dev_info(&dev->dev, "BAR %d: no space for %pR\n", resno, res);
|
2011-07-25 20:08:39 +00:00
|
|
|
ret = pci_revert_fw_address(res, dev, resno, size);
|
2014-07-08 22:04:22 +00:00
|
|
|
}
|
2009-04-24 03:49:25 +00:00
|
|
|
|
2014-07-08 22:04:22 +00:00
|
|
|
if (ret < 0) {
|
|
|
|
dev_info(&dev->dev, "BAR %d: failed to assign %pR\n", resno,
|
|
|
|
res);
|
2014-07-04 21:58:15 +00:00
|
|
|
return ret;
|
2014-07-08 22:04:22 +00:00
|
|
|
}
|
2014-07-04 21:58:15 +00:00
|
|
|
|
|
|
|
res->flags &= ~IORESOURCE_UNSET;
|
|
|
|
res->flags &= ~IORESOURCE_STARTALIGN;
|
|
|
|
dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
|
|
|
|
if (resno < PCI_BRIDGE_RESOURCES)
|
|
|
|
pci_update_resource(dev, resno);
|
|
|
|
|
|
|
|
return 0;
|
2009-04-24 03:49:25 +00:00
|
|
|
}
|
2014-04-25 20:32:25 +00:00
|
|
|
EXPORT_SYMBOL(pci_assign_resource);
|
2009-04-24 03:49:25 +00:00
|
|
|
|
2012-07-11 23:05:43 +00:00
|
|
|
int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsize,
|
|
|
|
resource_size_t min_align)
|
|
|
|
{
|
|
|
|
struct resource *res = dev->resource + resno;
|
2014-07-04 00:30:29 +00:00
|
|
|
unsigned long flags;
|
2012-07-11 23:05:43 +00:00
|
|
|
resource_size_t new_size;
|
|
|
|
int ret;
|
|
|
|
|
2014-07-04 00:30:29 +00:00
|
|
|
flags = res->flags;
|
2014-02-26 18:25:58 +00:00
|
|
|
res->flags |= IORESOURCE_UNSET;
|
2012-07-11 23:05:43 +00:00
|
|
|
if (!res->parent) {
|
2014-04-19 00:13:50 +00:00
|
|
|
dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR\n",
|
|
|
|
resno, res);
|
2012-07-11 23:05:43 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* already aligned with min_align */
|
|
|
|
new_size = resource_size(res) + addsize;
|
|
|
|
ret = _pci_assign_resource(dev, resno, new_size, min_align);
|
2014-07-04 21:58:15 +00:00
|
|
|
if (ret) {
|
2014-07-04 00:30:29 +00:00
|
|
|
res->flags = flags;
|
|
|
|
dev_info(&dev->dev, "BAR %d: %pR (failed to expand by %#llx)\n",
|
|
|
|
resno, res, (unsigned long long) addsize);
|
2014-07-04 21:58:15 +00:00
|
|
|
return ret;
|
2012-07-11 23:05:43 +00:00
|
|
|
}
|
2014-07-04 00:30:29 +00:00
|
|
|
|
2014-07-04 21:58:15 +00:00
|
|
|
res->flags &= ~IORESOURCE_UNSET;
|
|
|
|
res->flags &= ~IORESOURCE_STARTALIGN;
|
2014-07-08 22:04:22 +00:00
|
|
|
dev_info(&dev->dev, "BAR %d: reassigned %pR (expanded by %#llx)\n",
|
|
|
|
resno, res, (unsigned long long) addsize);
|
2014-07-04 21:58:15 +00:00
|
|
|
if (resno < PCI_BRIDGE_RESOURCES)
|
|
|
|
pci_update_resource(dev, resno);
|
|
|
|
|
|
|
|
return 0;
|
2012-07-11 23:05:43 +00:00
|
|
|
}
|
|
|
|
|
2008-03-04 18:56:47 +00:00
|
|
|
int pci_enable_resources(struct pci_dev *dev, int mask)
|
|
|
|
{
|
|
|
|
u16 cmd, old_cmd;
|
|
|
|
int i;
|
|
|
|
struct resource *r;
|
|
|
|
|
|
|
|
pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
|
|
|
old_cmd = cmd;
|
|
|
|
|
|
|
|
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
|
|
|
if (!(mask & (1 << i)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
r = &dev->resource[i];
|
|
|
|
|
|
|
|
if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
|
|
|
|
continue;
|
|
|
|
if ((i == PCI_ROM_RESOURCE) &&
|
|
|
|
(!(r->flags & IORESOURCE_ROM_ENABLE)))
|
|
|
|
continue;
|
|
|
|
|
2014-02-26 18:26:00 +00:00
|
|
|
if (r->flags & IORESOURCE_UNSET) {
|
|
|
|
dev_err(&dev->dev, "can't enable device: BAR %d %pR not assigned\n",
|
|
|
|
i, r);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2008-03-04 18:56:47 +00:00
|
|
|
if (!r->parent) {
|
2014-02-26 18:26:00 +00:00
|
|
|
dev_err(&dev->dev, "can't enable device: BAR %d %pR not claimed\n",
|
|
|
|
i, r);
|
2008-03-04 18:56:47 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r->flags & IORESOURCE_IO)
|
|
|
|
cmd |= PCI_COMMAND_IO;
|
|
|
|
if (r->flags & IORESOURCE_MEM)
|
|
|
|
cmd |= PCI_COMMAND_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd != old_cmd) {
|
|
|
|
dev_info(&dev->dev, "enabling device (%04x -> %04x)\n",
|
|
|
|
old_cmd, cmd);
|
|
|
|
pci_write_config_word(dev, PCI_COMMAND, cmd);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|