mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 11:18:45 +07:00
Merge branch 'pci/hotplug' into next
* pci/hotplug: PCI: pciehp: Do not clear Presence Detect Changed during initialization PCI: pciehp: Fix race condition handling surprise link down PCI: Distribute available resources to hotplug-capable bridges PCI: Distribute available buses to hotplug-capable bridges PCI: Do not allocate more buses than available in parent PCI: Open-code the two pass loop when scanning bridges PCI: Move pci_hp_add_bridge() to drivers/pci/probe.c PCI: Add for_each_pci_bridge() helper PCI: shpchp: Convert timers to use timer_setup() PCI: cpqphp: Convert timers to use timer_setup() PCI: pciehp: Convert timers to use timer_setup() PCI: ibmphp: Use common error handling code in unconfigure_boot_device()
This commit is contained in:
commit
8d666e53e0
@ -16,9 +16,6 @@ obj-$(CONFIG_PCIEPORTBUS) += pcie/
|
||||
|
||||
# Build the PCI Hotplug drivers if we were asked to
|
||||
obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
|
||||
ifdef CONFIG_HOTPLUG_PCI
|
||||
obj-y += hotplug-pci.o
|
||||
endif
|
||||
|
||||
# Build the PCI MSI interrupt support
|
||||
obj-$(CONFIG_PCI_MSI) += msi.o
|
||||
|
@ -1,29 +0,0 @@
|
||||
/* Core PCI functionality used only by PCI hotplug */
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/export.h>
|
||||
#include "pci.h"
|
||||
|
||||
int pci_hp_add_bridge(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_bus *parent = dev->bus;
|
||||
int pass, busnr, start = parent->busn_res.start;
|
||||
int end = parent->busn_res.end;
|
||||
|
||||
for (busnr = start; busnr <= end; busnr++) {
|
||||
if (!pci_find_bus(pci_domain_nr(parent), busnr))
|
||||
break;
|
||||
}
|
||||
if (busnr-- > end) {
|
||||
printk(KERN_ERR "No bus number available for hot-added bridge %s\n",
|
||||
pci_name(dev));
|
||||
return -1;
|
||||
}
|
||||
for (pass = 0; pass < 2; pass++)
|
||||
busnr = pci_scan_bridge(parent, dev, busnr, pass);
|
||||
if (!dev->subordinate)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_hp_add_bridge);
|
@ -462,18 +462,15 @@ static void enable_slot(struct acpiphp_slot *slot)
|
||||
acpiphp_rescan_slot(slot);
|
||||
max = acpiphp_max_busnr(bus);
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
if (PCI_SLOT(dev->devfn) != slot->device)
|
||||
continue;
|
||||
|
||||
if (pci_is_bridge(dev)) {
|
||||
max = pci_scan_bridge(bus, dev, max, pass);
|
||||
if (pass && dev->subordinate) {
|
||||
check_hotplug_bridge(slot, dev);
|
||||
pcibios_resource_survey_bus(dev->subordinate);
|
||||
__pci_bus_size_bridges(dev->subordinate,
|
||||
&add_list);
|
||||
}
|
||||
max = pci_scan_bridge(bus, dev, max, pass);
|
||||
if (pass && dev->subordinate) {
|
||||
check_hotplug_bridge(slot, dev);
|
||||
pcibios_resource_survey_bus(dev->subordinate);
|
||||
__pci_bus_size_bridges(dev->subordinate, &add_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -286,14 +286,11 @@ int cpci_configure_slot(struct slot *slot)
|
||||
}
|
||||
parent = slot->dev->bus;
|
||||
|
||||
list_for_each_entry(dev, &parent->devices, bus_list) {
|
||||
if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn))
|
||||
continue;
|
||||
if (pci_is_bridge(dev))
|
||||
for_each_pci_bridge(dev, parent) {
|
||||
if (PCI_SLOT(dev->devfn) == PCI_SLOT(slot->devfn))
|
||||
pci_hp_add_bridge(dev);
|
||||
}
|
||||
|
||||
|
||||
pci_assign_unassigned_bridge_resources(parent->self);
|
||||
|
||||
pci_bus_add_devices(parent);
|
||||
|
@ -410,7 +410,7 @@ void cpqhp_create_debugfs_files(struct controller *ctrl);
|
||||
void cpqhp_remove_debugfs_files(struct controller *ctrl);
|
||||
|
||||
/* controller functions */
|
||||
void cpqhp_pushbutton_thread(unsigned long event_pointer);
|
||||
void cpqhp_pushbutton_thread(struct timer_list *t);
|
||||
irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data);
|
||||
int cpqhp_find_available_resources(struct controller *ctrl,
|
||||
void __iomem *rom_start);
|
||||
|
@ -661,9 +661,8 @@ static int ctrl_slot_setup(struct controller *ctrl,
|
||||
|
||||
slot->p_sm_slot = slot_entry;
|
||||
|
||||
init_timer(&slot->task_event);
|
||||
timer_setup(&slot->task_event, cpqhp_pushbutton_thread, 0);
|
||||
slot->task_event.expires = jiffies + 5 * HZ;
|
||||
slot->task_event.function = cpqhp_pushbutton_thread;
|
||||
|
||||
/*FIXME: these capabilities aren't used but if they are
|
||||
* they need to be correctly implemented
|
||||
|
@ -47,7 +47,7 @@ static void interrupt_event_handler(struct controller *ctrl);
|
||||
|
||||
|
||||
static struct task_struct *cpqhp_event_thread;
|
||||
static unsigned long pushbutton_pending; /* = 0 */
|
||||
static struct timer_list *pushbutton_pending; /* = NULL */
|
||||
|
||||
/* delay is in jiffies to wait for */
|
||||
static void long_delay(int delay)
|
||||
@ -1732,9 +1732,10 @@ static u32 remove_board(struct pci_func *func, u32 replace_flag, struct controll
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pushbutton_helper_thread(unsigned long data)
|
||||
static void pushbutton_helper_thread(struct timer_list *t)
|
||||
{
|
||||
pushbutton_pending = data;
|
||||
pushbutton_pending = t;
|
||||
|
||||
wake_up_process(cpqhp_event_thread);
|
||||
}
|
||||
|
||||
@ -1883,13 +1884,13 @@ static void interrupt_event_handler(struct controller *ctrl)
|
||||
wait_for_ctrl_irq(ctrl);
|
||||
|
||||
mutex_unlock(&ctrl->crit_sect);
|
||||
init_timer(&p_slot->task_event);
|
||||
timer_setup(&p_slot->task_event,
|
||||
pushbutton_helper_thread,
|
||||
0);
|
||||
p_slot->hp_slot = hp_slot;
|
||||
p_slot->ctrl = ctrl;
|
||||
/* p_slot->physical_slot = physical_slot; */
|
||||
p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */
|
||||
p_slot->task_event.function = pushbutton_helper_thread;
|
||||
p_slot->task_event.data = (u32) p_slot;
|
||||
|
||||
dbg("add_timer p_slot = %p\n", p_slot);
|
||||
add_timer(&p_slot->task_event);
|
||||
@ -1920,15 +1921,15 @@ static void interrupt_event_handler(struct controller *ctrl)
|
||||
* Scheduled procedure to handle blocking stuff for the pushbuttons.
|
||||
* Handles all pending events and exits.
|
||||
*/
|
||||
void cpqhp_pushbutton_thread(unsigned long slot)
|
||||
void cpqhp_pushbutton_thread(struct timer_list *t)
|
||||
{
|
||||
u8 hp_slot;
|
||||
u8 device;
|
||||
struct pci_func *func;
|
||||
struct slot *p_slot = (struct slot *) slot;
|
||||
struct slot *p_slot = from_timer(p_slot, t, task_event);
|
||||
struct controller *ctrl = (struct controller *) p_slot->ctrl;
|
||||
|
||||
pushbutton_pending = 0;
|
||||
pushbutton_pending = NULL;
|
||||
hp_slot = p_slot->hp_slot;
|
||||
|
||||
device = p_slot->device;
|
||||
|
@ -1267,20 +1267,19 @@ static int unconfigure_boot_device(u8 busno, u8 device, u8 function)
|
||||
size = size & 0xFFFFFFFC;
|
||||
size = ~size + 1;
|
||||
end_address = start_address + size - 1;
|
||||
if (ibmphp_find_resource(bus, start_address, &io, IO) < 0) {
|
||||
err("cannot find corresponding IO resource to remove\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (ibmphp_find_resource(bus, start_address, &io, IO))
|
||||
goto report_search_failure;
|
||||
|
||||
debug("io->start = %x\n", io->start);
|
||||
temp_end = io->end;
|
||||
start_address = io->end + 1;
|
||||
ibmphp_remove_resource(io);
|
||||
/* This is needed b/c of the old I/O restrictions in the BIOS */
|
||||
while (temp_end < end_address) {
|
||||
if (ibmphp_find_resource(bus, start_address, &io, IO) < 0) {
|
||||
err("cannot find corresponding IO resource to remove\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (ibmphp_find_resource(bus, start_address,
|
||||
&io, IO))
|
||||
goto report_search_failure;
|
||||
|
||||
debug("io->start = %x\n", io->start);
|
||||
temp_end = io->end;
|
||||
start_address = io->end + 1;
|
||||
@ -1327,6 +1326,10 @@ static int unconfigure_boot_device(u8 busno, u8 device, u8 function)
|
||||
} /* end of for */
|
||||
|
||||
return 0;
|
||||
|
||||
report_search_failure:
|
||||
err("cannot find corresponding IO resource to remove\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int unconfigure_boot_bridge(u8 busno, u8 device, u8 function)
|
||||
|
@ -113,10 +113,11 @@ static int board_added(struct slot *p_slot)
|
||||
|
||||
retval = pciehp_configure_device(p_slot);
|
||||
if (retval) {
|
||||
ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
|
||||
pci_domain_nr(parent), parent->number);
|
||||
if (retval != -EEXIST)
|
||||
if (retval != -EEXIST) {
|
||||
ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
|
||||
pci_domain_nr(parent), parent->number);
|
||||
goto err_exit;
|
||||
}
|
||||
}
|
||||
|
||||
pciehp_green_led_on(p_slot);
|
||||
|
@ -50,14 +50,13 @@ static irqreturn_t pcie_isr(int irq, void *dev_id);
|
||||
static void start_int_poll_timer(struct controller *ctrl, int sec);
|
||||
|
||||
/* This is the interrupt polling timeout function. */
|
||||
static void int_poll_timeout(unsigned long data)
|
||||
static void int_poll_timeout(struct timer_list *t)
|
||||
{
|
||||
struct controller *ctrl = (struct controller *)data;
|
||||
struct controller *ctrl = from_timer(ctrl, t, poll_timer);
|
||||
|
||||
/* Poll for interrupt events. regs == NULL => polling */
|
||||
pcie_isr(0, ctrl);
|
||||
|
||||
init_timer(&ctrl->poll_timer);
|
||||
if (!pciehp_poll_time)
|
||||
pciehp_poll_time = 2; /* default polling interval is 2 sec */
|
||||
|
||||
@ -71,8 +70,6 @@ static void start_int_poll_timer(struct controller *ctrl, int sec)
|
||||
if ((sec <= 0) || (sec > 60))
|
||||
sec = 2;
|
||||
|
||||
ctrl->poll_timer.function = &int_poll_timeout;
|
||||
ctrl->poll_timer.data = (unsigned long)ctrl;
|
||||
ctrl->poll_timer.expires = jiffies + sec * HZ;
|
||||
add_timer(&ctrl->poll_timer);
|
||||
}
|
||||
@ -83,7 +80,7 @@ static inline int pciehp_request_irq(struct controller *ctrl)
|
||||
|
||||
/* Install interrupt polling timer. Start with 10 sec delay */
|
||||
if (pciehp_poll_mode) {
|
||||
init_timer(&ctrl->poll_timer);
|
||||
timer_setup(&ctrl->poll_timer, int_poll_timeout, 0);
|
||||
start_int_poll_timer(ctrl, 10);
|
||||
return 0;
|
||||
}
|
||||
@ -764,8 +761,7 @@ int pciehp_reset_slot(struct slot *slot, int probe)
|
||||
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
||||
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, ctrl_mask);
|
||||
if (pciehp_poll_mode)
|
||||
int_poll_timeout(ctrl->poll_timer.data);
|
||||
|
||||
int_poll_timeout(&ctrl->poll_timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -795,7 +791,7 @@ static int pcie_init_slot(struct controller *ctrl)
|
||||
if (!slot)
|
||||
return -ENOMEM;
|
||||
|
||||
slot->wq = alloc_workqueue("pciehp-%u", 0, 0, PSN(ctrl));
|
||||
slot->wq = alloc_ordered_workqueue("pciehp-%u", 0, PSN(ctrl));
|
||||
if (!slot->wq)
|
||||
goto abort;
|
||||
|
||||
@ -862,11 +858,16 @@ struct controller *pcie_init(struct pcie_device *dev)
|
||||
if (link_cap & PCI_EXP_LNKCAP_DLLLARC)
|
||||
ctrl->link_active_reporting = 1;
|
||||
|
||||
/* Clear all remaining event bits in Slot Status register */
|
||||
/*
|
||||
* Clear all remaining event bits in Slot Status register except
|
||||
* Presence Detect Changed. We want to make sure possible
|
||||
* hotplug event is triggered when the interrupt is unmasked so
|
||||
* that we don't lose that event.
|
||||
*/
|
||||
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
|
||||
PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
|
||||
PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC |
|
||||
PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
|
||||
PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC |
|
||||
PCI_EXP_SLTSTA_DLLSC);
|
||||
|
||||
ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c LLActRep%c\n",
|
||||
(slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
|
||||
|
@ -46,7 +46,11 @@ int pciehp_configure_device(struct slot *p_slot)
|
||||
|
||||
dev = pci_get_slot(parent, PCI_DEVFN(0, 0));
|
||||
if (dev) {
|
||||
ctrl_err(ctrl, "Device %s already exists at %04x:%02x:00, cannot hot-add\n",
|
||||
/*
|
||||
* The device is already there. Either configured by the
|
||||
* boot firmware or a previous hotplug event.
|
||||
*/
|
||||
ctrl_dbg(ctrl, "Device %s already exists at %04x:%02x:00, skipping hot-add\n",
|
||||
pci_name(dev), pci_domain_nr(parent), parent->number);
|
||||
pci_dev_put(dev);
|
||||
ret = -EEXIST;
|
||||
@ -60,9 +64,8 @@ int pciehp_configure_device(struct slot *p_slot)
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(dev, &parent->devices, bus_list)
|
||||
if (pci_is_bridge(dev))
|
||||
pci_hp_add_bridge(dev);
|
||||
for_each_pci_bridge(dev, parent)
|
||||
pci_hp_add_bridge(dev);
|
||||
|
||||
pci_assign_unassigned_bridge_resources(bridge);
|
||||
pcie_bus_configure_settings(parent);
|
||||
|
@ -229,14 +229,13 @@ static inline int shpc_indirect_read(struct controller *ctrl, int index,
|
||||
/*
|
||||
* This is the interrupt polling timeout function.
|
||||
*/
|
||||
static void int_poll_timeout(unsigned long data)
|
||||
static void int_poll_timeout(struct timer_list *t)
|
||||
{
|
||||
struct controller *ctrl = (struct controller *)data;
|
||||
struct controller *ctrl = from_timer(ctrl, t, poll_timer);
|
||||
|
||||
/* Poll for interrupt events. regs == NULL => polling */
|
||||
shpc_isr(0, ctrl);
|
||||
|
||||
init_timer(&ctrl->poll_timer);
|
||||
if (!shpchp_poll_time)
|
||||
shpchp_poll_time = 2; /* default polling interval is 2 sec */
|
||||
|
||||
@ -252,8 +251,6 @@ static void start_int_poll_timer(struct controller *ctrl, int sec)
|
||||
if ((sec <= 0) || (sec > 60))
|
||||
sec = 2;
|
||||
|
||||
ctrl->poll_timer.function = &int_poll_timeout;
|
||||
ctrl->poll_timer.data = (unsigned long)ctrl;
|
||||
ctrl->poll_timer.expires = jiffies + sec * HZ;
|
||||
add_timer(&ctrl->poll_timer);
|
||||
}
|
||||
@ -1054,7 +1051,7 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
|
||||
|
||||
if (shpchp_poll_mode) {
|
||||
/* Install interrupt polling timer. Start with 10 sec delay */
|
||||
init_timer(&ctrl->poll_timer);
|
||||
timer_setup(&ctrl->poll_timer, int_poll_timeout, 0);
|
||||
start_int_poll_timer(ctrl, 10);
|
||||
} else {
|
||||
/* Installs the interrupt handler */
|
||||
|
@ -61,10 +61,8 @@ int shpchp_configure_device(struct slot *p_slot)
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(dev, &parent->devices, bus_list) {
|
||||
if (PCI_SLOT(dev->devfn) != p_slot->device)
|
||||
continue;
|
||||
if (pci_is_bridge(dev))
|
||||
for_each_pci_bridge(dev, parent) {
|
||||
if (PCI_SLOT(dev->devfn) == p_slot->device)
|
||||
pci_hp_add_bridge(dev);
|
||||
}
|
||||
|
||||
|
@ -959,7 +959,21 @@ static void pci_enable_crs(struct pci_dev *pdev)
|
||||
PCI_EXP_RTCTL_CRSSVE);
|
||||
}
|
||||
|
||||
static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
|
||||
unsigned int available_buses);
|
||||
|
||||
/*
|
||||
* pci_scan_bridge_extend() - Scan buses behind a bridge
|
||||
* @bus: Parent bus the bridge is on
|
||||
* @dev: Bridge itself
|
||||
* @max: Starting subordinate number of buses behind this bridge
|
||||
* @available_buses: Total number of buses available for this bridge and
|
||||
* the devices below. After the minimal bus space has
|
||||
* been allocated the remaining buses will be
|
||||
* distributed equally between hotplug-capable bridges.
|
||||
* @pass: Either %0 (scan already configured bridges) or %1 (scan bridges
|
||||
* that need to be reconfigured.
|
||||
*
|
||||
* If it's a bridge, configure it and scan the bus behind it.
|
||||
* For CardBus bridges, we don't scan behind as the devices will
|
||||
* be handled by the bridge driver itself.
|
||||
@ -969,7 +983,9 @@ static void pci_enable_crs(struct pci_dev *pdev)
|
||||
* them, we proceed to assigning numbers to the remaining buses in
|
||||
* order to avoid overlaps between old and new bus numbers.
|
||||
*/
|
||||
int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
|
||||
static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
|
||||
int max, unsigned int available_buses,
|
||||
int pass)
|
||||
{
|
||||
struct pci_bus *child;
|
||||
int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS);
|
||||
@ -1076,9 +1092,13 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
|
||||
child = pci_add_new_bus(bus, dev, max+1);
|
||||
if (!child)
|
||||
goto out;
|
||||
pci_bus_insert_busn_res(child, max+1, 0xff);
|
||||
pci_bus_insert_busn_res(child, max+1,
|
||||
bus->busn_res.end);
|
||||
}
|
||||
max++;
|
||||
if (available_buses)
|
||||
available_buses--;
|
||||
|
||||
buses = (buses & 0xff000000)
|
||||
| ((unsigned int)(child->primary) << 0)
|
||||
| ((unsigned int)(child->busn_res.start) << 8)
|
||||
@ -1100,7 +1120,7 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
|
||||
|
||||
if (!is_cardbus) {
|
||||
child->bridge_ctl = bctl;
|
||||
max = pci_scan_child_bus(child);
|
||||
max = pci_scan_child_bus_extend(child, available_buses);
|
||||
} else {
|
||||
/*
|
||||
* For CardBus bridges, we leave 4 bus numbers
|
||||
@ -1168,6 +1188,28 @@ int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/*
|
||||
* pci_scan_bridge() - Scan buses behind a bridge
|
||||
* @bus: Parent bus the bridge is on
|
||||
* @dev: Bridge itself
|
||||
* @max: Starting subordinate number of buses behind this bridge
|
||||
* @pass: Either %0 (scan already configured bridges) or %1 (scan bridges
|
||||
* that need to be reconfigured.
|
||||
*
|
||||
* If it's a bridge, configure it and scan the bus behind it.
|
||||
* For CardBus bridges, we don't scan behind as the devices will
|
||||
* be handled by the bridge driver itself.
|
||||
*
|
||||
* We need to process bridges in two passes -- first we scan those
|
||||
* already configured by the BIOS and after we are done with all of
|
||||
* them, we proceed to assigning numbers to the remaining buses in
|
||||
* order to avoid overlaps between old and new bus numbers.
|
||||
*/
|
||||
int pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max, int pass)
|
||||
{
|
||||
return pci_scan_bridge_extend(bus, dev, max, 0, pass);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_scan_bridge);
|
||||
|
||||
/*
|
||||
@ -2396,9 +2438,24 @@ void __weak pcibios_fixup_bus(struct pci_bus *bus)
|
||||
/* nothing to do, expected to be removed in the future */
|
||||
}
|
||||
|
||||
unsigned int pci_scan_child_bus(struct pci_bus *bus)
|
||||
/**
|
||||
* pci_scan_child_bus_extend() - Scan devices below a bus
|
||||
* @bus: Bus to scan for devices
|
||||
* @available_buses: Total number of buses available (%0 does not try to
|
||||
* extend beyond the minimal)
|
||||
*
|
||||
* Scans devices below @bus including subordinate buses. Returns new
|
||||
* subordinate number including all the found devices. Passing
|
||||
* @available_buses causes the remaining bus space to be distributed
|
||||
* equally between hotplug-capable bridges to allow future extension of the
|
||||
* hierarchy.
|
||||
*/
|
||||
static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
|
||||
unsigned int available_buses)
|
||||
{
|
||||
unsigned int devfn, pass, max = bus->busn_res.start;
|
||||
unsigned int used_buses, normal_bridges = 0, hotplug_bridges = 0;
|
||||
unsigned int start = bus->busn_res.start;
|
||||
unsigned int devfn, cmax, max = start;
|
||||
struct pci_dev *dev;
|
||||
|
||||
dev_dbg(&bus->dev, "scanning bus\n");
|
||||
@ -2408,7 +2465,8 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus)
|
||||
pci_scan_slot(bus, devfn);
|
||||
|
||||
/* Reserve buses for SR-IOV capability. */
|
||||
max += pci_iov_bus_range(bus);
|
||||
used_buses = pci_iov_bus_range(bus);
|
||||
max += used_buses;
|
||||
|
||||
/*
|
||||
* After performing arch-dependent fixup of the bus, look behind
|
||||
@ -2420,19 +2478,73 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus)
|
||||
bus->is_added = 1;
|
||||
}
|
||||
|
||||
for (pass = 0; pass < 2; pass++)
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
if (pci_is_bridge(dev))
|
||||
max = pci_scan_bridge(bus, dev, max, pass);
|
||||
/*
|
||||
* Calculate how many hotplug bridges and normal bridges there
|
||||
* are on this bus. We will distribute the additional available
|
||||
* buses between hotplug bridges.
|
||||
*/
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
if (dev->is_hotplug_bridge)
|
||||
hotplug_bridges++;
|
||||
else
|
||||
normal_bridges++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan bridges that are already configured. We don't touch them
|
||||
* unless they are misconfigured (which will be done in the second
|
||||
* scan below).
|
||||
*/
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
cmax = max;
|
||||
max = pci_scan_bridge_extend(bus, dev, max, 0, 0);
|
||||
used_buses += cmax - max;
|
||||
}
|
||||
|
||||
/* Scan bridges that need to be reconfigured */
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
unsigned int buses = 0;
|
||||
|
||||
if (!hotplug_bridges && normal_bridges == 1) {
|
||||
/*
|
||||
* There is only one bridge on the bus (upstream
|
||||
* port) so it gets all available buses which it
|
||||
* can then distribute to the possible hotplug
|
||||
* bridges below.
|
||||
*/
|
||||
buses = available_buses;
|
||||
} else if (dev->is_hotplug_bridge) {
|
||||
/*
|
||||
* Distribute the extra buses between hotplug
|
||||
* bridges if any.
|
||||
*/
|
||||
buses = available_buses / hotplug_bridges;
|
||||
buses = min(buses, available_buses - used_buses);
|
||||
}
|
||||
|
||||
cmax = max;
|
||||
max = pci_scan_bridge_extend(bus, dev, cmax, buses, 1);
|
||||
used_buses += max - cmax;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure a hotplug bridge has at least the minimum requested
|
||||
* number of buses.
|
||||
* number of buses but allow it to grow up to the maximum available
|
||||
* bus number of there is room.
|
||||
*/
|
||||
if (bus->self && bus->self->is_hotplug_bridge && pci_hotplug_bus_size) {
|
||||
if (max - bus->busn_res.start < pci_hotplug_bus_size - 1)
|
||||
max = bus->busn_res.start + pci_hotplug_bus_size - 1;
|
||||
if (bus->self && bus->self->is_hotplug_bridge) {
|
||||
used_buses = max_t(unsigned int, available_buses,
|
||||
pci_hotplug_bus_size - 1);
|
||||
if (max - start < used_buses) {
|
||||
max = start + used_buses;
|
||||
|
||||
/* Do not allocate more buses than we have room left */
|
||||
if (max > bus->busn_res.end)
|
||||
max = bus->busn_res.end;
|
||||
|
||||
dev_dbg(&bus->dev, "%pR extended by %#02x\n",
|
||||
&bus->busn_res, max - start);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2445,6 +2557,18 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus)
|
||||
dev_dbg(&bus->dev, "bus scan returning with max=%02x\n", max);
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_scan_child_bus() - Scan devices below a bus
|
||||
* @bus: Bus to scan for devices
|
||||
*
|
||||
* Scans devices below @bus including subordinate buses. Returns new
|
||||
* subordinate number including all the found devices.
|
||||
*/
|
||||
unsigned int pci_scan_child_bus(struct pci_bus *bus)
|
||||
{
|
||||
return pci_scan_child_bus_extend(bus, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_scan_child_bus);
|
||||
|
||||
/**
|
||||
@ -2737,3 +2861,38 @@ void __init pci_sort_breadthfirst(void)
|
||||
{
|
||||
bus_sort_breadthfirst(&pci_bus_type, &pci_sort_bf_cmp);
|
||||
}
|
||||
|
||||
int pci_hp_add_bridge(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_bus *parent = dev->bus;
|
||||
int busnr, start = parent->busn_res.start;
|
||||
unsigned int available_buses = 0;
|
||||
int end = parent->busn_res.end;
|
||||
|
||||
for (busnr = start; busnr <= end; busnr++) {
|
||||
if (!pci_find_bus(pci_domain_nr(parent), busnr))
|
||||
break;
|
||||
}
|
||||
if (busnr-- > end) {
|
||||
dev_err(&dev->dev, "No bus number available for hot-added bridge\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Scan bridges that are already configured */
|
||||
busnr = pci_scan_bridge(parent, dev, busnr, 0);
|
||||
|
||||
/*
|
||||
* Distribute the available bus numbers between hotplug-capable
|
||||
* bridges to make extending the chain later possible.
|
||||
*/
|
||||
available_buses = end - busnr;
|
||||
|
||||
/* Scan bridges that need to be reconfigured */
|
||||
pci_scan_bridge_extend(parent, dev, busnr, available_buses, 1);
|
||||
|
||||
if (!dev->subordinate)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_hp_add_bridge);
|
||||
|
@ -1853,6 +1853,175 @@ void __init pci_assign_unassigned_resources(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void extend_bridge_window(struct pci_dev *bridge, struct resource *res,
|
||||
struct list_head *add_list, resource_size_t available)
|
||||
{
|
||||
struct pci_dev_resource *dev_res;
|
||||
|
||||
if (res->parent)
|
||||
return;
|
||||
|
||||
if (resource_size(res) >= available)
|
||||
return;
|
||||
|
||||
dev_res = res_to_dev_res(add_list, res);
|
||||
if (!dev_res)
|
||||
return;
|
||||
|
||||
/* Is there room to extend the window? */
|
||||
if (available - resource_size(res) <= dev_res->add_size)
|
||||
return;
|
||||
|
||||
dev_res->add_size = available - resource_size(res);
|
||||
dev_dbg(&bridge->dev, "bridge window %pR extended by %pa\n", res,
|
||||
&dev_res->add_size);
|
||||
}
|
||||
|
||||
static void pci_bus_distribute_available_resources(struct pci_bus *bus,
|
||||
struct list_head *add_list, resource_size_t available_io,
|
||||
resource_size_t available_mmio, resource_size_t available_mmio_pref)
|
||||
{
|
||||
resource_size_t remaining_io, remaining_mmio, remaining_mmio_pref;
|
||||
unsigned int normal_bridges = 0, hotplug_bridges = 0;
|
||||
struct resource *io_res, *mmio_res, *mmio_pref_res;
|
||||
struct pci_dev *dev, *bridge = bus->self;
|
||||
|
||||
io_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0];
|
||||
mmio_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1];
|
||||
mmio_pref_res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2];
|
||||
|
||||
/*
|
||||
* Update additional resource list (add_list) to fill all the
|
||||
* extra resource space available for this port except the space
|
||||
* calculated in __pci_bus_size_bridges() which covers all the
|
||||
* devices currently connected to the port and below.
|
||||
*/
|
||||
extend_bridge_window(bridge, io_res, add_list, available_io);
|
||||
extend_bridge_window(bridge, mmio_res, add_list, available_mmio);
|
||||
extend_bridge_window(bridge, mmio_pref_res, add_list,
|
||||
available_mmio_pref);
|
||||
|
||||
/*
|
||||
* Calculate the total amount of extra resource space we can
|
||||
* pass to bridges below this one. This is basically the
|
||||
* extra space reduced by the minimal required space for the
|
||||
* non-hotplug bridges.
|
||||
*/
|
||||
remaining_io = available_io;
|
||||
remaining_mmio = available_mmio;
|
||||
remaining_mmio_pref = available_mmio_pref;
|
||||
|
||||
/*
|
||||
* Calculate how many hotplug bridges and normal bridges there
|
||||
* are on this bus. We will distribute the additional available
|
||||
* resources between hotplug bridges.
|
||||
*/
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
if (dev->is_hotplug_bridge)
|
||||
hotplug_bridges++;
|
||||
else
|
||||
normal_bridges++;
|
||||
}
|
||||
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
const struct resource *res;
|
||||
|
||||
if (dev->is_hotplug_bridge)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Reduce the available resource space by what the
|
||||
* bridge and devices below it occupy.
|
||||
*/
|
||||
res = &dev->resource[PCI_BRIDGE_RESOURCES + 0];
|
||||
if (!res->parent && available_io > resource_size(res))
|
||||
remaining_io -= resource_size(res);
|
||||
|
||||
res = &dev->resource[PCI_BRIDGE_RESOURCES + 1];
|
||||
if (!res->parent && available_mmio > resource_size(res))
|
||||
remaining_mmio -= resource_size(res);
|
||||
|
||||
res = &dev->resource[PCI_BRIDGE_RESOURCES + 2];
|
||||
if (!res->parent && available_mmio_pref > resource_size(res))
|
||||
remaining_mmio_pref -= resource_size(res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Go over devices on this bus and distribute the remaining
|
||||
* resource space between hotplug bridges.
|
||||
*/
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
struct pci_bus *b;
|
||||
|
||||
b = dev->subordinate;
|
||||
if (!b)
|
||||
continue;
|
||||
|
||||
if (!hotplug_bridges && normal_bridges == 1) {
|
||||
/*
|
||||
* There is only one bridge on the bus (upstream
|
||||
* port) so it gets all available resources
|
||||
* which it can then distribute to the possible
|
||||
* hotplug bridges below.
|
||||
*/
|
||||
pci_bus_distribute_available_resources(b, add_list,
|
||||
available_io, available_mmio,
|
||||
available_mmio_pref);
|
||||
} else if (dev->is_hotplug_bridge) {
|
||||
resource_size_t align, io, mmio, mmio_pref;
|
||||
|
||||
/*
|
||||
* Distribute available extra resources equally
|
||||
* between hotplug-capable downstream ports
|
||||
* taking alignment into account.
|
||||
*
|
||||
* Here hotplug_bridges is always != 0.
|
||||
*/
|
||||
align = pci_resource_alignment(bridge, io_res);
|
||||
io = div64_ul(available_io, hotplug_bridges);
|
||||
io = min(ALIGN(io, align), remaining_io);
|
||||
remaining_io -= io;
|
||||
|
||||
align = pci_resource_alignment(bridge, mmio_res);
|
||||
mmio = div64_ul(available_mmio, hotplug_bridges);
|
||||
mmio = min(ALIGN(mmio, align), remaining_mmio);
|
||||
remaining_mmio -= mmio;
|
||||
|
||||
align = pci_resource_alignment(bridge, mmio_pref_res);
|
||||
mmio_pref = div64_ul(available_mmio_pref,
|
||||
hotplug_bridges);
|
||||
mmio_pref = min(ALIGN(mmio_pref, align),
|
||||
remaining_mmio_pref);
|
||||
remaining_mmio_pref -= mmio_pref;
|
||||
|
||||
pci_bus_distribute_available_resources(b, add_list, io,
|
||||
mmio, mmio_pref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pci_bridge_distribute_available_resources(struct pci_dev *bridge,
|
||||
struct list_head *add_list)
|
||||
{
|
||||
resource_size_t available_io, available_mmio, available_mmio_pref;
|
||||
const struct resource *res;
|
||||
|
||||
if (!bridge->is_hotplug_bridge)
|
||||
return;
|
||||
|
||||
/* Take the initial extra resources from the hotplug port */
|
||||
res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0];
|
||||
available_io = resource_size(res);
|
||||
res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1];
|
||||
available_mmio = resource_size(res);
|
||||
res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2];
|
||||
available_mmio_pref = resource_size(res);
|
||||
|
||||
pci_bus_distribute_available_resources(bridge->subordinate,
|
||||
add_list, available_io, available_mmio, available_mmio_pref);
|
||||
}
|
||||
|
||||
void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
|
||||
{
|
||||
struct pci_bus *parent = bridge->subordinate;
|
||||
@ -1867,6 +2036,14 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
|
||||
|
||||
again:
|
||||
__pci_bus_size_bridges(parent, &add_list);
|
||||
|
||||
/*
|
||||
* Distribute remaining resources (if any) equally between
|
||||
* hotplug bridges below. This makes it possible to extend the
|
||||
* hierarchy later without running out of resources.
|
||||
*/
|
||||
pci_bridge_distribute_available_resources(bridge, &add_list);
|
||||
|
||||
__pci_bridge_assign_resources(bridge, &add_list, &fail_head);
|
||||
BUG_ON(!list_empty(&add_list));
|
||||
tried_times++;
|
||||
@ -1921,10 +2098,9 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
|
||||
want additional resources */
|
||||
|
||||
down_read(&pci_bus_sem);
|
||||
list_for_each_entry(dev, &bus->devices, bus_list)
|
||||
if (pci_is_bridge(dev) && pci_has_subordinate(dev))
|
||||
__pci_bus_size_bridges(dev->subordinate,
|
||||
&add_list);
|
||||
for_each_pci_bridge(dev, bus)
|
||||
if (pci_has_subordinate(dev))
|
||||
__pci_bus_size_bridges(dev->subordinate, &add_list);
|
||||
up_read(&pci_bus_sem);
|
||||
__pci_bus_assign_resources(bus, &add_list, NULL);
|
||||
BUG_ON(!list_empty(&add_list));
|
||||
|
@ -77,9 +77,8 @@ int __ref cb_alloc(struct pcmcia_socket *s)
|
||||
|
||||
max = bus->busn_res.start;
|
||||
for (pass = 0; pass < 2; pass++)
|
||||
list_for_each_entry(dev, &bus->devices, bus_list)
|
||||
if (pci_is_bridge(dev))
|
||||
max = pci_scan_bridge(bus, dev, max, pass);
|
||||
for_each_pci_bridge(dev, bus)
|
||||
max = pci_scan_bridge(bus, dev, max, pass);
|
||||
|
||||
/*
|
||||
* Size all resources below the CardBus controller.
|
||||
|
@ -596,6 +596,10 @@ static inline bool pci_is_bridge(struct pci_dev *dev)
|
||||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS;
|
||||
}
|
||||
|
||||
#define for_each_pci_bridge(dev, bus) \
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) \
|
||||
if (!pci_is_bridge(dev)) {} else
|
||||
|
||||
static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev)
|
||||
{
|
||||
dev = pci_physfn(dev);
|
||||
|
Loading…
Reference in New Issue
Block a user