Skip to content

Commit

Permalink
Merge branch 'pci/hotplug' into next
Browse files Browse the repository at this point in the history
* pci/hotplug:
  PCI: pciehp: Remove useless pciehp_get_latch_status() calls
  PCI: pciehp: Clean up dmesg "Slot(%s)" messages
  PCI: pciehp: Remove unnecessary guard
  PCI: pciehp: Don't re-read Slot Status when handling surprise event
  PCI: pciehp: Don't re-read Slot Status when queuing hotplug event
  PCI: pciehp: Process all hotplug events before looking for new ones
  PCI: pciehp: Return IRQ_NONE when we can't read interrupt status
  PCI: pciehp: Rename pcie_isr() locals for clarity
  PCI: pciehp: Clear attention LED on device add
  • Loading branch information
bjorn-helgaas committed Sep 21, 2016
2 parents c1c5cc0 + 29a654e commit 1fa6af2
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 95 deletions.
84 changes: 31 additions & 53 deletions drivers/pci/hotplug/pciehp_ctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static int board_added(struct slot *p_slot)

/* Check for a power fault */
if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot));
retval = -EIO;
goto err_exit;
}
Expand All @@ -120,6 +120,7 @@ static int board_added(struct slot *p_slot)
}

pciehp_green_led_on(p_slot);
pciehp_set_attention_status(p_slot, 0);
return 0;

err_exit:
Expand Down Expand Up @@ -253,11 +254,11 @@ static void handle_button_press_event(struct slot *p_slot)
pciehp_get_power_status(p_slot, &getstatus);
if (getstatus) {
p_slot->state = BLINKINGOFF_STATE;
ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n",
ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
slot_name(p_slot));
} else {
p_slot->state = BLINKINGON_STATE;
ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n",
ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
slot_name(p_slot));
}
/* blink green LED and turn off amber */
Expand All @@ -272,14 +273,14 @@ static void handle_button_press_event(struct slot *p_slot)
* press the attention again before the 5 sec. limit
* expires to cancel hot-add or hot-remove
*/
ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot));
ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot));
cancel_delayed_work(&p_slot->work);
if (p_slot->state == BLINKINGOFF_STATE)
pciehp_green_led_on(p_slot);
else
pciehp_green_led_off(p_slot);
pciehp_set_attention_status(p_slot, 0);
ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n",
ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
slot_name(p_slot));
p_slot->state = STATIC_STATE;
break;
Expand All @@ -290,28 +291,16 @@ static void handle_button_press_event(struct slot *p_slot)
* this means that the previous attention button action
* to hot-add or hot-remove is undergoing
*/
ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
ctrl_info(ctrl, "Slot(%s): Button ignored\n",
slot_name(p_slot));
break;
default:
ctrl_warn(ctrl, "ignoring invalid state %#x\n", p_slot->state);
ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
slot_name(p_slot), p_slot->state);
break;
}
}

/*
* Note: This function must be called with slot->lock held
*/
static void handle_surprise_event(struct slot *p_slot)
{
u8 getstatus;

pciehp_get_adapter_status(p_slot, &getstatus);
if (!getstatus)
pciehp_queue_power_work(p_slot, DISABLE_REQ);
else
pciehp_queue_power_work(p_slot, ENABLE_REQ);
}

