aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLukas Wunner <lukas@wunner.de>2018-07-28 01:22:00 -0400
committerBjorn Helgaas <bhelgaas@google.com>2018-07-31 14:27:24 -0400
commit4e6a13356f1c1dc27ff48ff35576a478d73f8713 (patch)
treef46973859c83eaa6e3d1be7087a208cea14c389f
parent8bb46b079d05be4435892869dad23e9af23098ab (diff)
PCI: pciehp: Deduplicate presence check on probe & resume
On driver probe and on resume from system sleep, pciehp checks the Presence Detect State bit in the Slot Status register to bring up an occupied slot or bring down an unoccupied slot. Both code paths are identical, so deduplicate them per Mika's request. On probe, an additional check is performed to disable power of an unoccupied slot. This can e.g. happen if power was enabled by BIOS. It cannot happen once pciehp has taken control, hence is not necessary on resume: The Slot Control register is set to the same value that it had on suspend by pci_restore_state(), so if the slot was occupied, power is enabled and if it wasn't, power is disabled. Should occupancy have changed during the system sleep transition, power is adjusted by bringing up or down the slot per the paragraph above. To allow for deduplication of the presence check, move the power check to pcie_init(). This seems safer anyway, because right now it is performed while interrupts are already enabled, and although I can't think of a scenario where pciehp_power_off_slot() and the IRQ thread collide, it does feel brittle. However this means that pcie_init() may now write to the Slot Control register before the IRQ is requested. If both the CCIE and HPIE bits happen to be set, pcie_wait_cmd() will wait for an interrupt (instead of polling the Command Completed bit) and eventually emit a timeout message. Additionally, if a level-triggered INTx interrupt is used, the user may see a spurious interrupt splat. Avoid by disabling interrupts before disabling power. (Normally the HPIE and CCIE bits should be clear on probe, but conceivably they may already have been set e.g. by BIOS.) Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
-rw-r--r--drivers/pci/hotplug/pciehp_core.c63
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c14
2 files changed, 46 insertions, 31 deletions
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 01fcf1fa0f66..ec48c9433ae5 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -200,12 +200,40 @@ static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
200 return pciehp_reset_slot(slot, probe); 200 return pciehp_reset_slot(slot, probe);
201} 201}
202 202
203/**
204 * pciehp_check_presence() - synthesize event if presence has changed
205 *
206 * On probe and resume, an explicit presence check is necessary to bring up an
207 * occupied slot or bring down an unoccupied slot. This can't be triggered by
208 * events in the Slot Status register, they may be stale and are therefore
209 * cleared. Secondly, sending an interrupt for "events that occur while
210 * interrupt generation is disabled [when] interrupt generation is subsequently
211 * enabled" is optional per PCIe r4.0, sec 6.7.3.4.
212 */
213static void pciehp_check_presence(struct controller *ctrl)
214{
215 struct slot *slot = ctrl->slot;
216 u8 occupied;
217
218 down_read(&ctrl->reset_lock);
219 mutex_lock(&slot->lock);
220
221 pciehp_get_adapter_status(slot, &occupied);
222 if ((occupied && (slot->state == OFF_STATE ||
223 slot->state == BLINKINGON_STATE)) ||
224 (!occupied && (slot->state == ON_STATE ||
225 slot->state == BLINKINGOFF_STATE)))
226 pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
227
228 mutex_unlock(&slot->lock);
229 up_read(&ctrl->reset_lock);
230}
231
203static int pciehp_probe(struct pcie_device *dev) 232static int pciehp_probe(struct pcie_device *dev)
204{ 233{
205 int rc; 234 int rc;
206 struct controller *ctrl; 235 struct controller *ctrl;
207 struct slot *slot; 236 struct slot *slot;
208 u8 occupied, poweron;
209 237
210 /* If this is not a "hotplug" service, we have no business here. */ 238 /* If this is not a "hotplug" service, we have no business here. */
211 if (dev->service != PCIE_PORT_SERVICE_HP) 239 if (dev->service != PCIE_PORT_SERVICE_HP)
@@ -250,21 +278,7 @@ static int pciehp_probe(struct pcie_device *dev)
250 goto err_out_shutdown_notification; 278 goto err_out_shutdown_notification;
251 } 279 }
252 280
253 /* Check if slot is occupied */ 281 pciehp_check_presence(ctrl);
254 down_read(&ctrl->reset_lock);
255 mutex_lock(&slot->lock);
256 pciehp_get_adapter_status(slot, &occupied);
257 pciehp_get_power_status(slot, &poweron);
258 if ((occupied && (slot->state == OFF_STATE ||
259 slot->state == BLINKINGON_STATE)) ||
260 (!occupied && (slot->state == ON_STATE ||
261 slot->state == BLINKINGOFF_STATE)))
262 pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
263 /* If empty slot's power status is on, turn power off */
264 if (!occupied && poweron && POWER_CTRL(ctrl))
265 pciehp_power_off_slot(slot);
266 mutex_unlock(&slot->lock);
267 up_read(&ctrl->reset_lock);
268 282
269 return 0; 283 return 0;
270 284
@@ -311,22 +325,9 @@ static int pciehp_resume_noirq(struct pcie_device *dev)
311 325
312static int pciehp_resume(struct pcie_device *dev) 326static int pciehp_resume(struct pcie_device *dev)
313{ 327{
314 struct controller *ctrl; 328 struct controller *ctrl = get_service_data(dev);
315 struct slot *slot;
316 u8 status;
317
318 ctrl = get_service_data(dev);
319 slot = ctrl->slot;
320 329
321 /* Check if slot is occupied */ 330 pciehp_check_presence(ctrl);
322 pciehp_get_adapter_status(slot, &status);
323 mutex_lock(&slot->lock);
324 if ((status && (slot->state == OFF_STATE ||
325 slot->state == BLINKINGON_STATE)) ||
326 (!status && (slot->state == ON_STATE ||
327 slot->state == BLINKINGOFF_STATE)))
328 pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
329 mutex_unlock(&slot->lock);
330 331
331 return 0; 332 return 0;
332} 333}
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 6f7de0819c88..5b15e76f3564 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -856,6 +856,7 @@ struct controller *pcie_init(struct pcie_device *dev)
856{ 856{
857 struct controller *ctrl; 857 struct controller *ctrl;
858 u32 slot_cap, link_cap; 858 u32 slot_cap, link_cap;
859 u8 occupied, poweron;
859 struct pci_dev *pdev = dev->port; 860 struct pci_dev *pdev = dev->port;
860 861
861 ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); 862 ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
@@ -910,6 +911,19 @@ struct controller *pcie_init(struct pcie_device *dev)
910 if (pcie_init_slot(ctrl)) 911 if (pcie_init_slot(ctrl))
911 goto abort_ctrl; 912 goto abort_ctrl;
912 913
914 /*
915 * If empty slot's power status is on, turn power off. The IRQ isn't
916 * requested yet, so avoid triggering a notification with this command.
917 */
918 if (POWER_CTRL(ctrl)) {
919 pciehp_get_adapter_status(ctrl->slot, &occupied);
920 pciehp_get_power_status(ctrl->slot, &poweron);
921 if (!occupied && poweron) {
922 pcie_disable_notification(ctrl);
923 pciehp_power_off_slot(ctrl->slot);
924 }
925 }
926
913 return ctrl; 927 return ctrl;
914 928
915abort_ctrl: 929abort_ctrl: