Commit 2e35afaefe64946caaecfacaf7fb568e46185e88
Committed by
Bjorn Helgaas
1 parent
5c32b35b00
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
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>
Showing 3 changed files with 44 additions and 0 deletions Side-by-side Diff
drivers/pci/hotplug/pciehp.h
... | ... | @@ -155,6 +155,7 @@ |
155 | 155 | void pciehp_green_led_blink(struct slot *slot); |
156 | 156 | int pciehp_check_link_status(struct controller *ctrl); |
157 | 157 | void pciehp_release_ctrl(struct controller *ctrl); |
158 | +int pciehp_reset_slot(struct slot *slot, int probe); | |
158 | 159 | |
159 | 160 | static inline const char *slot_name(struct slot *slot) |
160 | 161 | { |
drivers/pci/hotplug/pciehp_core.c
... | ... | @@ -69,6 +69,7 @@ |
69 | 69 | static int get_attention_status (struct hotplug_slot *slot, u8 *value); |
70 | 70 | static int get_latch_status (struct hotplug_slot *slot, u8 *value); |
71 | 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 | 75 | * release_slot - free up the memory used by a slot |
... | ... | @@ -111,6 +112,7 @@ |
111 | 112 | ops->disable_slot = disable_slot; |
112 | 113 | ops->get_power_status = get_power_status; |
113 | 114 | ops->get_adapter_status = get_adapter_status; |
115 | + ops->reset_slot = reset_slot; | |
114 | 116 | if (MRL_SENS(ctrl)) |
115 | 117 | ops->get_latch_status = get_latch_status; |
116 | 118 | if (ATTN_LED(ctrl)) { |
... | ... | @@ -221,6 +223,16 @@ |
221 | 223 | __func__, slot_name(slot)); |
222 | 224 | |
223 | 225 | return pciehp_get_adapter_status(slot, value); |
226 | +} | |
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); | |
224 | 236 | } |
225 | 237 | |
226 | 238 | static int pciehp_probe(struct pcie_device *dev) |
drivers/pci/hotplug/pciehp_hpc.c
... | ... | @@ -749,6 +749,37 @@ |
749 | 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 | 783 | int pcie_init_notification(struct controller *ctrl) |
753 | 784 | { |
754 | 785 | if (pciehp_request_irq(ctrl)) |