|
@@ -17,8 +17,9 @@
|
|
|
#include <linux/types.h>
|
|
|
#include <linux/signal.h>
|
|
|
#include <linux/jiffies.h>
|
|
|
-#include <linux/timer.h>
|
|
|
+#include <linux/kthread.h>
|
|
|
#include <linux/pci.h>
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
#include <linux/interrupt.h>
|
|
|
#include <linux/time.h>
|
|
|
#include <linux/slab.h>
|
|
@@ -31,47 +32,24 @@ static inline struct pci_dev *ctrl_dev(struct controller *ctrl)
|
|
|
return ctrl->pcie->port;
|
|
|
}
|
|
|
|
|
|
-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(struct timer_list *t)
|
|
|
-{
|
|
|
- struct controller *ctrl = from_timer(ctrl, t, poll_timer);
|
|
|
-
|
|
|
- /* Poll for interrupt events. regs == NULL => polling */
|
|
|
- pcie_isr(0, ctrl);
|
|
|
-
|
|
|
- if (!pciehp_poll_time)
|
|
|
- pciehp_poll_time = 2; /* default polling interval is 2 sec */
|
|
|
-
|
|
|
- start_int_poll_timer(ctrl, pciehp_poll_time);
|
|
|
-}
|
|
|
-
|
|
|
-/* This function starts the interrupt polling timer. */
|
|
|
-static void start_int_poll_timer(struct controller *ctrl, int sec)
|
|
|
-{
|
|
|
- /* Clamp to sane value */
|
|
|
- if ((sec <= 0) || (sec > 60))
|
|
|
- sec = 2;
|
|
|
-
|
|
|
- ctrl->poll_timer.expires = jiffies + sec * HZ;
|
|
|
- add_timer(&ctrl->poll_timer);
|
|
|
-}
|
|
|
+static irqreturn_t pciehp_isr(int irq, void *dev_id);
|
|
|
+static irqreturn_t pciehp_ist(int irq, void *dev_id);
|
|
|
+static int pciehp_poll(void *data);
|
|
|
|
|
|
static inline int pciehp_request_irq(struct controller *ctrl)
|
|
|
{
|
|
|
int retval, irq = ctrl->pcie->irq;
|
|
|
|
|
|
- /* Install interrupt polling timer. Start with 10 sec delay */
|
|
|
if (pciehp_poll_mode) {
|
|
|
- timer_setup(&ctrl->poll_timer, int_poll_timeout, 0);
|
|
|
- start_int_poll_timer(ctrl, 10);
|
|
|
- return 0;
|
|
|
+ ctrl->poll_thread = kthread_run(&pciehp_poll, ctrl,
|
|
|
+ "pciehp_poll-%s",
|
|
|
+ slot_name(ctrl->slot));
|
|
|
+ return PTR_ERR_OR_ZERO(ctrl->poll_thread);
|
|
|
}
|
|
|
|
|
|
/* Installs the interrupt handler */
|
|
|
- retval = request_irq(irq, pcie_isr, IRQF_SHARED, MY_NAME, ctrl);
|
|
|
+ retval = request_threaded_irq(irq, pciehp_isr, pciehp_ist,
|
|
|
+ IRQF_SHARED, MY_NAME, ctrl);
|
|
|
if (retval)
|
|
|
ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n",
|
|
|
irq);
|
|
@@ -81,7 +59,7 @@ static inline int pciehp_request_irq(struct controller *ctrl)
|
|
|
static inline void pciehp_free_irq(struct controller *ctrl)
|
|
|
{
|
|
|
if (pciehp_poll_mode)
|
|
|
- del_timer_sync(&ctrl->poll_timer);
|
|
|
+ kthread_stop(ctrl->poll_thread);
|
|
|
else
|
|
|
free_irq(ctrl->pcie->irq, ctrl);
|
|
|
}
|
|
@@ -293,6 +271,11 @@ int pciehp_check_link_status(struct controller *ctrl)
|
|
|
found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
|
|
|
PCI_DEVFN(0, 0));
|
|
|
|
|
|
+ /* ignore link or presence changes up to this point */
|
|
|
+ if (found)
|
|
|
+ atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC),
|
|
|
+ &ctrl->pending_events);
|
|
|
+
|
|
|
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status);
|
|
|
ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
|
|
|
if ((lnk_status & PCI_EXP_LNKSTA_LT) ||
|
|
@@ -339,7 +322,9 @@ int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
|
|
struct pci_dev *pdev = ctrl_dev(slot->ctrl);
|
|
|
u16 slot_ctrl;
|
|
|
|
|
|
+ pci_config_pm_runtime_get(pdev);
|
|
|
pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
|
|
|
+ pci_config_pm_runtime_put(pdev);
|
|
|
*status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6;
|
|
|
return 0;
|
|
|
}
|
|
@@ -350,7 +335,9 @@ void pciehp_get_attention_status(struct slot *slot, u8 *status)
|
|
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
|
|
u16 slot_ctrl;
|
|
|
|
|
|
+ pci_config_pm_runtime_get(pdev);
|
|
|
pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl);
|
|
|
+ pci_config_pm_runtime_put(pdev);
|
|
|
ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__,
|
|
|
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl);
|
|
|
|
|
@@ -425,9 +412,12 @@ int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot,
|
|
|
{
|
|
|
struct slot *slot = hotplug_slot->private;
|
|
|
struct controller *ctrl = slot->ctrl;
|
|
|
+ struct pci_dev *pdev = ctrl_dev(ctrl);
|
|
|
|
|
|
+ pci_config_pm_runtime_get(pdev);
|
|
|
pcie_write_cmd_nowait(ctrl, status << 6,
|
|
|
PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC);
|
|
|
+ pci_config_pm_runtime_put(pdev);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -539,20 +529,35 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
|
|
|
{
|
|
|
struct controller *ctrl = (struct controller *)dev_id;
|
|
|
struct pci_dev *pdev = ctrl_dev(ctrl);
|
|
|
- struct pci_bus *subordinate = pdev->subordinate;
|
|
|
- struct pci_dev *dev;
|
|
|
- struct slot *slot = ctrl->slot;
|
|
|
+ struct device *parent = pdev->dev.parent;
|
|
|
u16 status, events;
|
|
|
- u8 present;
|
|
|
- bool link;
|
|
|
|
|
|
- /* Interrupts cannot originate from a controller that's asleep */
|
|
|
+ /*
|
|
|
+ * Interrupts only occur in D3hot or shallower (PCIe r4.0, sec 6.7.3.4).
|
|
|
+ */
|
|
|
if (pdev->current_state == PCI_D3cold)
|
|
|
return IRQ_NONE;
|
|
|
|
|
|
+ /*
|
|
|
+ * Keep the port accessible by holding a runtime PM ref on its parent.
|
|
|
+ * Defer resume of the parent to the IRQ thread if it's suspended.
|
|
|
+ * Mask the interrupt until then.
|
|
|
+ */
|
|
|
+ if (parent) {
|
|
|
+ pm_runtime_get_noresume(parent);
|
|
|
+ if (!pm_runtime_active(parent)) {
|
|
|
+ pm_runtime_put(parent);
|
|
|
+ disable_irq_nosync(irq);
|
|
|
+ atomic_or(RERUN_ISR, &ctrl->pending_events);
|
|
|
+ return IRQ_WAKE_THREAD;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status);
|
|
|
if (status == (u16) ~0) {
|
|
|
ctrl_info(ctrl, "%s: no response from device\n", __func__);
|
|
|
+ if (parent)
|
|
|
+ pm_runtime_put(parent);
|
|
|
return IRQ_NONE;
|
|
|
}
|
|
|
|
|
@@ -571,86 +576,119 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
|
|
|
if (ctrl->power_fault_detected)
|
|
|
events &= ~PCI_EXP_SLTSTA_PFD;
|
|
|
|
|
|
- if (!events)
|
|
|
+ if (!events) {
|
|
|
+ if (parent)
|
|
|
+ pm_runtime_put(parent);
|
|
|
return IRQ_NONE;
|
|
|
-
|
|
|
- /* Capture link status before clearing interrupts */
|
|
|
- if (events & PCI_EXP_SLTSTA_DLLSC)
|
|
|
- link = pciehp_check_link_active(ctrl);
|
|
|
+ }
|
|
|
|
|
|
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events);
|
|
|
ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events);
|
|
|
+ if (parent)
|
|
|
+ pm_runtime_put(parent);
|
|
|
|
|
|
- /* Check Command Complete Interrupt Pending */
|
|
|
+ /*
|
|
|
+ * Command Completed notifications are not deferred to the
|
|
|
+ * IRQ thread because it may be waiting for their arrival.
|
|
|
+ */
|
|
|
if (events & PCI_EXP_SLTSTA_CC) {
|
|
|
ctrl->cmd_busy = 0;
|
|
|
smp_mb();
|
|
|
wake_up(&ctrl->queue);
|
|
|
+
|
|
|
+ if (events == PCI_EXP_SLTSTA_CC)
|
|
|
+ return IRQ_HANDLED;
|
|
|
+
|
|
|
+ events &= ~PCI_EXP_SLTSTA_CC;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pdev->ignore_hotplug) {
|
|
|
+ ctrl_dbg(ctrl, "ignoring hotplug event %#06x\n", events);
|
|
|
+ return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
- if (subordinate) {
|
|
|
- list_for_each_entry(dev, &subordinate->devices, bus_list) {
|
|
|
- if (dev->ignore_hotplug) {
|
|
|
- ctrl_dbg(ctrl, "ignoring hotplug event %#06x (%s requested no hotplug)\n",
|
|
|
- events, pci_name(dev));
|
|
|
- return IRQ_HANDLED;
|
|
|
- }
|
|
|
+ /* Save pending events for consumption by IRQ thread. */
|
|
|
+ atomic_or(events, &ctrl->pending_events);
|
|
|
+ return IRQ_WAKE_THREAD;
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t pciehp_ist(int irq, void *dev_id)
|
|
|
+{
|
|
|
+ struct controller *ctrl = (struct controller *)dev_id;
|
|
|
+ struct pci_dev *pdev = ctrl_dev(ctrl);
|
|
|
+ struct slot *slot = ctrl->slot;
|
|
|
+ irqreturn_t ret;
|
|
|
+ u32 events;
|
|
|
+
|
|
|
+ pci_config_pm_runtime_get(pdev);
|
|
|
+
|
|
|
+ /* rerun pciehp_isr() if the port was inaccessible on interrupt */
|
|
|
+ if (atomic_fetch_and(~RERUN_ISR, &ctrl->pending_events) & RERUN_ISR) {
|
|
|
+ ret = pciehp_isr(irq, dev_id);
|
|
|
+ enable_irq(irq);
|
|
|
+ if (ret != IRQ_WAKE_THREAD) {
|
|
|
+ pci_config_pm_runtime_put(pdev);
|
|
|
+ return ret;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ synchronize_hardirq(irq);
|
|
|
+ events = atomic_xchg(&ctrl->pending_events, 0);
|
|
|
+ if (!events) {
|
|
|
+ pci_config_pm_runtime_put(pdev);
|
|
|
+ return IRQ_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
/* Check Attention Button Pressed */
|
|
|
if (events & PCI_EXP_SLTSTA_ABP) {
|
|
|
ctrl_info(ctrl, "Slot(%s): Attention button pressed\n",
|
|
|
slot_name(slot));
|
|
|
- pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS);
|
|
|
+ pciehp_handle_button_press(slot);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * Check Link Status Changed at higher precedence than Presence
|
|
|
- * Detect Changed. The PDS value may be set to "card present" from
|
|
|
- * out-of-band detection, which may be in conflict with a Link Down
|
|
|
- * and cause the wrong event to queue.
|
|
|
+ * Disable requests have higher priority than Presence Detect Changed
|
|
|
+ * or Data Link Layer State Changed events.
|
|
|
*/
|
|
|
- if (events & PCI_EXP_SLTSTA_DLLSC) {
|
|
|
- ctrl_info(ctrl, "Slot(%s): Link %s\n", slot_name(slot),
|
|
|
- link ? "Up" : "Down");
|
|
|
- pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP :
|
|
|
- INT_LINK_DOWN);
|
|
|
- } else if (events & PCI_EXP_SLTSTA_PDC) {
|
|
|
- present = !!(status & PCI_EXP_SLTSTA_PDS);
|
|
|
- ctrl_info(ctrl, "Slot(%s): Card %spresent\n", slot_name(slot),
|
|
|
- present ? "" : "not ");
|
|
|
- pciehp_queue_interrupt_event(slot, present ? INT_PRESENCE_ON :
|
|
|
- INT_PRESENCE_OFF);
|
|
|
- }
|
|
|
+ down_read(&ctrl->reset_lock);
|
|
|
+ if (events & DISABLE_SLOT)
|
|
|
+ pciehp_handle_disable_request(slot);
|
|
|
+ else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC))
|
|
|
+ pciehp_handle_presence_or_link_change(slot, events);
|
|
|
+ up_read(&ctrl->reset_lock);
|
|
|
|
|
|
/* Check Power Fault Detected */
|
|
|
if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
|
|
|
ctrl->power_fault_detected = 1;
|
|
|
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot));
|
|
|
- pciehp_queue_interrupt_event(slot, INT_POWER_FAULT);
|
|
|
+ pciehp_set_attention_status(slot, 1);
|
|
|
+ pciehp_green_led_off(slot);
|
|
|
}
|
|
|
|
|
|
+ pci_config_pm_runtime_put(pdev);
|
|
|
+ wake_up(&ctrl->requester);
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
-static irqreturn_t pcie_isr(int irq, void *dev_id)
|
|
|
+static int pciehp_poll(void *data)
|
|
|
{
|
|
|
- irqreturn_t rc, handled = IRQ_NONE;
|
|
|
+ struct controller *ctrl = data;
|
|
|
|
|
|
- /*
|
|
|
- * To guarantee that all interrupt events are serviced, we need to
|
|
|
- * re-inspect Slot Status register after clearing what is presumed
|
|
|
- * to be the last pending interrupt.
|
|
|
- */
|
|
|
- do {
|
|
|
- rc = pciehp_isr(irq, dev_id);
|
|
|
- if (rc == IRQ_HANDLED)
|
|
|
- handled = IRQ_HANDLED;
|
|
|
- } while (rc == IRQ_HANDLED);
|
|
|
+ schedule_timeout_idle(10 * HZ); /* start with 10 sec delay */
|
|
|
+
|
|
|
+ while (!kthread_should_stop()) {
|
|
|
+ /* poll for interrupt events or user requests */
|
|
|
+ while (pciehp_isr(IRQ_NOTCONNECTED, ctrl) == IRQ_WAKE_THREAD ||
|
|
|
+ atomic_read(&ctrl->pending_events))
|
|
|
+ pciehp_ist(IRQ_NOTCONNECTED, ctrl);
|
|
|
+
|
|
|
+ if (pciehp_poll_time <= 0 || pciehp_poll_time > 60)
|
|
|
+ pciehp_poll_time = 2; /* clamp to sane value */
|
|
|
+
|
|
|
+ schedule_timeout_idle(pciehp_poll_time * HZ);
|
|
|
+ }
|
|
|
|
|
|
- /* Return IRQ_HANDLED if we handled one or more events */
|
|
|
- return handled;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static void pcie_enable_notification(struct controller *ctrl)
|
|
@@ -691,17 +729,6 @@ static void pcie_enable_notification(struct controller *ctrl)
|
|
|
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, cmd);
|
|
|
}
|
|
|
|
|
|
-void pcie_reenable_notification(struct controller *ctrl)
|
|
|
-{
|
|
|
- /*
|
|
|
- * Clear both Presence and Data Link Layer Changed to make sure
|
|
|
- * those events still fire after we have re-enabled them.
|
|
|
- */
|
|
|
- pcie_capability_write_word(ctrl->pcie->port, PCI_EXP_SLTSTA,
|
|
|
- PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
|
|
|
- pcie_enable_notification(ctrl);
|
|
|
-}
|
|
|
-
|
|
|
static void pcie_disable_notification(struct controller *ctrl)
|
|
|
{
|
|
|
u16 mask;
|
|
@@ -715,6 +742,12 @@ static void pcie_disable_notification(struct controller *ctrl)
|
|
|
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
|
|
|
}
|
|
|
|
|
|
+void pcie_clear_hotplug_events(struct controller *ctrl)
|
|
|
+{
|
|
|
+ pcie_capability_write_word(ctrl_dev(ctrl), PCI_EXP_SLTSTA,
|
|
|
+ PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
|
|
|
* bus reset of the bridge, but at the same time we want to ensure that it is
|
|
@@ -732,6 +765,8 @@ int pciehp_reset_slot(struct slot *slot, int probe)
|
|
|
if (probe)
|
|
|
return 0;
|
|
|
|
|
|
+ down_write(&ctrl->reset_lock);
|
|
|
+
|
|
|
if (!ATTN_BUTTN(ctrl)) {
|
|
|
ctrl_mask |= PCI_EXP_SLTCTL_PDCE;
|
|
|
stat_mask |= PCI_EXP_SLTSTA_PDC;
|
|
@@ -742,8 +777,6 @@ int pciehp_reset_slot(struct slot *slot, int probe)
|
|
|
pcie_write_cmd(ctrl, 0, ctrl_mask);
|
|
|
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
|
|
|
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, 0);
|
|
|
- if (pciehp_poll_mode)
|
|
|
- del_timer_sync(&ctrl->poll_timer);
|
|
|
|
|
|
pci_reset_bridge_secondary_bus(ctrl->pcie->port);
|
|
|
|
|
@@ -751,8 +784,8 @@ int pciehp_reset_slot(struct slot *slot, int probe)
|
|
|
pcie_write_cmd_nowait(ctrl, ctrl_mask, ctrl_mask);
|
|
|
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);
|
|
|
+
|
|
|
+ up_write(&ctrl->reset_lock);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -765,7 +798,7 @@ int pcie_init_notification(struct controller *ctrl)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void pcie_shutdown_notification(struct controller *ctrl)
|
|
|
+void pcie_shutdown_notification(struct controller *ctrl)
|
|
|
{
|
|
|
if (ctrl->notification_enabled) {
|
|
|
pcie_disable_notification(ctrl);
|
|
@@ -776,32 +809,29 @@ static void pcie_shutdown_notification(struct controller *ctrl)
|
|
|
|
|
|
static int pcie_init_slot(struct controller *ctrl)
|
|
|
{
|
|
|
+ struct pci_bus *subordinate = ctrl_dev(ctrl)->subordinate;
|
|
|
struct slot *slot;
|
|
|
|
|
|
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
|
|
|
if (!slot)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- slot->wq = alloc_ordered_workqueue("pciehp-%u", 0, PSN(ctrl));
|
|
|
- if (!slot->wq)
|
|
|
- goto abort;
|
|
|
+ down_read(&pci_bus_sem);
|
|
|
+ slot->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE;
|
|
|
+ up_read(&pci_bus_sem);
|
|
|
|
|
|
slot->ctrl = ctrl;
|
|
|
mutex_init(&slot->lock);
|
|
|
- mutex_init(&slot->hotplug_lock);
|
|
|
INIT_DELAYED_WORK(&slot->work, pciehp_queue_pushbutton_work);
|
|
|
ctrl->slot = slot;
|
|
|
return 0;
|
|
|
-abort:
|
|
|
- kfree(slot);
|
|
|
- return -ENOMEM;
|
|
|
}
|
|
|
|
|
|
static void pcie_cleanup_slot(struct controller *ctrl)
|
|
|
{
|
|
|
struct slot *slot = ctrl->slot;
|
|
|
- cancel_delayed_work(&slot->work);
|
|
|
- destroy_workqueue(slot->wq);
|
|
|
+
|
|
|
+ cancel_delayed_work_sync(&slot->work);
|
|
|
kfree(slot);
|
|
|
}
|
|
|
|
|
@@ -826,6 +856,7 @@ struct controller *pcie_init(struct pcie_device *dev)
|
|
|
{
|
|
|
struct controller *ctrl;
|
|
|
u32 slot_cap, link_cap;
|
|
|
+ u8 occupied, poweron;
|
|
|
struct pci_dev *pdev = dev->port;
|
|
|
|
|
|
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
|
|
@@ -847,6 +878,8 @@ struct controller *pcie_init(struct pcie_device *dev)
|
|
|
|
|
|
ctrl->slot_cap = slot_cap;
|
|
|
mutex_init(&ctrl->ctrl_lock);
|
|
|
+ init_rwsem(&ctrl->reset_lock);
|
|
|
+ init_waitqueue_head(&ctrl->requester);
|
|
|
init_waitqueue_head(&ctrl->queue);
|
|
|
dbg_ctrl(ctrl);
|
|
|
|
|
@@ -855,16 +888,11 @@ 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 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.
|
|
|
- */
|
|
|
+ /* Clear all remaining event bits in Slot Status register. */
|
|
|
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
|
|
|
PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
|
|
|
PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC |
|
|
|
- PCI_EXP_SLTSTA_DLLSC);
|
|
|
+ PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC);
|
|
|
|
|
|
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%s\n",
|
|
|
(slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
|
|
@@ -883,6 +911,19 @@ struct controller *pcie_init(struct pcie_device *dev)
|
|
|
if (pcie_init_slot(ctrl))
|
|
|
goto abort_ctrl;
|
|
|
|
|
|
+ /*
|
|
|
+ * If empty slot's power status is on, turn power off. The IRQ isn't
|
|
|
+ * requested yet, so avoid triggering a notification with this command.
|
|
|
+ */
|
|
|
+ if (POWER_CTRL(ctrl)) {
|
|
|
+ pciehp_get_adapter_status(ctrl->slot, &occupied);
|
|
|
+ pciehp_get_power_status(ctrl->slot, &poweron);
|
|
|
+ if (!occupied && poweron) {
|
|
|
+ pcie_disable_notification(ctrl);
|
|
|
+ pciehp_power_off_slot(ctrl->slot);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return ctrl;
|
|
|
|
|
|
abort_ctrl:
|
|
@@ -893,7 +934,6 @@ abort:
|
|
|
|
|
|
void pciehp_release_ctrl(struct controller *ctrl)
|
|
|
{
|
|
|
- pcie_shutdown_notification(ctrl);
|
|
|
pcie_cleanup_slot(ctrl);
|
|
|
kfree(ctrl);
|
|
|
}
|