2005-04-17 02:20:36 +04: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/init.h>
|
|
|
|
#include <linux/kernel.h>
|
2011-05-27 17:37:25 +04:00
|
|
|
#include <linux/export.h>
|
2005-04-17 02:20:36 +04: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 21:38:52 +03:00
|
|
|
void pci_update_resource(struct pci_dev *dev, int resno)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct pci_bus_region region;
|
|
|
|
u32 new, check, mask;
|
|
|
|
int reg;
|
2008-11-21 21:41:27 +03:00
|
|
|
enum pci_bar_type type;
|
2008-11-21 21:38:52 +03:00
|
|
|
struct resource *res = dev->resource + resno;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-12-20 00:12:08 +03:00
|
|
|
/*
|
|
|
|
* Ignore resources for unimplemented BARs and unused resource slots
|
|
|
|
* for 64 bit BARs.
|
|
|
|
*/
|
2005-08-07 13:49:59 +04:00
|
|
|
if (!res->flags)
|
|
|
|
return;
|
|
|
|
|
2006-12-20 00:12:08 +03:00
|
|
|
/*
|
|
|
|
* Ignore non-moveable resources. This might be legacy resources for
|
|
|
|
* which no functional BAR register exists or another important
|
2008-06-13 20:52:11 +04:00
|
|
|
* system resource we shouldn't move around.
|
2006-12-20 00:12:08 +03:00
|
|
|
*/
|
|
|
|
if (res->flags & IORESOURCE_PCI_FIXED)
|
|
|
|
return;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
pcibios_resource_to_bus(dev, ®ion, res);
|
|
|
|
|
|
|
|
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 21:41:27 +03:00
|
|
|
reg = pci_resource_bar(dev, resno, &type);
|
|
|
|
if (!reg)
|
|
|
|
return;
|
|
|
|
if (type != pci_bar_unknown) {
|
2005-08-26 21:49:22 +04:00
|
|
|
if (!(res->flags & IORESOURCE_ROM_ENABLE))
|
|
|
|
return;
|
|
|
|
new |= PCI_ROM_ADDRESS_ENABLE;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
pci_write_config_dword(dev, reg, new);
|
|
|
|
pci_read_config_dword(dev, reg, &check);
|
|
|
|
|
|
|
|
if ((new ^ check) & mask) {
|
2008-06-13 20:52:11 +04:00
|
|
|
dev_err(&dev->dev, "BAR %d: error updating (%#08x != %#08x)\n",
|
|
|
|
resno, new, check);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2011-06-14 23:04:35 +04:00
|
|
|
if (res->flags & IORESOURCE_MEM_64) {
|
2005-08-07 13:49:59 +04:00
|
|
|
new = region.start >> 16 >> 16;
|
2005-04-17 02:20:36 +04:00
|
|
|
pci_write_config_dword(dev, reg + 4, new);
|
|
|
|
pci_read_config_dword(dev, reg + 4, &check);
|
|
|
|
if (check != new) {
|
2008-06-13 20:52:11 +04:00
|
|
|
dev_err(&dev->dev, "BAR %d: error updating "
|
|
|
|
"(high %#08x != %#08x)\n", resno, new, check);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
res->flags &= ~IORESOURCE_UNSET;
|
2011-12-05 23:51:18 +04:00
|
|
|
dev_dbg(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n",
|
|
|
|
resno, res, (unsigned long long)region.start,
|
|
|
|
(unsigned long long)region.end);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2007-03-27 09:53:30 +04:00
|
|
|
int pci_claim_resource(struct pci_dev *dev, int resource)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct resource *res = &dev->resource[resource];
|
2010-03-12 03:01:19 +03:00
|
|
|
struct resource *root, *conflict;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-06-18 00:33:33 +04:00
|
|
|
root = pci_find_parent_resource(dev, res);
|
2009-11-04 20:32:57 +03:00
|
|
|
if (!root) {
|
2010-06-03 23:47:18 +04:00
|
|
|
dev_info(&dev->dev, "no compatible bridge window for %pR\n",
|
|
|
|
res);
|
2009-11-04 20:32:57 +03:00
|
|
|
return -EINVAL;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2010-03-12 03:01:19 +03:00
|
|
|
conflict = request_resource_conflict(root, res);
|
|
|
|
if (conflict) {
|
2010-06-03 23:47:18 +04:00
|
|
|
dev_info(&dev->dev,
|
|
|
|
"address space collision: %pR conflicts with %s %pR\n",
|
|
|
|
res, conflict->name, conflict);
|
2010-03-12 03:01:19 +03:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
2009-11-04 20:32:57 +03:00
|
|
|
|
2010-03-12 03:01:19 +03:00
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2009-07-01 08:45:44 +04:00
|
|
|
EXPORT_SYMBOL(pci_claim_resource);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-03-16 11:13:39 +03:00
|
|
|
void pci_disable_bridge_window(struct pci_dev *dev)
|
|
|
|
{
|
2009-11-04 20:32:57 +03:00
|
|
|
dev_info(&dev->dev, "disabling bridge mem windows\n");
|
2009-03-16 11:13:39 +03: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-26 00:08:39 +04:00
|
|
|
|
2009-04-24 07:49:25 +04:00
|
|
|
static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
|
2011-07-26 00:08:39 +04:00
|
|
|
int resno, resource_size_t size, resource_size_t align)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct resource *res = dev->resource + resno;
|
2011-07-26 00:08:39 +04:00
|
|
|
resource_size_t min;
|
2005-04-17 02:20:36 +04:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
|
|
|
|
|
|
|
|
/* First, try exact prefetching match.. */
|
|
|
|
ret = pci_bus_alloc_resource(bus, res, size, align, min,
|
|
|
|
IORESOURCE_PREFETCH,
|
|
|
|
pcibios_align_resource, dev);
|
|
|
|
|
|
|
|
if (ret < 0 && (res->flags & IORESOURCE_PREFETCH)) {
|
|
|
|
/*
|
|
|
|
* That failed.
|
|
|
|
*
|
|
|
|
* But a prefetching area can handle a non-prefetching
|
|
|
|
* window (it will just not perform as well).
|
|
|
|
*/
|
|
|
|
ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
|
|
|
|
pcibios_align_resource, dev);
|
|
|
|
}
|
2011-07-26 00:08:39 +04:00
|
|
|
return ret;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2011-11-21 22:54:19 +04: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;
|
|
|
|
}
|
|
|
|
|
2011-07-26 00:08:39 +04:00
|
|
|
static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
|
|
|
|
int resno, resource_size_t size)
|
|
|
|
{
|
|
|
|
struct resource *root, *conflict;
|
2011-11-21 22:54:19 +04:00
|
|
|
resource_size_t fw_addr, start, end;
|
2011-07-26 00:08:39 +04:00
|
|
|
int ret = 0;
|
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 19:41:42 +04:00
|
|
|
|
2011-11-21 22:54:19 +04:00
|
|
|
fw_addr = pcibios_retrieve_fw_addr(dev, resno);
|
|
|
|
if (!fw_addr)
|
|
|
|
return 1;
|
|
|
|
|
2011-07-26 00:08:39 +04:00
|
|
|
start = res->start;
|
|
|
|
end = res->end;
|
2011-11-21 22:54:19 +04:00
|
|
|
res->start = fw_addr;
|
2011-07-26 00:08:39 +04:00
|
|
|
res->end = res->start + size - 1;
|
2011-11-21 22:54:07 +04:00
|
|
|
|
|
|
|
root = pci_find_parent_resource(dev, res);
|
|
|
|
if (!root) {
|
|
|
|
if (res->flags & IORESOURCE_IO)
|
|
|
|
root = &ioport_resource;
|
|
|
|
else
|
|
|
|
root = &iomem_resource;
|
|
|
|
}
|
|
|
|
|
2011-07-26 00:08:39 +04:00
|
|
|
dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n",
|
|
|
|
resno, res);
|
|
|
|
conflict = request_resource_conflict(root, res);
|
|
|
|
if (conflict) {
|
|
|
|
dev_info(&dev->dev,
|
|
|
|
"BAR %d: %pR conflicts with %s %pR\n", resno,
|
|
|
|
res, conflict->name, conflict);
|
|
|
|
res->start = start;
|
|
|
|
res->end = end;
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-06-20 23:56:00 +04:00
|
|
|
static int _pci_assign_resource(struct pci_dev *dev, int resno,
|
|
|
|
resource_size_t size, resource_size_t min_align)
|
2011-07-26 00:08:39 +04:00
|
|
|
{
|
|
|
|
struct resource *res = dev->resource + resno;
|
|
|
|
struct pci_bus *bus;
|
|
|
|
int ret;
|
|
|
|
char *type;
|
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 19:41:42 +04:00
|
|
|
|
2011-07-26 00:08:39 +04: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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
if (res->flags & IORESOURCE_MEM)
|
|
|
|
if (res->flags & IORESOURCE_PREFETCH)
|
|
|
|
type = "mem pref";
|
|
|
|
else
|
|
|
|
type = "mem";
|
|
|
|
else if (res->flags & IORESOURCE_IO)
|
|
|
|
type = "io";
|
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 19:41:42 +04:00
|
|
|
else
|
2011-07-26 00:08:39 +04:00
|
|
|
type = "unknown";
|
|
|
|
dev_info(&dev->dev,
|
|
|
|
"BAR %d: can't assign %s (size %#llx)\n",
|
|
|
|
resno, type, (unsigned long long) resource_size(res));
|
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 19:41:42 +04:00
|
|
|
}
|
|
|
|
|
2011-07-26 00:08:39 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
resource_size_t new_size;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!res->parent) {
|
2012-01-26 18:45:47 +04:00
|
|
|
dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR "
|
2011-07-26 00:08:39 +04:00
|
|
|
"\n", resno, res);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2012-01-21 14:08:17 +04:00
|
|
|
/* already aligned with min_align */
|
|
|
|
new_size = resource_size(res) + addsize;
|
2011-07-26 00:08:39 +04:00
|
|
|
ret = _pci_assign_resource(dev, resno, new_size, min_align);
|
2009-04-24 07:49:25 +04:00
|
|
|
if (!ret) {
|
PCI: clean up resource alignment management
Done per Linus' request and suggestions. Linus has explained that
better than I'll be able to explain:
On Thu, Mar 27, 2008 at 10:12:10AM -0700, Linus Torvalds wrote:
> Actually, before we go any further, there might be a less intrusive
> alternative: add just a couple of flags to the resource flags field (we
> still have something like 8 unused bits on 32-bit), and use those to
> implement a generic "resource_alignment()" routine.
>
> Two flags would do it:
>
> - IORESOURCE_SIZEALIGN: size indicates alignment (regular PCI device
> resources)
>
> - IORESOURCE_STARTALIGN: start field is alignment (PCI bus resources
> during probing)
>
> and then the case of both flags zero (or both bits set) would actually be
> "invalid", and we would also clear the IORESOURCE_STARTALIGN flag when we
> actually allocate the resource (so that we don't use the "start" field as
> alignment incorrectly when it no longer indicates alignment).
>
> That wouldn't be totally generic, but it would have the nice property of
> automatically at least add sanity checking for that whole "res->start has
> the odd meaning of 'alignment' during probing" and remove the need for a
> new field, and it would allow us to have a generic "resource_alignment()"
> routine that just gets a resource pointer.
Besides, I removed IORESOURCE_BUS_HAS_VGA flag which was unused for ages.
Signed-off-by: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Gary Hade <garyhade@us.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-30 19:50:14 +04:00
|
|
|
res->flags &= ~IORESOURCE_STARTALIGN;
|
2012-01-21 14:08:17 +04:00
|
|
|
dev_info(&dev->dev, "BAR %d: reassigned %pR\n", resno, res);
|
PCI: clean up resource alignment management
Done per Linus' request and suggestions. Linus has explained that
better than I'll be able to explain:
On Thu, Mar 27, 2008 at 10:12:10AM -0700, Linus Torvalds wrote:
> Actually, before we go any further, there might be a less intrusive
> alternative: add just a couple of flags to the resource flags field (we
> still have something like 8 unused bits on 32-bit), and use those to
> implement a generic "resource_alignment()" routine.
>
> Two flags would do it:
>
> - IORESOURCE_SIZEALIGN: size indicates alignment (regular PCI device
> resources)
>
> - IORESOURCE_STARTALIGN: start field is alignment (PCI bus resources
> during probing)
>
> and then the case of both flags zero (or both bits set) would actually be
> "invalid", and we would also clear the IORESOURCE_STARTALIGN flag when we
> actually allocate the resource (so that we don't use the "start" field as
> alignment incorrectly when it no longer indicates alignment).
>
> That wouldn't be totally generic, but it would have the nice property of
> automatically at least add sanity checking for that whole "res->start has
> the odd meaning of 'alignment' during probing" and remove the need for a
> new field, and it would allow us to have a generic "resource_alignment()"
> routine that just gets a resource pointer.
Besides, I removed IORESOURCE_BUS_HAS_VGA flag which was unused for ages.
Signed-off-by: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Gary Hade <garyhade@us.ibm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2008-03-30 19:50:14 +04:00
|
|
|
if (resno < PCI_BRIDGE_RESOURCES)
|
2008-11-21 21:38:52 +03:00
|
|
|
pci_update_resource(dev, resno);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-04-24 07:49:25 +04:00
|
|
|
int pci_assign_resource(struct pci_dev *dev, int resno)
|
|
|
|
{
|
|
|
|
struct resource *res = dev->resource + resno;
|
2011-07-26 00:08:39 +04:00
|
|
|
resource_size_t align, size;
|
2009-04-24 07:49:25 +04:00
|
|
|
struct pci_bus *bus;
|
|
|
|
int ret;
|
|
|
|
|
2009-08-29 00:00:06 +04:00
|
|
|
align = pci_resource_alignment(dev, res);
|
2009-04-24 07:49:25 +04:00
|
|
|
if (!align) {
|
2009-11-04 20:32:57 +03:00
|
|
|
dev_info(&dev->dev, "BAR %d: can't assign %pR "
|
2009-10-07 01:33:44 +04:00
|
|
|
"(bogus alignment)\n", resno, res);
|
2009-04-24 07:49:25 +04:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bus = dev->bus;
|
2011-07-26 00:08:39 +04:00
|
|
|
size = resource_size(res);
|
|
|
|
ret = _pci_assign_resource(dev, resno, size, align);
|
2009-04-24 07:49:25 +04:00
|
|
|
|
2011-07-26 00:08:39 +04: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.
|
|
|
|
*/
|
2011-11-21 22:54:19 +04:00
|
|
|
if (ret < 0)
|
2011-07-26 00:08:39 +04:00
|
|
|
ret = pci_revert_fw_address(res, dev, resno, size);
|
2009-04-24 07:49:25 +04:00
|
|
|
|
2011-07-26 00:08:39 +04:00
|
|
|
if (!ret) {
|
|
|
|
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);
|
|
|
|
}
|
2009-04-24 07:49:25 +04:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2008-03-04 21:56:47 +03: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;
|
|
|
|
|
|
|
|
if (!r->parent) {
|
2009-11-04 20:32:57 +03:00
|
|
|
dev_err(&dev->dev, "device not available "
|
|
|
|
"(can't reserve %pR)\n", r);
|
2008-03-04 21:56:47 +03: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;
|
|
|
|
}
|