/*
* Note: This function must be called with slot->lock held
*/
Expand All @@ -330,31 +319,27 @@ static void handle_link_event(struct slot *p_slot, u32 event)
break;
case POWERON_STATE:
if (event == INT_LINK_UP) {
ctrl_info(ctrl,
"Link Up event ignored on slot(%s): already powering on\n",
ctrl_info(ctrl, "Slot(%s): Link Up event ignored; already powering on\n",
slot_name(p_slot));
} else {
ctrl_info(ctrl,
"Link Down event queued on slot(%s): currently getting powered on\n",
ctrl_info(ctrl, "Slot(%s): Link Down event queued; currently getting powered on\n",
slot_name(p_slot));
pciehp_queue_power_work(p_slot, DISABLE_REQ);
}
break;
case POWEROFF_STATE:
if (event == INT_LINK_UP) {
ctrl_info(ctrl,
"Link Up event queued on slot(%s): currently getting powered off\n",
ctrl_info(ctrl, "Slot(%s): Link Up event queued; currently getting powered off\n",
slot_name(p_slot));
pciehp_queue_power_work(p_slot, ENABLE_REQ);
} else {
ctrl_info(ctrl,
"Link Down event ignored on slot(%s): already powering off\n",
ctrl_info(ctrl, "Slot(%s): Link Down event ignored; already powering off\n",
slot_name(p_slot));
}
break;
default:
ctrl_err(ctrl, "ignoring invalid state %#x on slot(%s)\n",
p_slot->state, slot_name(p_slot));
ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
slot_name(p_slot), p_slot->state);
break;
}
}
Expand All @@ -377,14 +362,14 @@ static void interrupt_event_handler(struct work_struct *work)
pciehp_green_led_off(p_slot);
break;
case INT_PRESENCE_ON:
handle_surprise_event(p_slot);
pciehp_queue_power_work(p_slot, ENABLE_REQ);
break;
case INT_PRESENCE_OFF:
/*
* Regardless of surprise capability, we need to
* definitely remove a card that has been pulled out!
*/
handle_surprise_event(p_slot);
pciehp_queue_power_work(p_slot, DISABLE_REQ);
break;
case INT_LINK_UP:
case INT_LINK_DOWN:
Expand All @@ -404,18 +389,17 @@ static void interrupt_event_handler(struct work_struct *work)
int pciehp_enable_slot(struct slot *p_slot)
{
u8 getstatus = 0;
int rc;
struct controller *ctrl = p_slot->ctrl;

pciehp_get_adapter_status(p_slot, &getstatus);
if (!getstatus) {
ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot));
return -ENODEV;
}
if (MRL_SENS(p_slot->ctrl)) {
pciehp_get_latch_status(p_slot, &getstatus);
if (getstatus) {
ctrl_info(ctrl, "Latch open on slot(%s)\n",
ctrl_info(ctrl, "Slot(%s): Latch open\n",
slot_name(p_slot));
return -ENODEV;
}
Expand All @@ -424,19 +408,13 @@ int pciehp_enable_slot(struct slot *p_slot)
if (POWER_CTRL(p_slot->ctrl)) {
pciehp_get_power_status(p_slot, &getstatus);
if (getstatus) {
ctrl_info(ctrl, "Already enabled on slot(%s)\n",
ctrl_info(ctrl, "Slot(%s): Already enabled\n",
slot_name(p_slot));
return -EINVAL;
}
}

pciehp_get_latch_status(p_slot, &getstatus);

rc = board_added(p_slot);
if (rc)
pciehp_get_latch_status(p_slot, &getstatus);

return rc;
return board_added(p_slot);
}

/*
Expand All @@ -453,7 +431,7 @@ int pciehp_disable_slot(struct slot *p_slot)
if (POWER_CTRL(p_slot->ctrl)) {
pciehp_get_power_status(p_slot, &getstatus);
if (!getstatus) {
ctrl_info(ctrl, "Already disabled on slot(%s)\n",
ctrl_info(ctrl, "Slot(%s): Already disabled\n",
slot_name(p_slot));
return -EINVAL;
}
Expand Down Expand Up @@ -481,17 +459,17 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
p_slot->state = STATIC_STATE;
break;
case POWERON_STATE:
ctrl_info(ctrl, "Slot %s is already in powering on state\n",
ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
slot_name(p_slot));
break;
case BLINKINGOFF_STATE:
case POWEROFF_STATE:
ctrl_info(ctrl, "Already enabled on slot %s\n",
ctrl_info(ctrl, "Slot(%s): Already enabled\n",
slot_name(p_slot));
break;
default:
ctrl_err(ctrl, "invalid state %#x on slot %s\n",
p_slot->state, slot_name(p_slot));
ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
slot_name(p_slot), p_slot->state);
break;
}
mutex_unlock(&p_slot->lock);
Expand All @@ -518,17 +496,17 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
p_slot->state = STATIC_STATE;
break;
case POWEROFF_STATE:
ctrl_info(ctrl, "Slot %s is already in powering off state\n",
ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
slot_name(p_slot));
break;
case BLINKINGON_STATE:
case POWERON_STATE:
ctrl_info(ctrl, "Already disabled on slot %s\n",
ctrl_info(ctrl, "Slot(%s): Already disabled\n",
slot_name(p_slot));
break;
default:
ctrl_err(ctrl, "invalid state %#x on slot %s\n",
p_slot->state, slot_name(p_slot));
ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
slot_name(p_slot), p_slot->state);
break;
}
mutex_unlock(&p_slot->lock);
Expand Down
94 changes: 52 additions & 42 deletions drivers/pci/hotplug/pciehp_hpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -535,51 +535,46 @@ void pciehp_power_off_slot(struct slot *slot)
PCI_EXP_SLTCTL_PWR_OFF);
}

static irqreturn_t pcie_isr(int irq, void *dev_id)
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;
u16 detected, intr_loc;
u16 status, events;
u8 present;
bool link;

/* Interrupts cannot originate from a controller that's asleep */
if (pdev->current_state == PCI_D3cold)
return IRQ_NONE;

pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status);
if (status == (u16) ~0) {
ctrl_info(ctrl, "%s: no response from device\n", __func__);
return IRQ_NONE;
}

/*
* In order 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.
* Slot Status contains plain status bits as well as event
* notification bits; right now we only want the event bits.
*/
intr_loc = 0;
do {
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &detected);
if (detected == (u16) ~0) {
ctrl_info(ctrl, "%s: no response from device\n",
__func__);
return IRQ_HANDLED;
}
events = status & (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC |
PCI_EXP_SLTSTA_DLLSC);
if (!events)
return IRQ_NONE;

detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
PCI_EXP_SLTSTA_PDC |
PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC);
detected &= ~intr_loc;
intr_loc |= detected;
if (!intr_loc)
return IRQ_NONE;
if (detected)
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA,
intr_loc);
} while (detected);
/* Capture link status before clearing interrupts */
if (events & PCI_EXP_SLTSTA_DLLSC)
link = pciehp_check_link_active(ctrl);

ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", intr_loc);
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events);
ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events);

