forked from Minki/linux
Merge branch 'pci/hotplug'
- Avoid returning prematurely from sysfs requests to enable or disable a PCIe hotplug slot (Lukas Wunner) - Don't disable interrupts twice when suspending hotplug ports (Mika Westerberg) - Fix deadlocks when PCIe ports are hot-removed while suspended (Mika Westerberg) - Fix boot-time Embedded Controller GPE storm caused by incorrect resource assignment after ACPI Bus Check Notification (Mika Westerberg) * pci/hotplug: ACPI / hotplug / PCI: Allocate resources directly under the non-hotplug bridge PCI: pciehp: Prevent deadlock on disconnect PCI: pciehp: Do not disable interrupt twice on suspend PCI: pciehp: Refactor infinite loop in pcie_poll_cmd() PCI: pciehp: Avoid returning prematurely from sysfs requests
This commit is contained in:
commit
2df08822a6
@ -449,8 +449,15 @@ static void acpiphp_native_scan_bridge(struct pci_dev *bridge)
|
||||
|
||||
/* Scan non-hotplug bridges that need to be reconfigured */
|
||||
for_each_pci_bridge(dev, bus) {
|
||||
if (!hotplug_is_native(dev))
|
||||
max = pci_scan_bridge(bus, dev, max, 1);
|
||||
if (hotplug_is_native(dev))
|
||||
continue;
|
||||
|
||||
max = pci_scan_bridge(bus, dev, max, 1);
|
||||
if (dev->subordinate) {
|
||||
pcibios_resource_survey_bus(dev->subordinate);
|
||||
pci_bus_size_bridges(dev->subordinate);
|
||||
pci_bus_assign_resources(dev->subordinate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,7 +487,6 @@ static void enable_slot(struct acpiphp_slot *slot, bool bridge)
|
||||
if (PCI_SLOT(dev->devfn) == slot->device)
|
||||
acpiphp_native_scan_bridge(dev);
|
||||
}
|
||||
pci_assign_unassigned_bridge_resources(bus->self);
|
||||
} else {
|
||||
LIST_HEAD(add_list);
|
||||
int max, pass;
|
||||
|
@ -72,6 +72,7 @@ extern int pciehp_poll_time;
|
||||
* @reset_lock: prevents access to the Data Link Layer Link Active bit in the
|
||||
* Link Status register and to the Presence Detect State bit in the Slot
|
||||
* Status register during a slot reset which may cause them to flap
|
||||
* @ist_running: flag to keep user request waiting while IRQ thread is running
|
||||
* @request_result: result of last user request submitted to the IRQ thread
|
||||
* @requester: wait queue to wake up on completion of user request,
|
||||
* used for synchronous slot enable/disable request via sysfs
|
||||
@ -101,6 +102,7 @@ struct controller {
|
||||
|
||||
struct hotplug_slot hotplug_slot; /* hotplug core interface */
|
||||
struct rw_semaphore reset_lock;
|
||||
unsigned int ist_running;
|
||||
int request_result;
|
||||
wait_queue_head_t requester;
|
||||
};
|
||||
@ -172,10 +174,10 @@ void pciehp_set_indicators(struct controller *ctrl, int pwr, int attn);
|
||||
|
||||
void pciehp_get_latch_status(struct controller *ctrl, u8 *status);
|
||||
int pciehp_query_power_fault(struct controller *ctrl);
|
||||
bool pciehp_card_present(struct controller *ctrl);
|
||||
bool pciehp_card_present_or_link_active(struct controller *ctrl);
|
||||
int pciehp_card_present(struct controller *ctrl);
|
||||
int pciehp_card_present_or_link_active(struct controller *ctrl);
|
||||
int pciehp_check_link_status(struct controller *ctrl);
|
||||
bool pciehp_check_link_active(struct controller *ctrl);
|
||||
int pciehp_check_link_active(struct controller *ctrl);
|
||||
void pciehp_release_ctrl(struct controller *ctrl);
|
||||
|
||||
int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot);
|
||||
|
@ -139,10 +139,15 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
{
|
||||
struct controller *ctrl = to_ctrl(hotplug_slot);
|
||||
struct pci_dev *pdev = ctrl->pcie->port;
|
||||
int ret;
|
||||
|
||||
pci_config_pm_runtime_get(pdev);
|
||||
*value = pciehp_card_present_or_link_active(ctrl);
|
||||
ret = pciehp_card_present_or_link_active(ctrl);
|
||||
pci_config_pm_runtime_put(pdev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*value = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -158,13 +163,13 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
|
||||
*/
|
||||
static void pciehp_check_presence(struct controller *ctrl)
|
||||
{
|
||||
bool occupied;
|
||||
int occupied;
|
||||
|
||||
down_read(&ctrl->reset_lock);
|
||||
mutex_lock(&ctrl->state_lock);
|
||||
|
||||
occupied = pciehp_card_present_or_link_active(ctrl);
|
||||
if ((occupied && (ctrl->state == OFF_STATE ||
|
||||
if ((occupied > 0 && (ctrl->state == OFF_STATE ||
|
||||
ctrl->state == BLINKINGON_STATE)) ||
|
||||
(!occupied && (ctrl->state == ON_STATE ||
|
||||
ctrl->state == BLINKINGOFF_STATE)))
|
||||
@ -253,7 +258,7 @@ static bool pme_is_native(struct pcie_device *dev)
|
||||
return pcie_ports_native || host->native_pme;
|
||||
}
|
||||
|
||||
static int pciehp_suspend(struct pcie_device *dev)
|
||||
static void pciehp_disable_interrupt(struct pcie_device *dev)
|
||||
{
|
||||
/*
|
||||
* Disable hotplug interrupt so that it does not trigger
|
||||
@ -261,7 +266,19 @@ static int pciehp_suspend(struct pcie_device *dev)
|
||||
*/
|
||||
if (pme_is_native(dev))
|
||||
pcie_disable_interrupt(get_service_data(dev));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pciehp_suspend(struct pcie_device *dev)
|
||||
{
|
||||
/*
|
||||
* If the port is already runtime suspended we can keep it that
|
||||
* way.
|
||||
*/
|
||||
if (dev_pm_smart_suspend_and_suspended(&dev->port->dev))
|
||||
return 0;
|
||||
|
||||
pciehp_disable_interrupt(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -279,6 +296,7 @@ static int pciehp_resume_noirq(struct pcie_device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pciehp_resume(struct pcie_device *dev)
|
||||
{
|
||||
@ -292,6 +310,12 @@ static int pciehp_resume(struct pcie_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pciehp_runtime_suspend(struct pcie_device *dev)
|
||||
{
|
||||
pciehp_disable_interrupt(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pciehp_runtime_resume(struct pcie_device *dev)
|
||||
{
|
||||
struct controller *ctrl = get_service_data(dev);
|
||||
@ -318,10 +342,12 @@ static struct pcie_port_service_driver hpdriver_portdrv = {
|
||||
.remove = pciehp_remove,
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.suspend = pciehp_suspend,
|
||||
.resume_noirq = pciehp_resume_noirq,
|
||||
.resume = pciehp_resume,
|
||||
.runtime_suspend = pciehp_suspend,
|
||||
#endif
|
||||
.runtime_suspend = pciehp_runtime_suspend,
|
||||
.runtime_resume = pciehp_runtime_resume,
|
||||
#endif /* PM */
|
||||
};
|
||||
|
@ -226,7 +226,7 @@ void pciehp_handle_disable_request(struct controller *ctrl)
|
||||
|
||||
void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
|
||||
{
|
||||
bool present, link_active;
|
||||
int present, link_active;
|
||||
|
||||
/*
|
||||
* If the slot is on and presence or link has changed, turn it off.
|
||||
@ -257,7 +257,7 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
|
||||
mutex_lock(&ctrl->state_lock);
|
||||
present = pciehp_card_present(ctrl);
|
||||
link_active = pciehp_check_link_active(ctrl);
|
||||
if (!present && !link_active) {
|
||||
if (present <= 0 && link_active <= 0) {
|
||||
mutex_unlock(&ctrl->state_lock);
|
||||
return;
|
||||
}
|
||||
@ -375,7 +375,8 @@ int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot)
|
||||
ctrl->request_result = -ENODEV;
|
||||
pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
|
||||
wait_event(ctrl->requester,
|
||||
!atomic_read(&ctrl->pending_events));
|
||||
!atomic_read(&ctrl->pending_events) &&
|
||||
!ctrl->ist_running);
|
||||
return ctrl->request_result;
|
||||
case POWERON_STATE:
|
||||
ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
|
||||
@ -408,7 +409,8 @@ int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot)
|
||||
mutex_unlock(&ctrl->state_lock);
|
||||
pciehp_request(ctrl, DISABLE_SLOT);
|
||||
wait_event(ctrl->requester,
|
||||
!atomic_read(&ctrl->pending_events));
|
||||
!atomic_read(&ctrl->pending_events) &&
|
||||
!ctrl->ist_running);
|
||||
return ctrl->request_result;
|
||||
case POWEROFF_STATE:
|
||||
ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
|
||||
|
@ -68,7 +68,7 @@ static int pcie_poll_cmd(struct controller *ctrl, int timeout)
|
||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||
u16 slot_status;
|
||||
|
||||
while (true) {
|
||||
do {
|
||||
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
|
||||
if (slot_status == (u16) ~0) {
|
||||
ctrl_info(ctrl, "%s: no response from device\n",
|
||||
@ -81,11 +81,9 @@ static int pcie_poll_cmd(struct controller *ctrl, int timeout)
|
||||
PCI_EXP_SLTSTA_CC);
|
||||
return 1;
|
||||
}
|
||||
if (timeout < 0)
|
||||
break;
|
||||
msleep(10);
|
||||
timeout -= 10;
|
||||
}
|
||||
} while (timeout >= 0);
|
||||
return 0; /* timeout */
|
||||
}
|
||||
|
||||
@ -201,17 +199,29 @@ static void pcie_write_cmd_nowait(struct controller *ctrl, u16 cmd, u16 mask)
|
||||
pcie_do_write_cmd(ctrl, cmd, mask, false);
|
||||
}
|
||||
|
||||
bool pciehp_check_link_active(struct controller *ctrl)
|
||||
/**
|
||||
* pciehp_check_link_active() - Is the link active
|
||||
* @ctrl: PCIe hotplug controller
|
||||
*
|
||||
* Check whether the downstream link is currently active. Note it is
|
||||
* possible that the card is removed immediately after this so the
|
||||
* caller may need to take it into account.
|
||||
*
|
||||
* If the hotplug controller itself is not available anymore returns
|
||||
* %-ENODEV.
|
||||
*/
|
||||
int pciehp_check_link_active(struct controller *ctrl)
|
||||
{
|
||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||
u16 lnk_status;
|
||||
bool ret;
|
||||
int ret;
|
||||
|
||||
ret = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
||||
if (ret == PCIBIOS_DEVICE_NOT_FOUND || lnk_status == (u16)~0)
|
||||
return -ENODEV;
|
||||
|
||||
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
||||
ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
|
||||
|
||||
if (ret)
|
||||
ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
|
||||
ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -373,13 +383,29 @@ void pciehp_get_latch_status(struct controller *ctrl, u8 *status)
|
||||
*status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS);
|
||||
}
|
||||
|
||||
bool pciehp_card_present(struct controller *ctrl)
|
||||
/**
|
||||
* pciehp_card_present() - Is the card present
|
||||
* @ctrl: PCIe hotplug controller
|
||||
*
|
||||
* Function checks whether the card is currently present in the slot and
|
||||
* in that case returns true. Note it is possible that the card is
|
||||
* removed immediately after the check so the caller may need to take
|
||||
* this into account.
|
||||
*
|
||||
* It the hotplug controller itself is not available anymore returns
|
||||
* %-ENODEV.
|
||||
*/
|
||||
int pciehp_card_present(struct controller *ctrl)
|
||||
{
|
||||
struct pci_dev *pdev = ctrl_dev(ctrl);
|
||||
u16 slot_status;
|
||||
int ret;
|
||||
|
||||
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
|
||||
return slot_status & PCI_EXP_SLTSTA_PDS;
|
||||
ret = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
|
||||
if (ret == PCIBIOS_DEVICE_NOT_FOUND || slot_status == (u16)~0)
|
||||
return -ENODEV;
|
||||
|
||||
return !!(slot_status & PCI_EXP_SLTSTA_PDS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -390,10 +416,19 @@ bool pciehp_card_present(struct controller *ctrl)
|
||||
* Presence Detect State bit, this helper also returns true if the Link Active
|
||||
* bit is set. This is a concession to broken hotplug ports which hardwire
|
||||
* Presence Detect State to zero, such as Wilocity's [1ae9:0200].
|
||||
*
|
||||
* Returns: %1 if the slot is occupied and %0 if it is not. If the hotplug
|
||||
* port is not present anymore returns %-ENODEV.
|
||||
*/
|
||||
bool pciehp_card_present_or_link_active(struct controller *ctrl)
|
||||
int pciehp_card_present_or_link_active(struct controller *ctrl)
|
||||
{
|
||||
return pciehp_card_present(ctrl) || pciehp_check_link_active(ctrl);
|
||||
int ret;
|
||||
|
||||
ret = pciehp_card_present(ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return pciehp_check_link_active(ctrl);
|
||||
}
|
||||
|
||||
int pciehp_query_power_fault(struct controller *ctrl)
|
||||
@ -583,6 +618,7 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
|
||||
irqreturn_t ret;
|
||||
u32 events;
|
||||
|
||||
ctrl->ist_running = true;
|
||||
pci_config_pm_runtime_get(pdev);
|
||||
|
||||
/* rerun pciehp_isr() if the port was inaccessible on interrupt */
|
||||
@ -629,6 +665,7 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
|
||||
up_read(&ctrl->reset_lock);
|
||||
|
||||
pci_config_pm_runtime_put(pdev);
|
||||
ctrl->ist_running = false;
|
||||
wake_up(&ctrl->requester);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user