mirror of
https://github.com/torvalds/linux
synced 2024-09-21 19:47:35 +00:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes/pci-2.6: PCI: OF: Don't crash when bridge parent is NULL. PCI: export pcie_bus_configure_settings symbol PCI: code and comments cleanup PCI: make cardbus-bridge resources optional PCI: make SRIOV resources optional PCI : ability to relocate assigned pci-resources PCI: honor child buses add_size in hot plug configuration PCI: Set PCI-E Max Payload Size on fabric
This commit is contained in:
commit
0c3bef6128
|
@ -360,6 +360,15 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* After the PCI-E bus has been walked and all devices discovered,
|
||||||
|
* configure any settings of the fabric that might be necessary.
|
||||||
|
*/
|
||||||
|
if (bus) {
|
||||||
|
struct pci_bus *child;
|
||||||
|
list_for_each_entry(child, &bus->children, node)
|
||||||
|
pcie_bus_configure_settings(child, child->self->pcie_mpss);
|
||||||
|
}
|
||||||
|
|
||||||
if (!bus)
|
if (!bus)
|
||||||
kfree(sd);
|
kfree(sd);
|
||||||
|
|
||||||
|
|
|
@ -158,47 +158,6 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Program PCIE MaxPayload setting on device: ensure parent maxpayload <= device */
|
|
||||||
static int pci_set_payload(struct pci_dev *dev)
|
|
||||||
{
|
|
||||||
int pos, ppos;
|
|
||||||
u16 pctl, psz;
|
|
||||||
u16 dctl, dsz, dcap, dmax;
|
|
||||||
struct pci_dev *parent;
|
|
||||||
|
|
||||||
parent = dev->bus->self;
|
|
||||||
pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
|
|
||||||
if (!pos)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Read Device MaxPayload capability and setting */
|
|
||||||
pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &dctl);
|
|
||||||
pci_read_config_word(dev, pos + PCI_EXP_DEVCAP, &dcap);
|
|
||||||
dsz = (dctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5;
|
|
||||||
dmax = (dcap & PCI_EXP_DEVCAP_PAYLOAD);
|
|
||||||
|
|
||||||
/* Read Parent MaxPayload setting */
|
|
||||||
ppos = pci_find_capability(parent, PCI_CAP_ID_EXP);
|
|
||||||
if (!ppos)
|
|
||||||
return 0;
|
|
||||||
pci_read_config_word(parent, ppos + PCI_EXP_DEVCTL, &pctl);
|
|
||||||
psz = (pctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5;
|
|
||||||
|
|
||||||
/* If parent payload > device max payload -> error
|
|
||||||
* If parent payload > device payload -> set speed
|
|
||||||
* If parent payload <= device payload -> do nothing
|
|
||||||
*/
|
|
||||||
if (psz > dmax)
|
|
||||||
return -1;
|
|
||||||
else if (psz > dsz) {
|
|
||||||
dev_info(&dev->dev, "Setting MaxPayload to %d\n", 128 << psz);
|
|
||||||
pci_write_config_word(dev, pos + PCI_EXP_DEVCTL,
|
|
||||||
(dctl & ~PCI_EXP_DEVCTL_PAYLOAD) +
|
|
||||||
(psz << 5));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pci_configure_slot(struct pci_dev *dev)
|
void pci_configure_slot(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
struct pci_dev *cdev;
|
struct pci_dev *cdev;
|
||||||
|
@ -210,9 +169,7 @@ void pci_configure_slot(struct pci_dev *dev)
|
||||||
(dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
|
(dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ret = pci_set_payload(dev);
|
pcie_bus_configure_settings(dev->bus, dev->bus->self->pcie_mpss);
|
||||||
if (ret)
|
|
||||||
dev_warn(&dev->dev, "could not set device max payload\n");
|
|
||||||
|
|
||||||
memset(&hpp, 0, sizeof(hpp));
|
memset(&hpp, 0, sizeof(hpp));
|
||||||
ret = pci_get_hp_params(dev, &hpp);
|
ret = pci_get_hp_params(dev, &hpp);
|
||||||
|
|
|
@ -55,7 +55,7 @@ struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus)
|
||||||
*/
|
*/
|
||||||
if (bus->bridge->of_node)
|
if (bus->bridge->of_node)
|
||||||
return of_node_get(bus->bridge->of_node);
|
return of_node_get(bus->bridge->of_node);
|
||||||
if (bus->bridge->parent->of_node)
|
if (bus->bridge->parent && bus->bridge->parent->of_node)
|
||||||
return of_node_get(bus->bridge->parent->of_node);
|
return of_node_get(bus->bridge->parent->of_node);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,8 @@ unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE;
|
||||||
unsigned long pci_hotplug_io_size = DEFAULT_HOTPLUG_IO_SIZE;
|
unsigned long pci_hotplug_io_size = DEFAULT_HOTPLUG_IO_SIZE;
|
||||||
unsigned long pci_hotplug_mem_size = DEFAULT_HOTPLUG_MEM_SIZE;
|
unsigned long pci_hotplug_mem_size = DEFAULT_HOTPLUG_MEM_SIZE;
|
||||||
|
|
||||||
|
enum pcie_bus_config_types pcie_bus_config = PCIE_BUS_PERFORMANCE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The default CLS is used if arch didn't set CLS explicitly and not
|
* The default CLS is used if arch didn't set CLS explicitly and not
|
||||||
* all pci devices agree on the same value. Arch can override either
|
* all pci devices agree on the same value. Arch can override either
|
||||||
|
@ -3222,6 +3224,67 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(pcie_set_readrq);
|
EXPORT_SYMBOL(pcie_set_readrq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pcie_get_mps - get PCI Express maximum payload size
|
||||||
|
* @dev: PCI device to query
|
||||||
|
*
|
||||||
|
* Returns maximum payload size in bytes
|
||||||
|
* or appropriate error value.
|
||||||
|
*/
|
||||||
|
int pcie_get_mps(struct pci_dev *dev)
|
||||||
|
{
|
||||||
|
int ret, cap;
|
||||||
|
u16 ctl;
|
||||||
|
|
||||||
|
cap = pci_pcie_cap(dev);
|
||||||
|
if (!cap)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
|
||||||
|
if (!ret)
|
||||||
|
ret = 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pcie_set_mps - set PCI Express maximum payload size
|
||||||
|
* @dev: PCI device to query
|
||||||
|
* @rq: maximum payload size in bytes
|
||||||
|
* valid values are 128, 256, 512, 1024, 2048, 4096
|
||||||
|
*
|
||||||
|
* If possible sets maximum payload size
|
||||||
|
*/
|
||||||
|
int pcie_set_mps(struct pci_dev *dev, int mps)
|
||||||
|
{
|
||||||
|
int cap, err = -EINVAL;
|
||||||
|
u16 ctl, v;
|
||||||
|
|
||||||
|
if (mps < 128 || mps > 4096 || !is_power_of_2(mps))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
v = ffs(mps) - 8;
|
||||||
|
if (v > dev->pcie_mpss)
|
||||||
|
goto out;
|
||||||
|
v <<= 5;
|
||||||
|
|
||||||
|
cap = pci_pcie_cap(dev);
|
||||||
|
if (!cap)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if ((ctl & PCI_EXP_DEVCTL_PAYLOAD) != v) {
|
||||||
|
ctl &= ~PCI_EXP_DEVCTL_PAYLOAD;
|
||||||
|
ctl |= v;
|
||||||
|
err = pci_write_config_word(dev, cap + PCI_EXP_DEVCTL, ctl);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pci_select_bars - Make BAR mask from the type of resource
|
* pci_select_bars - Make BAR mask from the type of resource
|
||||||
* @dev: the PCI device for which BAR mask is made
|
* @dev: the PCI device for which BAR mask is made
|
||||||
|
@ -3505,6 +3568,10 @@ static int __init pci_setup(char *str)
|
||||||
pci_hotplug_io_size = memparse(str + 9, &str);
|
pci_hotplug_io_size = memparse(str + 9, &str);
|
||||||
} else if (!strncmp(str, "hpmemsize=", 10)) {
|
} else if (!strncmp(str, "hpmemsize=", 10)) {
|
||||||
pci_hotplug_mem_size = memparse(str + 10, &str);
|
pci_hotplug_mem_size = memparse(str + 10, &str);
|
||||||
|
} else if (!strncmp(str, "pcie_bus_safe", 13)) {
|
||||||
|
pcie_bus_config = PCIE_BUS_SAFE;
|
||||||
|
} else if (!strncmp(str, "pcie_bus_perf", 13)) {
|
||||||
|
pcie_bus_config = PCIE_BUS_PERFORMANCE;
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_ERR "PCI: Unknown option `%s'\n",
|
printk(KERN_ERR "PCI: Unknown option `%s'\n",
|
||||||
str);
|
str);
|
||||||
|
|
|
@ -283,6 +283,8 @@ static inline int pci_iov_bus_range(struct pci_bus *bus)
|
||||||
|
|
||||||
#endif /* CONFIG_PCI_IOV */
|
#endif /* CONFIG_PCI_IOV */
|
||||||
|
|
||||||
|
extern unsigned long pci_cardbus_resource_alignment(struct resource *);
|
||||||
|
|
||||||
static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
|
static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
|
||||||
struct resource *res)
|
struct resource *res)
|
||||||
{
|
{
|
||||||
|
@ -292,6 +294,8 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
|
||||||
if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
|
if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END)
|
||||||
return pci_sriov_resource_alignment(dev, resno);
|
return pci_sriov_resource_alignment(dev, resno);
|
||||||
#endif
|
#endif
|
||||||
|
if (dev->class >> 8 == PCI_CLASS_BRIDGE_CARDBUS)
|
||||||
|
return pci_cardbus_resource_alignment(res);
|
||||||
return resource_alignment(res);
|
return resource_alignment(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -856,6 +856,8 @@ void set_pcie_port_type(struct pci_dev *pdev)
|
||||||
pdev->pcie_cap = pos;
|
pdev->pcie_cap = pos;
|
||||||
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16);
|
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16);
|
||||||
pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4;
|
pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4;
|
||||||
|
pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, ®16);
|
||||||
|
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_pcie_hotplug_bridge(struct pci_dev *pdev)
|
void set_pcie_hotplug_bridge(struct pci_dev *pdev)
|
||||||
|
@ -1326,6 +1328,150 @@ int pci_scan_slot(struct pci_bus *bus, int devfn)
|
||||||
return nr;
|
return nr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int pcie_find_smpss(struct pci_dev *dev, void *data)
|
||||||
|
{
|
||||||
|
u8 *smpss = data;
|
||||||
|
|
||||||
|
if (!pci_is_pcie(dev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* For PCIE hotplug enabled slots not connected directly to a
|
||||||
|
* PCI-E root port, there can be problems when hotplugging
|
||||||
|
* devices. This is due to the possibility of hotplugging a
|
||||||
|
* device into the fabric with a smaller MPS that the devices
|
||||||
|
* currently running have configured. Modifying the MPS on the
|
||||||
|
* running devices could cause a fatal bus error due to an
|
||||||
|
* incoming frame being larger than the newly configured MPS.
|
||||||
|
* To work around this, the MPS for the entire fabric must be
|
||||||
|
* set to the minimum size. Any devices hotplugged into this
|
||||||
|
* fabric will have the minimum MPS set. If the PCI hotplug
|
||||||
|
* slot is directly connected to the root port and there are not
|
||||||
|
* other devices on the fabric (which seems to be the most
|
||||||
|
* common case), then this is not an issue and MPS discovery
|
||||||
|
* will occur as normal.
|
||||||
|
*/
|
||||||
|
if (dev->is_hotplug_bridge && (!list_is_singular(&dev->bus->devices) ||
|
||||||
|
dev->bus->self->pcie_type != PCI_EXP_TYPE_ROOT_PORT))
|
||||||
|
*smpss = 0;
|
||||||
|
|
||||||
|
if (*smpss > dev->pcie_mpss)
|
||||||
|
*smpss = dev->pcie_mpss;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pcie_write_mps(struct pci_dev *dev, int mps)
|
||||||
|
{
|
||||||
|
int rc, dev_mpss;
|
||||||
|
|
||||||
|
dev_mpss = 128 << dev->pcie_mpss;
|
||||||
|
|
||||||
|
if (pcie_bus_config == PCIE_BUS_PERFORMANCE) {
|
||||||
|
if (dev->bus->self) {
|
||||||
|
dev_dbg(&dev->bus->dev, "Bus MPSS %d\n",
|
||||||
|
128 << dev->bus->self->pcie_mpss);
|
||||||
|
|
||||||
|
/* For "MPS Force Max", the assumption is made that
|
||||||
|
* downstream communication will never be larger than
|
||||||
|
* the MRRS. So, the MPS only needs to be configured
|
||||||
|
* for the upstream communication. This being the case,
|
||||||
|
* walk from the top down and set the MPS of the child
|
||||||
|
* to that of the parent bus.
|
||||||
|
*/
|
||||||
|
mps = 128 << dev->bus->self->pcie_mpss;
|
||||||
|
if (mps > dev_mpss)
|
||||||
|
dev_warn(&dev->dev, "MPS configured higher than"
|
||||||
|
" maximum supported by the device. If"
|
||||||
|
" a bus issue occurs, try running with"
|
||||||
|
" pci=pcie_bus_safe.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
dev->pcie_mpss = ffs(mps) - 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = pcie_set_mps(dev, mps);
|
||||||
|
if (rc)
|
||||||
|
dev_err(&dev->dev, "Failed attempting to set the MPS\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pcie_write_mrrs(struct pci_dev *dev, int mps)
|
||||||
|
{
|
||||||
|
int rc, mrrs;
|
||||||
|
|
||||||
|
if (pcie_bus_config == PCIE_BUS_PERFORMANCE) {
|
||||||
|
int dev_mpss = 128 << dev->pcie_mpss;
|
||||||
|
|
||||||
|
/* For Max performance, the MRRS must be set to the largest
|
||||||
|
* supported value. However, it cannot be configured larger
|
||||||
|
* than the MPS the device or the bus can support. This assumes
|
||||||
|
* that the largest MRRS available on the device cannot be
|
||||||
|
* smaller than the device MPSS.
|
||||||
|
*/
|
||||||
|
mrrs = mps < dev_mpss ? mps : dev_mpss;
|
||||||
|
} else
|
||||||
|
/* In the "safe" case, configure the MRRS for fairness on the
|
||||||
|
* bus by making all devices have the same size
|
||||||
|
*/
|
||||||
|
mrrs = mps;
|
||||||
|
|
||||||
|
|
||||||
|
/* MRRS is a R/W register. Invalid values can be written, but a
|
||||||
|
* subsiquent read will verify if the value is acceptable or not.
|
||||||
|
* If the MRRS value provided is not acceptable (e.g., too large),
|
||||||
|
* shrink the value until it is acceptable to the HW.
|
||||||
|
*/
|
||||||
|
while (mrrs != pcie_get_readrq(dev) && mrrs >= 128) {
|
||||||
|
rc = pcie_set_readrq(dev, mrrs);
|
||||||
|
if (rc)
|
||||||
|
dev_err(&dev->dev, "Failed attempting to set the MRRS\n");
|
||||||
|
|
||||||
|
mrrs /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pcie_bus_configure_set(struct pci_dev *dev, void *data)
|
||||||
|
{
|
||||||
|
int mps = 128 << *(u8 *)data;
|
||||||
|
|
||||||
|
if (!pci_is_pcie(dev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dev_info(&dev->dev, "Dev MPS %d MPSS %d MRRS %d\n",
|
||||||
|
pcie_get_mps(dev), 128<<dev->pcie_mpss, pcie_get_readrq(dev));
|
||||||
|
|
||||||
|
pcie_write_mps(dev, mps);
|
||||||
|
pcie_write_mrrs(dev, mps);
|
||||||
|
|
||||||
|
dev_info(&dev->dev, "Dev MPS %d MPSS %d MRRS %d\n",
|
||||||
|
pcie_get_mps(dev), 128<<dev->pcie_mpss, pcie_get_readrq(dev));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pcie_bus_configure_mps requires that pci_walk_bus work in a top-down,
|
||||||
|
* parents then children fashion. If this changes, then this code will not
|
||||||
|
* work as designed.
|
||||||
|
*/
|
||||||
|
void pcie_bus_configure_settings(struct pci_bus *bus, u8 mpss)
|
||||||
|
{
|
||||||
|
u8 smpss = mpss;
|
||||||
|
|
||||||
|
if (!bus->self)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!pci_is_pcie(bus->self))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pcie_bus_config == PCIE_BUS_SAFE) {
|
||||||
|
pcie_find_smpss(bus->self, &smpss);
|
||||||
|
pci_walk_bus(bus, pcie_find_smpss, &smpss);
|
||||||
|
}
|
||||||
|
|
||||||
|
pcie_bus_configure_set(bus->self, &smpss);
|
||||||
|
pci_walk_bus(bus, pcie_bus_configure_set, &smpss);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pcie_bus_configure_settings);
|
||||||
|
|
||||||
unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
|
unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
|
||||||
{
|
{
|
||||||
unsigned int devfn, pass, max = bus->secondary;
|
unsigned int devfn, pass, max = bus->secondary;
|
||||||
|
|
|
@ -34,6 +34,7 @@ struct resource_list_x {
|
||||||
resource_size_t start;
|
resource_size_t start;
|
||||||
resource_size_t end;
|
resource_size_t end;
|
||||||
resource_size_t add_size;
|
resource_size_t add_size;
|
||||||
|
resource_size_t min_align;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,7 +66,7 @@ void pci_realloc(void)
|
||||||
*/
|
*/
|
||||||
static void add_to_list(struct resource_list_x *head,
|
static void add_to_list(struct resource_list_x *head,
|
||||||
struct pci_dev *dev, struct resource *res,
|
struct pci_dev *dev, struct resource *res,
|
||||||
resource_size_t add_size)
|
resource_size_t add_size, resource_size_t min_align)
|
||||||
{
|
{
|
||||||
struct resource_list_x *list = head;
|
struct resource_list_x *list = head;
|
||||||
struct resource_list_x *ln = list->next;
|
struct resource_list_x *ln = list->next;
|
||||||
|
@ -84,13 +85,16 @@ static void add_to_list(struct resource_list_x *head,
|
||||||
tmp->end = res->end;
|
tmp->end = res->end;
|
||||||
tmp->flags = res->flags;
|
tmp->flags = res->flags;
|
||||||
tmp->add_size = add_size;
|
tmp->add_size = add_size;
|
||||||
|
tmp->min_align = min_align;
|
||||||
list->next = tmp;
|
list->next = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_to_failed_list(struct resource_list_x *head,
|
static void add_to_failed_list(struct resource_list_x *head,
|
||||||
struct pci_dev *dev, struct resource *res)
|
struct pci_dev *dev, struct resource *res)
|
||||||
{
|
{
|
||||||
add_to_list(head, dev, res, 0);
|
add_to_list(head, dev, res,
|
||||||
|
0 /* dont care */,
|
||||||
|
0 /* dont care */);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __dev_sort_resources(struct pci_dev *dev,
|
static void __dev_sort_resources(struct pci_dev *dev,
|
||||||
|
@ -121,18 +125,18 @@ static inline void reset_resource(struct resource *res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* adjust_resources_sorted() - satisfy any additional resource requests
|
* reassign_resources_sorted() - satisfy any additional resource requests
|
||||||
*
|
*
|
||||||
* @add_head : head of the list tracking requests requiring additional
|
* @realloc_head : head of the list tracking requests requiring additional
|
||||||
* resources
|
* resources
|
||||||
* @head : head of the list tracking requests with allocated
|
* @head : head of the list tracking requests with allocated
|
||||||
* resources
|
* resources
|
||||||
*
|
*
|
||||||
* Walk through each element of the add_head and try to procure
|
* Walk through each element of the realloc_head and try to procure
|
||||||
* additional resources for the element, provided the element
|
* additional resources for the element, provided the element
|
||||||
* is in the head list.
|
* is in the head list.
|
||||||
*/
|
*/
|
||||||
static void adjust_resources_sorted(struct resource_list_x *add_head,
|
static void reassign_resources_sorted(struct resource_list_x *realloc_head,
|
||||||
struct resource_list *head)
|
struct resource_list *head)
|
||||||
{
|
{
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
|
@ -141,8 +145,8 @@ static void adjust_resources_sorted(struct resource_list_x *add_head,
|
||||||
resource_size_t add_size;
|
resource_size_t add_size;
|
||||||
int idx;
|
int idx;
|
||||||
|
|
||||||
prev = add_head;
|
prev = realloc_head;
|
||||||
for (list = add_head->next; list;) {
|
for (list = realloc_head->next; list;) {
|
||||||
res = list->res;
|
res = list->res;
|
||||||
/* skip resource that has been reset */
|
/* skip resource that has been reset */
|
||||||
if (!res->flags)
|
if (!res->flags)
|
||||||
|
@ -159,13 +163,17 @@ static void adjust_resources_sorted(struct resource_list_x *add_head,
|
||||||
|
|
||||||
idx = res - &list->dev->resource[0];
|
idx = res - &list->dev->resource[0];
|
||||||
add_size=list->add_size;
|
add_size=list->add_size;
|
||||||
if (!resource_size(res) && add_size) {
|
if (!resource_size(res)) {
|
||||||
res->end = res->start + add_size - 1;
|
res->start = list->start;
|
||||||
if(pci_assign_resource(list->dev, idx))
|
res->end = res->start + add_size - 1;
|
||||||
|
if(pci_assign_resource(list->dev, idx))
|
||||||
reset_resource(res);
|
reset_resource(res);
|
||||||
} else if (add_size) {
|
} else {
|
||||||
adjust_resource(res, res->start,
|
resource_size_t align = list->min_align;
|
||||||
resource_size(res) + add_size);
|
res->flags |= list->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);
|
||||||
|
if (pci_reassign_resource(list->dev, idx, add_size, align))
|
||||||
|
dev_printk(KERN_DEBUG, &list->dev->dev, "failed to add optional resources res=%pR\n",
|
||||||
|
res);
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
tmp = list;
|
tmp = list;
|
||||||
|
@ -210,16 +218,16 @@ static void assign_requested_resources_sorted(struct resource_list *head,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __assign_resources_sorted(struct resource_list *head,
|
static void __assign_resources_sorted(struct resource_list *head,
|
||||||
struct resource_list_x *add_head,
|
struct resource_list_x *realloc_head,
|
||||||
struct resource_list_x *fail_head)
|
struct resource_list_x *fail_head)
|
||||||
{
|
{
|
||||||
/* Satisfy the must-have resource requests */
|
/* Satisfy the must-have resource requests */
|
||||||
assign_requested_resources_sorted(head, fail_head);
|
assign_requested_resources_sorted(head, fail_head);
|
||||||
|
|
||||||
/* Try to satisfy any additional nice-to-have resource
|
/* Try to satisfy any additional optional resource
|
||||||
requests */
|
requests */
|
||||||
if (add_head)
|
if (realloc_head)
|
||||||
adjust_resources_sorted(add_head, head);
|
reassign_resources_sorted(realloc_head, head);
|
||||||
free_list(resource_list, head);
|
free_list(resource_list, head);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +243,7 @@ static void pdev_assign_resources_sorted(struct pci_dev *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pbus_assign_resources_sorted(const struct pci_bus *bus,
|
static void pbus_assign_resources_sorted(const struct pci_bus *bus,
|
||||||
struct resource_list_x *add_head,
|
struct resource_list_x *realloc_head,
|
||||||
struct resource_list_x *fail_head)
|
struct resource_list_x *fail_head)
|
||||||
{
|
{
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
|
@ -245,7 +253,7 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus,
|
||||||
list_for_each_entry(dev, &bus->devices, bus_list)
|
list_for_each_entry(dev, &bus->devices, bus_list)
|
||||||
__dev_sort_resources(dev, &head);
|
__dev_sort_resources(dev, &head);
|
||||||
|
|
||||||
__assign_resources_sorted(&head, add_head, fail_head);
|
__assign_resources_sorted(&head, realloc_head, fail_head);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pci_setup_cardbus(struct pci_bus *bus)
|
void pci_setup_cardbus(struct pci_bus *bus)
|
||||||
|
@ -540,13 +548,27 @@ static resource_size_t calculate_memsize(resource_size_t size,
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static resource_size_t get_res_add_size(struct resource_list_x *realloc_head,
|
||||||
|
struct resource *res)
|
||||||
|
{
|
||||||
|
struct resource_list_x *list;
|
||||||
|
|
||||||
|
/* check if it is in realloc_head list */
|
||||||
|
for (list = realloc_head->next; list && list->res != res;
|
||||||
|
list = list->next);
|
||||||
|
if (list)
|
||||||
|
return list->add_size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* pbus_size_io() - size the io window of a given bus
|
* pbus_size_io() - size the io window of a given bus
|
||||||
*
|
*
|
||||||
* @bus : the bus
|
* @bus : the bus
|
||||||
* @min_size : the minimum io window that must to be allocated
|
* @min_size : the minimum io window that must to be allocated
|
||||||
* @add_size : additional optional io window
|
* @add_size : additional optional io window
|
||||||
* @add_head : track the additional io window on this list
|
* @realloc_head : track the additional io window on this list
|
||||||
*
|
*
|
||||||
* Sizing the IO windows of the PCI-PCI bridge is trivial,
|
* Sizing the IO windows of the PCI-PCI bridge is trivial,
|
||||||
* since these windows have 4K granularity and the IO ranges
|
* since these windows have 4K granularity and the IO ranges
|
||||||
|
@ -554,11 +576,12 @@ static resource_size_t calculate_memsize(resource_size_t size,
|
||||||
* We must be careful with the ISA aliasing though.
|
* We must be careful with the ISA aliasing though.
|
||||||
*/
|
*/
|
||||||
static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
||||||
resource_size_t add_size, struct resource_list_x *add_head)
|
resource_size_t add_size, struct resource_list_x *realloc_head)
|
||||||
{
|
{
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
|
struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
|
||||||
unsigned long size = 0, size0 = 0, size1 = 0;
|
unsigned long size = 0, size0 = 0, size1 = 0;
|
||||||
|
resource_size_t children_add_size = 0;
|
||||||
|
|
||||||
if (!b_res)
|
if (!b_res)
|
||||||
return;
|
return;
|
||||||
|
@ -579,11 +602,16 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
||||||
size += r_size;
|
size += r_size;
|
||||||
else
|
else
|
||||||
size1 += r_size;
|
size1 += r_size;
|
||||||
|
|
||||||
|
if (realloc_head)
|
||||||
|
children_add_size += get_res_add_size(realloc_head, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
size0 = calculate_iosize(size, min_size, size1,
|
size0 = calculate_iosize(size, min_size, size1,
|
||||||
resource_size(b_res), 4096);
|
resource_size(b_res), 4096);
|
||||||
size1 = (!add_head || (add_head && !add_size)) ? size0 :
|
if (children_add_size > add_size)
|
||||||
|
add_size = children_add_size;
|
||||||
|
size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
|
||||||
calculate_iosize(size, min_size+add_size, size1,
|
calculate_iosize(size, min_size+add_size, size1,
|
||||||
resource_size(b_res), 4096);
|
resource_size(b_res), 4096);
|
||||||
if (!size0 && !size1) {
|
if (!size0 && !size1) {
|
||||||
|
@ -598,8 +626,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
||||||
b_res->start = 4096;
|
b_res->start = 4096;
|
||||||
b_res->end = b_res->start + size0 - 1;
|
b_res->end = b_res->start + size0 - 1;
|
||||||
b_res->flags |= IORESOURCE_STARTALIGN;
|
b_res->flags |= IORESOURCE_STARTALIGN;
|
||||||
if (size1 > size0 && add_head)
|
if (size1 > size0 && realloc_head)
|
||||||
add_to_list(add_head, bus->self, b_res, size1-size0);
|
add_to_list(realloc_head, bus->self, b_res, size1-size0, 4096);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -608,7 +636,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
||||||
* @bus : the bus
|
* @bus : the bus
|
||||||
* @min_size : the minimum memory window that must to be allocated
|
* @min_size : the minimum memory window that must to be allocated
|
||||||
* @add_size : additional optional memory window
|
* @add_size : additional optional memory window
|
||||||
* @add_head : track the additional memory window on this list
|
* @realloc_head : track the additional memory window on this list
|
||||||
*
|
*
|
||||||
* Calculate the size of the bus and minimal alignment which
|
* Calculate the size of the bus and minimal alignment which
|
||||||
* guarantees that all child resources fit in this size.
|
* guarantees that all child resources fit in this size.
|
||||||
|
@ -616,7 +644,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
|
||||||
static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
||||||
unsigned long type, resource_size_t min_size,
|
unsigned long type, resource_size_t min_size,
|
||||||
resource_size_t add_size,
|
resource_size_t add_size,
|
||||||
struct resource_list_x *add_head)
|
struct resource_list_x *realloc_head)
|
||||||
{
|
{
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
resource_size_t min_align, align, size, size0, size1;
|
resource_size_t min_align, align, size, size0, size1;
|
||||||
|
@ -624,6 +652,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
||||||
int order, max_order;
|
int order, max_order;
|
||||||
struct resource *b_res = find_free_bus_resource(bus, type);
|
struct resource *b_res = find_free_bus_resource(bus, type);
|
||||||
unsigned int mem64_mask = 0;
|
unsigned int mem64_mask = 0;
|
||||||
|
resource_size_t children_add_size = 0;
|
||||||
|
|
||||||
if (!b_res)
|
if (!b_res)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -645,6 +674,16 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
||||||
if (r->parent || (r->flags & mask) != type)
|
if (r->parent || (r->flags & mask) != type)
|
||||||
continue;
|
continue;
|
||||||
r_size = resource_size(r);
|
r_size = resource_size(r);
|
||||||
|
#ifdef CONFIG_PCI_IOV
|
||||||
|
/* put SRIOV requested res to the optional list */
|
||||||
|
if (realloc_head && i >= PCI_IOV_RESOURCES &&
|
||||||
|
i <= PCI_IOV_RESOURCE_END) {
|
||||||
|
r->end = r->start - 1;
|
||||||
|
add_to_list(realloc_head, dev, r, r_size, 0/* dont' care */);
|
||||||
|
children_add_size += r_size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
/* For bridges size != alignment */
|
/* For bridges size != alignment */
|
||||||
align = pci_resource_alignment(dev, r);
|
align = pci_resource_alignment(dev, r);
|
||||||
order = __ffs(align) - 20;
|
order = __ffs(align) - 20;
|
||||||
|
@ -665,6 +704,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
||||||
if (order > max_order)
|
if (order > max_order)
|
||||||
max_order = order;
|
max_order = order;
|
||||||
mem64_mask &= r->flags & IORESOURCE_MEM_64;
|
mem64_mask &= r->flags & IORESOURCE_MEM_64;
|
||||||
|
|
||||||
|
if (realloc_head)
|
||||||
|
children_add_size += get_res_add_size(realloc_head, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
align = 0;
|
align = 0;
|
||||||
|
@ -681,7 +723,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
||||||
align += aligns[order];
|
align += aligns[order];
|
||||||
}
|
}
|
||||||
size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
|
size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
|
||||||
size1 = (!add_head || (add_head && !add_size)) ? size0 :
|
if (children_add_size > add_size)
|
||||||
|
add_size = children_add_size;
|
||||||
|
size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
|
||||||
calculate_memsize(size, min_size+add_size, 0,
|
calculate_memsize(size, min_size+add_size, 0,
|
||||||
resource_size(b_res), min_align);
|
resource_size(b_res), min_align);
|
||||||
if (!size0 && !size1) {
|
if (!size0 && !size1) {
|
||||||
|
@ -695,12 +739,22 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
|
||||||
b_res->start = min_align;
|
b_res->start = min_align;
|
||||||
b_res->end = size0 + min_align - 1;
|
b_res->end = size0 + min_align - 1;
|
||||||
b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask;
|
b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask;
|
||||||
if (size1 > size0 && add_head)
|
if (size1 > size0 && realloc_head)
|
||||||
add_to_list(add_head, bus->self, b_res, size1-size0);
|
add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pci_bus_size_cardbus(struct pci_bus *bus)
|
unsigned long pci_cardbus_resource_alignment(struct resource *res)
|
||||||
|
{
|
||||||
|
if (res->flags & IORESOURCE_IO)
|
||||||
|
return pci_cardbus_io_size;
|
||||||
|
if (res->flags & IORESOURCE_MEM)
|
||||||
|
return pci_cardbus_mem_size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pci_bus_size_cardbus(struct pci_bus *bus,
|
||||||
|
struct resource_list_x *realloc_head)
|
||||||
{
|
{
|
||||||
struct pci_dev *bridge = bus->self;
|
struct pci_dev *bridge = bus->self;
|
||||||
struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
|
struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
|
||||||
|
@ -711,12 +765,14 @@ static void pci_bus_size_cardbus(struct pci_bus *bus)
|
||||||
* a fixed amount of bus space for CardBus bridges.
|
* a fixed amount of bus space for CardBus bridges.
|
||||||
*/
|
*/
|
||||||
b_res[0].start = 0;
|
b_res[0].start = 0;
|
||||||
b_res[0].end = pci_cardbus_io_size - 1;
|
|
||||||
b_res[0].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN;
|
b_res[0].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN;
|
||||||
|
if (realloc_head)
|
||||||
|
add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, 0 /* dont care */);
|
||||||
|
|
||||||
b_res[1].start = 0;
|
b_res[1].start = 0;
|
||||||
b_res[1].end = pci_cardbus_io_size - 1;
|
|
||||||
b_res[1].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN;
|
b_res[1].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN;
|
||||||
|
if (realloc_head)
|
||||||
|
add_to_list(realloc_head, bridge, b_res+1, pci_cardbus_io_size, 0 /* dont care */);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check whether prefetchable memory is supported
|
* Check whether prefetchable memory is supported
|
||||||
|
@ -736,21 +792,31 @@ static void pci_bus_size_cardbus(struct pci_bus *bus)
|
||||||
*/
|
*/
|
||||||
if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
|
if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
|
||||||
b_res[2].start = 0;
|
b_res[2].start = 0;
|
||||||
b_res[2].end = pci_cardbus_mem_size - 1;
|
|
||||||
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_SIZEALIGN;
|
b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_SIZEALIGN;
|
||||||
|
if (realloc_head)
|
||||||
|
add_to_list(realloc_head, bridge, b_res+2, pci_cardbus_mem_size, 0 /* dont care */);
|
||||||
|
|
||||||
b_res[3].start = 0;
|
b_res[3].start = 0;
|
||||||
b_res[3].end = pci_cardbus_mem_size - 1;
|
|
||||||
b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN;
|
b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN;
|
||||||
|
if (realloc_head)
|
||||||
|
add_to_list(realloc_head, bridge, b_res+3, pci_cardbus_mem_size, 0 /* dont care */);
|
||||||
} else {
|
} else {
|
||||||
b_res[3].start = 0;
|
b_res[3].start = 0;
|
||||||
b_res[3].end = pci_cardbus_mem_size * 2 - 1;
|
|
||||||
b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN;
|
b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN;
|
||||||
|
if (realloc_head)
|
||||||
|
add_to_list(realloc_head, bridge, b_res+3, pci_cardbus_mem_size * 2, 0 /* dont care */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* set the size of the resource to zero, so that the resource does not
|
||||||
|
* get assigned during required-resource allocation cycle but gets assigned
|
||||||
|
* during the optional-resource allocation cycle.
|
||||||
|
*/
|
||||||
|
b_res[0].start = b_res[1].start = b_res[2].start = b_res[3].start = 1;
|
||||||
|
b_res[0].end = b_res[1].end = b_res[2].end = b_res[3].end = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __ref __pci_bus_size_bridges(struct pci_bus *bus,
|
void __ref __pci_bus_size_bridges(struct pci_bus *bus,
|
||||||
struct resource_list_x *add_head)
|
struct resource_list_x *realloc_head)
|
||||||
{
|
{
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
unsigned long mask, prefmask;
|
unsigned long mask, prefmask;
|
||||||
|
@ -763,12 +829,12 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus,
|
||||||
|
|
||||||
switch (dev->class >> 8) {
|
switch (dev->class >> 8) {
|
||||||
case PCI_CLASS_BRIDGE_CARDBUS:
|
case PCI_CLASS_BRIDGE_CARDBUS:
|
||||||
pci_bus_size_cardbus(b);
|
pci_bus_size_cardbus(b, realloc_head);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PCI_CLASS_BRIDGE_PCI:
|
case PCI_CLASS_BRIDGE_PCI:
|
||||||
default:
|
default:
|
||||||
__pci_bus_size_bridges(b, add_head);
|
__pci_bus_size_bridges(b, realloc_head);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -792,7 +858,7 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus,
|
||||||
* Follow thru
|
* Follow thru
|
||||||
*/
|
*/
|
||||||
default:
|
default:
|
||||||
pbus_size_io(bus, 0, additional_io_size, add_head);
|
pbus_size_io(bus, 0, additional_io_size, realloc_head);
|
||||||
/* If the bridge supports prefetchable range, size it
|
/* If the bridge supports prefetchable range, size it
|
||||||
separately. If it doesn't, or its prefetchable window
|
separately. If it doesn't, or its prefetchable window
|
||||||
has already been allocated by arch code, try
|
has already been allocated by arch code, try
|
||||||
|
@ -800,11 +866,11 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus,
|
||||||
resources. */
|
resources. */
|
||||||
mask = IORESOURCE_MEM;
|
mask = IORESOURCE_MEM;
|
||||||
prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;
|
prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;
|
||||||
if (pbus_size_mem(bus, prefmask, prefmask, 0, additional_mem_size, add_head))
|
if (pbus_size_mem(bus, prefmask, prefmask, 0, additional_mem_size, realloc_head))
|
||||||
mask = prefmask; /* Success, size non-prefetch only. */
|
mask = prefmask; /* Success, size non-prefetch only. */
|
||||||
else
|
else
|
||||||
additional_mem_size += additional_mem_size;
|
additional_mem_size += additional_mem_size;
|
||||||
pbus_size_mem(bus, mask, IORESOURCE_MEM, 0, additional_mem_size, add_head);
|
pbus_size_mem(bus, mask, IORESOURCE_MEM, 0, additional_mem_size, realloc_head);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -816,20 +882,20 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus)
|
||||||
EXPORT_SYMBOL(pci_bus_size_bridges);
|
EXPORT_SYMBOL(pci_bus_size_bridges);
|
||||||
|
|
||||||
static void __ref __pci_bus_assign_resources(const struct pci_bus *bus,
|
static void __ref __pci_bus_assign_resources(const struct pci_bus *bus,
|
||||||
struct resource_list_x *add_head,
|
struct resource_list_x *realloc_head,
|
||||||
struct resource_list_x *fail_head)
|
struct resource_list_x *fail_head)
|
||||||
{
|
{
|
||||||
struct pci_bus *b;
|
struct pci_bus *b;
|
||||||
struct pci_dev *dev;
|
struct pci_dev *dev;
|
||||||
|
|
||||||
pbus_assign_resources_sorted(bus, add_head, fail_head);
|
pbus_assign_resources_sorted(bus, realloc_head, fail_head);
|
||||||
|
|
||||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||||
b = dev->subordinate;
|
b = dev->subordinate;
|
||||||
if (!b)
|
if (!b)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
__pci_bus_assign_resources(b, add_head, fail_head);
|
__pci_bus_assign_resources(b, realloc_head, fail_head);
|
||||||
|
|
||||||
switch (dev->class >> 8) {
|
switch (dev->class >> 8) {
|
||||||
case PCI_CLASS_BRIDGE_PCI:
|
case PCI_CLASS_BRIDGE_PCI:
|
||||||
|
@ -1039,7 +1105,7 @@ void __init
|
||||||
pci_assign_unassigned_resources(void)
|
pci_assign_unassigned_resources(void)
|
||||||
{
|
{
|
||||||
struct pci_bus *bus;
|
struct pci_bus *bus;
|
||||||
struct resource_list_x add_list; /* list of resources that
|
struct resource_list_x realloc_list; /* list of resources that
|
||||||
want additional resources */
|
want additional resources */
|
||||||
int tried_times = 0;
|
int tried_times = 0;
|
||||||
enum release_type rel_type = leaf_only;
|
enum release_type rel_type = leaf_only;
|
||||||
|
@ -1052,7 +1118,7 @@ pci_assign_unassigned_resources(void)
|
||||||
|
|
||||||
|
|
||||||
head.next = NULL;
|
head.next = NULL;
|
||||||
add_list.next = NULL;
|
realloc_list.next = NULL;
|
||||||
|
|
||||||
pci_try_num = max_depth + 1;
|
pci_try_num = max_depth + 1;
|
||||||
printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n",
|
printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n",
|
||||||
|
@ -1062,12 +1128,12 @@ pci_assign_unassigned_resources(void)
|
||||||
/* Depth first, calculate sizes and alignments of all
|
/* Depth first, calculate sizes and alignments of all
|
||||||
subordinate buses. */
|
subordinate buses. */
|
||||||
list_for_each_entry(bus, &pci_root_buses, node)
|
list_for_each_entry(bus, &pci_root_buses, node)
|
||||||
__pci_bus_size_bridges(bus, &add_list);
|
__pci_bus_size_bridges(bus, &realloc_list);
|
||||||
|
|
||||||
/* Depth last, allocate resources and update the hardware. */
|
/* Depth last, allocate resources and update the hardware. */
|
||||||
list_for_each_entry(bus, &pci_root_buses, node)
|
list_for_each_entry(bus, &pci_root_buses, node)
|
||||||
__pci_bus_assign_resources(bus, &add_list, &head);
|
__pci_bus_assign_resources(bus, &realloc_list, &head);
|
||||||
BUG_ON(add_list.next);
|
BUG_ON(realloc_list.next);
|
||||||
tried_times++;
|
tried_times++;
|
||||||
|
|
||||||
/* any device complain? */
|
/* any device complain? */
|
||||||
|
|
|
@ -128,16 +128,16 @@ void pci_disable_bridge_window(struct pci_dev *dev)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_PCI_QUIRKS */
|
#endif /* CONFIG_PCI_QUIRKS */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
|
static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
|
||||||
int resno)
|
int resno, resource_size_t size, resource_size_t align)
|
||||||
{
|
{
|
||||||
struct resource *res = dev->resource + resno;
|
struct resource *res = dev->resource + resno;
|
||||||
resource_size_t size, min, align;
|
resource_size_t min;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
size = resource_size(res);
|
|
||||||
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
|
min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
|
||||||
align = pci_resource_alignment(dev, res);
|
|
||||||
|
|
||||||
/* First, try exact prefetching match.. */
|
/* First, try exact prefetching match.. */
|
||||||
ret = pci_bus_alloc_resource(bus, res, size, align, min,
|
ret = pci_bus_alloc_resource(bus, res, size, align, min,
|
||||||
|
@ -154,73 +154,51 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
|
||||||
ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
|
ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
|
||||||
pcibios_align_resource, dev);
|
pcibios_align_resource, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret < 0 && dev->fw_addr[resno]) {
|
|
||||||
struct resource *root, *conflict;
|
|
||||||
resource_size_t start, end;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (res->flags & IORESOURCE_IO)
|
|
||||||
root = &ioport_resource;
|
|
||||||
else
|
|
||||||
root = &iomem_resource;
|
|
||||||
|
|
||||||
start = res->start;
|
|
||||||
end = res->end;
|
|
||||||
res->start = dev->fw_addr[resno];
|
|
||||||
res->end = res->start + size - 1;
|
|
||||||
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;
|
|
||||||
} else
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pci_assign_resource(struct pci_dev *dev, int resno)
|
static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
|
||||||
|
int resno, resource_size_t size)
|
||||||
|
{
|
||||||
|
struct resource *root, *conflict;
|
||||||
|
resource_size_t start, end;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (res->flags & IORESOURCE_IO)
|
||||||
|
root = &ioport_resource;
|
||||||
|
else
|
||||||
|
root = &iomem_resource;
|
||||||
|
|
||||||
|
start = res->start;
|
||||||
|
end = res->end;
|
||||||
|
res->start = dev->fw_addr[resno];
|
||||||
|
res->end = res->start + size - 1;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _pci_assign_resource(struct pci_dev *dev, int resno, int size, resource_size_t min_align)
|
||||||
{
|
{
|
||||||
struct resource *res = dev->resource + resno;
|
struct resource *res = dev->resource + resno;
|
||||||
resource_size_t align;
|
|
||||||
struct pci_bus *bus;
|
struct pci_bus *bus;
|
||||||
int ret;
|
int ret;
|
||||||
char *type;
|
char *type;
|
||||||
|
|
||||||
align = pci_resource_alignment(dev, res);
|
|
||||||
if (!align) {
|
|
||||||
dev_info(&dev->dev, "BAR %d: can't assign %pR "
|
|
||||||
"(bogus alignment)\n", resno, res);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bus = dev->bus;
|
bus = dev->bus;
|
||||||
while ((ret = __pci_assign_resource(bus, dev, resno))) {
|
while ((ret = __pci_assign_resource(bus, dev, resno, size, min_align))) {
|
||||||
if (bus->parent && bus->self->transparent)
|
if (!bus->parent || !bus->self->transparent)
|
||||||
bus = bus->parent;
|
break;
|
||||||
else
|
bus = bus->parent;
|
||||||
bus = NULL;
|
|
||||||
if (bus)
|
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -241,6 +219,66 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
|
||||||
return ret;
|
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) {
|
||||||
|
dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resouce %pR "
|
||||||
|
"\n", resno, res);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_size = resource_size(res) + addsize + min_align;
|
||||||
|
ret = _pci_assign_resource(dev, resno, new_size, min_align);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pci_assign_resource(struct pci_dev *dev, int resno)
|
||||||
|
{
|
||||||
|
struct resource *res = dev->resource + resno;
|
||||||
|
resource_size_t align, size;
|
||||||
|
struct pci_bus *bus;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
align = pci_resource_alignment(dev, res);
|
||||||
|
if (!align) {
|
||||||
|
dev_info(&dev->dev, "BAR %d: can't assign %pR "
|
||||||
|
"(bogus alignment)\n", resno, res);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bus = dev->bus;
|
||||||
|
size = resource_size(res);
|
||||||
|
ret = _pci_assign_resource(dev, resno, size, align);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
if (ret < 0 && dev->fw_addr[resno])
|
||||||
|
ret = pci_revert_fw_address(res, dev, resno, size);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Sort resources by alignment */
|
/* Sort resources by alignment */
|
||||||
void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
|
void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
|
||||||
{
|
{
|
||||||
|
|
|
@ -251,7 +251,8 @@ struct pci_dev {
|
||||||
u8 revision; /* PCI revision, low byte of class word */
|
u8 revision; /* PCI revision, low byte of class word */
|
||||||
u8 hdr_type; /* PCI header type (`multi' flag masked out) */
|
u8 hdr_type; /* PCI header type (`multi' flag masked out) */
|
||||||
u8 pcie_cap; /* PCI-E capability offset */
|
u8 pcie_cap; /* PCI-E capability offset */
|
||||||
u8 pcie_type; /* PCI-E device/port type */
|
u8 pcie_type:4; /* PCI-E device/port type */
|
||||||
|
u8 pcie_mpss:3; /* PCI-E Max Payload Size Supported */
|
||||||
u8 rom_base_reg; /* which config register controls the ROM */
|
u8 rom_base_reg; /* which config register controls the ROM */
|
||||||
u8 pin; /* which interrupt pin this device uses */
|
u8 pin; /* which interrupt pin this device uses */
|
||||||
|
|
||||||
|
@ -617,6 +618,16 @@ struct pci_driver {
|
||||||
/* these external functions are only available when PCI support is enabled */
|
/* these external functions are only available when PCI support is enabled */
|
||||||
#ifdef CONFIG_PCI
|
#ifdef CONFIG_PCI
|
||||||
|
|
||||||
|
extern void pcie_bus_configure_settings(struct pci_bus *bus, u8 smpss);
|
||||||
|
|
||||||
|
enum pcie_bus_config_types {
|
||||||
|
PCIE_BUS_PERFORMANCE,
|
||||||
|
PCIE_BUS_SAFE,
|
||||||
|
PCIE_BUS_PEER2PEER,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern enum pcie_bus_config_types pcie_bus_config;
|
||||||
|
|
||||||
extern struct bus_type pci_bus_type;
|
extern struct bus_type pci_bus_type;
|
||||||
|
|
||||||
/* Do NOT directly access these two variables, unless you are arch specific pci
|
/* Do NOT directly access these two variables, unless you are arch specific pci
|
||||||
|
@ -796,10 +807,13 @@ int pcix_get_mmrbc(struct pci_dev *dev);
|
||||||
int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc);
|
int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc);
|
||||||
int pcie_get_readrq(struct pci_dev *dev);
|
int pcie_get_readrq(struct pci_dev *dev);
|
||||||
int pcie_set_readrq(struct pci_dev *dev, int rq);
|
int pcie_set_readrq(struct pci_dev *dev, int rq);
|
||||||
|
int pcie_get_mps(struct pci_dev *dev);
|
||||||
|
int pcie_set_mps(struct pci_dev *dev, int mps);
|
||||||
int __pci_reset_function(struct pci_dev *dev);
|
int __pci_reset_function(struct pci_dev *dev);
|
||||||
int pci_reset_function(struct pci_dev *dev);
|
int pci_reset_function(struct pci_dev *dev);
|
||||||
void pci_update_resource(struct pci_dev *dev, int resno);
|
void pci_update_resource(struct pci_dev *dev, int resno);
|
||||||
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
|
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
|
||||||
|
int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);
|
||||||
int pci_select_bars(struct pci_dev *dev, unsigned long flags);
|
int pci_select_bars(struct pci_dev *dev, unsigned long flags);
|
||||||
|
|
||||||
/* ROM control related routines */
|
/* ROM control related routines */
|
||||||
|
|
Loading…
Reference in a new issue