diff options
author | Alex Williamson <alex.williamson@redhat.com> | 2013-08-08 16:09:37 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2013-08-14 16:55:26 -0400 |
commit | 2e35afaefe64946caaecfacaf7fb568e46185e88 (patch) | |
tree | b8aaf1bb815978199cf9d03897dc1a83d84208d7 | |
parent | 5c32b35b004f5ef70dcf62bbc42b8bed1e50b471 (diff) |
PCI: pciehp: Add reset_slot() method
PCIe hotplug has a bus per slot, so we can just use a normal
secondary bus reset. However, if a slot supports surprise removal,
a bus reset can be seen as a presence detection change triggering
a hot-remove followed by a hot-add. Disable presence detection from
triggering an interrupt or being polled around the bus reset.
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r-- | drivers/pci/hotplug/pciehp.h | 1 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 12 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 31 |
3 files changed, 44 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 7fb326983ed6..541bbe6d5343 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h | |||
@@ -155,6 +155,7 @@ void pciehp_green_led_off(struct slot *slot); | |||
155 | void pciehp_green_led_blink(struct slot *slot); | 155 | void pciehp_green_led_blink(struct slot *slot); |
156 | int pciehp_check_link_status(struct controller *ctrl); | 156 | int pciehp_check_link_status(struct controller *ctrl); |
157 | void pciehp_release_ctrl(struct controller *ctrl); | 157 | void pciehp_release_ctrl(struct controller *ctrl); |
158 | int pciehp_reset_slot(struct slot *slot, int probe); | ||
158 | 159 | ||
159 | static inline const char *slot_name(struct slot *slot) | 160 | static inline const char *slot_name(struct slot *slot) |
160 | { | 161 | { |
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 7d72c5e2eba9..f4a18f51a29c 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c | |||
@@ -69,6 +69,7 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value); | |||
69 | static int get_attention_status (struct hotplug_slot *slot, u8 *value); | 69 | static int get_attention_status (struct hotplug_slot *slot, u8 *value); |
70 | static int get_latch_status (struct hotplug_slot *slot, u8 *value); | 70 | static int get_latch_status (struct hotplug_slot *slot, u8 *value); |
71 | static int get_adapter_status (struct hotplug_slot *slot, u8 *value); | 71 | static int get_adapter_status (struct hotplug_slot *slot, u8 *value); |
72 | static int reset_slot (struct hotplug_slot *slot, int probe); | ||
72 | 73 | ||
73 | /** | 74 | /** |
74 | * release_slot - free up the memory used by a slot | 75 | * release_slot - free up the memory used by a slot |
@@ -111,6 +112,7 @@ static int init_slot(struct controller *ctrl) | |||
111 | ops->disable_slot = disable_slot; | 112 | ops->disable_slot = disable_slot; |
112 | ops->get_power_status = get_power_status; | 113 | ops->get_power_status = get_power_status; |
113 | ops->get_adapter_status = get_adapter_status; | 114 | ops->get_adapter_status = get_adapter_status; |
115 | ops->reset_slot = reset_slot; | ||
114 | if (MRL_SENS(ctrl)) | 116 | if (MRL_SENS(ctrl)) |
115 | ops->get_latch_status = get_latch_status; | 117 | ops->get_latch_status = get_latch_status; |
116 | if (ATTN_LED(ctrl)) { | 118 | if (ATTN_LED(ctrl)) { |
@@ -223,6 +225,16 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) | |||
223 | return pciehp_get_adapter_status(slot, value); | 225 | return pciehp_get_adapter_status(slot, value); |
224 | } | 226 | } |
225 | 227 | ||
228 | static int reset_slot(struct hotplug_slot *hotplug_slot, int probe) | ||
229 | { | ||
230 | struct slot *slot = hotplug_slot->private; | ||
231 | |||
232 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", | ||
233 | __func__, slot_name(slot)); | ||
234 | |||
235 | return pciehp_reset_slot(slot, probe); | ||
236 | } | ||
237 | |||
226 | static int pciehp_probe(struct pcie_device *dev) | 238 | static int pciehp_probe(struct pcie_device *dev) |
227 | { | 239 | { |
228 | int rc; | 240 | int rc; |
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index b2255736ac81..51f56ef4ab6f 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
@@ -749,6 +749,37 @@ static void pcie_disable_notification(struct controller *ctrl) | |||
749 | ctrl_warn(ctrl, "Cannot disable software notification\n"); | 749 | ctrl_warn(ctrl, "Cannot disable software notification\n"); |
750 | } | 750 | } |
751 | 751 | ||
752 | /* | ||
753 | * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary | ||
754 | * bus reset of the bridge, but if the slot supports surprise removal we need | ||
755 | * to disable presence detection around the bus reset and clear any spurious | ||
756 | * events after. | ||
757 | */ | ||
758 | int pciehp_reset_slot(struct slot *slot, int probe) | ||
759 | { | ||
760 | struct controller *ctrl = slot->ctrl; | ||
761 | |||
762 | if (probe) | ||
763 | return 0; | ||
764 | |||
765 | if (HP_SUPR_RM(ctrl)) { | ||
766 | pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_PDCE); | ||
767 | if (pciehp_poll_mode) | ||
768 | del_timer_sync(&ctrl->poll_timer); | ||
769 | } | ||
770 | |||
771 | pci_reset_bridge_secondary_bus(ctrl->pcie->port); | ||
772 | |||
773 | if (HP_SUPR_RM(ctrl)) { | ||
774 | pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC); | ||
775 | pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE); | ||
776 | if (pciehp_poll_mode) | ||
777 | int_poll_timeout(ctrl->poll_timer.data); | ||
778 | } | ||
779 | |||
780 | return 0; | ||
781 | } | ||
782 | |||
752 | int pcie_init_notification(struct controller *ctrl) | 783 | int pcie_init_notification(struct controller *ctrl) |
753 | { | 784 | { |
754 | if (pciehp_request_irq(ctrl)) | 785 | if (pciehp_request_irq(ctrl)) |