/* Check Command Complete Interrupt Pending */
if (intr_loc & PCI_EXP_SLTSTA_CC) {
if (events & PCI_EXP_SLTSTA_CC) {
ctrl->cmd_busy = 0;
smp_mb();
wake_up(&ctrl->queue);
Expand All @@ -589,49 +584,64 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
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",
intr_loc, pci_name(dev));
events, pci_name(dev));
return IRQ_HANDLED;
}
}
}

if (!(intr_loc & ~PCI_EXP_SLTSTA_CC))
return IRQ_HANDLED;

/* Check Attention Button Pressed */
if (intr_loc & PCI_EXP_SLTSTA_ABP) {
ctrl_info(ctrl, "Button pressed on Slot(%s)\n",
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);
}

/* Check Presence Detect Changed */
if (intr_loc & PCI_EXP_SLTSTA_PDC) {
pciehp_get_adapter_status(slot, &present);
ctrl_info(ctrl, "Card %spresent on Slot(%s)\n",
present ? "" : "not ", slot_name(slot));
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);
}

/* Check Power Fault Detected */
if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) {
ctrl->power_fault_detected = 1;
ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(slot));
ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot));
pciehp_queue_interrupt_event(slot, INT_POWER_FAULT);
}

if (intr_loc & PCI_EXP_SLTSTA_DLLSC) {
link = pciehp_check_link_active(ctrl);
ctrl_info(ctrl, "slot(%s): Link %s event\n",
slot_name(slot), link ? "Up" : "Down");
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);
}

return IRQ_HANDLED;
}

static irqreturn_t pcie_isr(int irq, void *dev_id)
{
irqreturn_t rc, handled = IRQ_NONE;

/*
* 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);

/* Return IRQ_HANDLED if we handled one or more events */
return handled;
}

void pcie_enable_notification(struct controller *ctrl)
{
u16 cmd, mask;
Expand Down

0 comments on commit 1fa6af2

Please sign in to comment.