diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2016-10-03 10:43:19 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2016-10-03 10:43:19 -0400 |
commit | 64ea3b99d665d0140cdf5047571e65d8ca28d0e6 (patch) | |
tree | 59bf503a1a14b88eae8ebe6793cd685eb187be6c | |
parent | fb6b6cc41bed72cbafb0d01ab139a4a5ad673e01 (diff) | |
parent | 3161832d58c7f3bf8b190a2887086be0932d8dd3 (diff) |
Merge branch 'pci/hotplug' into next
* pci/hotplug:
x86/PCI: VMD: Request userspace control of PCIe hotplug indicators
PCI: pciehp: Allow exclusive userspace control of indicators
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
-rw-r--r-- | arch/x86/include/asm/pci.h | 14 | ||||
-rw-r--r-- | arch/x86/pci/common.c | 7 | ||||
-rw-r--r-- | arch/x86/pci/vmd.c | 1 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp.h | 3 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 3 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_ctrl.c | 84 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 121 | ||||
-rw-r--r-- | include/linux/pci.h | 3 |
8 files changed, 141 insertions, 95 deletions
diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index 9ab7507ca1c2..1411dbed5e5e 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h | |||
@@ -23,6 +23,9 @@ struct pci_sysdata { | |||
23 | #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN | 23 | #ifdef CONFIG_PCI_MSI_IRQ_DOMAIN |
24 | void *fwnode; /* IRQ domain for MSI assignment */ | 24 | void *fwnode; /* IRQ domain for MSI assignment */ |
25 | #endif | 25 | #endif |
26 | #if IS_ENABLED(CONFIG_VMD) | ||
27 | bool vmd_domain; /* True if in Intel VMD domain */ | ||
28 | #endif | ||
26 | }; | 29 | }; |
27 | 30 | ||
28 | extern int pci_routeirq; | 31 | extern int pci_routeirq; |
@@ -56,6 +59,17 @@ static inline void *_pci_root_bus_fwnode(struct pci_bus *bus) | |||
56 | #define pci_root_bus_fwnode _pci_root_bus_fwnode | 59 | #define pci_root_bus_fwnode _pci_root_bus_fwnode |
57 | #endif | 60 | #endif |
58 | 61 | ||
62 | static inline bool is_vmd(struct pci_bus *bus) | ||
63 | { | ||
64 | #if IS_ENABLED(CONFIG_VMD) | ||
65 | struct pci_sysdata *sd = bus->sysdata; | ||
66 | |||
67 | return sd->vmd_domain; | ||
68 | #else | ||
69 | return false; | ||
70 | #endif | ||
71 | } | ||
72 | |||
59 | /* Can be used to override the logic in pci_scan_bus for skipping | 73 | /* Can be used to override the logic in pci_scan_bus for skipping |
60 | already-configured bus numbers - to be used for buggy BIOSes | 74 | already-configured bus numbers - to be used for buggy BIOSes |
61 | or architectures with incomplete PCI setup by the loader */ | 75 | or architectures with incomplete PCI setup by the loader */ |
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 7b6a9d14c8c0..a4fdfa7dcc1b 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c | |||
@@ -677,6 +677,12 @@ static void set_dma_domain_ops(struct pci_dev *pdev) | |||
677 | static void set_dma_domain_ops(struct pci_dev *pdev) {} | 677 | static void set_dma_domain_ops(struct pci_dev *pdev) {} |
678 | #endif | 678 | #endif |
679 | 679 | ||
680 | static void set_dev_domain_options(struct pci_dev *pdev) | ||
681 | { | ||
682 | if (is_vmd(pdev->bus)) | ||
683 | pdev->hotplug_user_indicators = 1; | ||
684 | } | ||
685 | |||
680 | int pcibios_add_device(struct pci_dev *dev) | 686 | int pcibios_add_device(struct pci_dev *dev) |
681 | { | 687 | { |
682 | struct setup_data *data; | 688 | struct setup_data *data; |
@@ -707,6 +713,7 @@ int pcibios_add_device(struct pci_dev *dev) | |||
707 | iounmap(data); | 713 | iounmap(data); |
708 | } | 714 | } |
709 | set_dma_domain_ops(dev); | 715 | set_dma_domain_ops(dev); |
716 | set_dev_domain_options(dev); | ||
710 | return 0; | 717 | return 0; |
711 | } | 718 | } |
712 | 719 | ||
diff --git a/arch/x86/pci/vmd.c b/arch/x86/pci/vmd.c index b814ca675131..a021b7b0eb69 100644 --- a/arch/x86/pci/vmd.c +++ b/arch/x86/pci/vmd.c | |||
@@ -596,6 +596,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd) | |||
596 | .parent = res, | 596 | .parent = res, |
597 | }; | 597 | }; |
598 | 598 | ||
599 | sd->vmd_domain = true; | ||
599 | sd->domain = vmd_find_free_domain(); | 600 | sd->domain = vmd_find_free_domain(); |
600 | if (sd->domain < 0) | 601 | if (sd->domain < 0) |
601 | return sd->domain; | 602 | return sd->domain; |
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index e764918641ae..37d70b5ad22f 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h | |||
@@ -152,6 +152,9 @@ bool pciehp_check_link_active(struct controller *ctrl); | |||
152 | void pciehp_release_ctrl(struct controller *ctrl); | 152 | void pciehp_release_ctrl(struct controller *ctrl); |
153 | int pciehp_reset_slot(struct slot *slot, int probe); | 153 | int pciehp_reset_slot(struct slot *slot, int probe); |
154 | 154 | ||
155 | int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status); | ||
156 | int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status); | ||
157 | |||
155 | static inline const char *slot_name(struct slot *slot) | 158 | static inline const char *slot_name(struct slot *slot) |
156 | { | 159 | { |
157 | return hotplug_slot_name(slot->hotplug_slot); | 160 | return hotplug_slot_name(slot->hotplug_slot); |
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index fb0f86335158..7d32fa33dcef 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c | |||
@@ -113,6 +113,9 @@ static int init_slot(struct controller *ctrl) | |||
113 | if (ATTN_LED(ctrl)) { | 113 | if (ATTN_LED(ctrl)) { |
114 | ops->get_attention_status = get_attention_status; | 114 | ops->get_attention_status = get_attention_status; |
115 | ops->set_attention_status = set_attention_status; | 115 | ops->set_attention_status = set_attention_status; |
116 | } else if (ctrl->pcie->port->hotplug_user_indicators) { | ||
117 | ops->get_attention_status = pciehp_get_raw_indicator_status; | ||
118 | ops->set_attention_status = pciehp_set_raw_indicator_status; | ||
116 | } | 119 | } |
117 | 120 | ||
118 | /* register this slot with the hotplug pci core */ | 121 | /* register this slot with the hotplug pci core */ |
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 880978b6d534..efe69e879455 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c | |||
@@ -106,7 +106,7 @@ static int board_added(struct slot *p_slot) | |||
106 | 106 | ||
107 | /* Check for a power fault */ | 107 | /* Check for a power fault */ |
108 | if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) { | 108 | if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) { |
109 | ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot)); | 109 | ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot)); |
110 | retval = -EIO; | 110 | retval = -EIO; |
111 | goto err_exit; | 111 | goto err_exit; |
112 | } | 112 | } |
@@ -120,6 +120,7 @@ static int board_added(struct slot *p_slot) | |||
120 | } | 120 | } |
121 | 121 | ||
122 | pciehp_green_led_on(p_slot); | 122 | pciehp_green_led_on(p_slot); |
123 | pciehp_set_attention_status(p_slot, 0); | ||
123 | return 0; | 124 | return 0; |
124 | 125 | ||
125 | err_exit: | 126 | err_exit: |
@@ -253,11 +254,11 @@ static void handle_button_press_event(struct slot *p_slot) | |||
253 | pciehp_get_power_status(p_slot, &getstatus); | 254 | pciehp_get_power_status(p_slot, &getstatus); |
254 | if (getstatus) { | 255 | if (getstatus) { |
255 | p_slot->state = BLINKINGOFF_STATE; | 256 | p_slot->state = BLINKINGOFF_STATE; |
256 | ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n", | 257 | ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n", |
257 | slot_name(p_slot)); | 258 | slot_name(p_slot)); |
258 | } else { | 259 | } else { |
259 | p_slot->state = BLINKINGON_STATE; | 260 | p_slot->state = BLINKINGON_STATE; |
260 | ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n", | 261 | ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n", |
261 | slot_name(p_slot)); | 262 | slot_name(p_slot)); |
262 | } | 263 | } |
263 | /* blink green LED and turn off amber */ | 264 | /* blink green LED and turn off amber */ |
@@ -272,14 +273,14 @@ static void handle_button_press_event(struct slot *p_slot) | |||
272 | * press the attention again before the 5 sec. limit | 273 | * press the attention again before the 5 sec. limit |
273 | * expires to cancel hot-add or hot-remove | 274 | * expires to cancel hot-add or hot-remove |
274 | */ | 275 | */ |
275 | ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot)); | 276 | ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot)); |
276 | cancel_delayed_work(&p_slot->work); | 277 | cancel_delayed_work(&p_slot->work); |
277 | if (p_slot->state == BLINKINGOFF_STATE) | 278 | if (p_slot->state == BLINKINGOFF_STATE) |
278 | pciehp_green_led_on(p_slot); | 279 | pciehp_green_led_on(p_slot); |
279 | else | 280 | else |
280 | pciehp_green_led_off(p_slot); | 281 | pciehp_green_led_off(p_slot); |
281 | pciehp_set_attention_status(p_slot, 0); | 282 | pciehp_set_attention_status(p_slot, 0); |
282 | ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n", | 283 | ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", |
283 | slot_name(p_slot)); | 284 | slot_name(p_slot)); |
284 | p_slot->state = STATIC_STATE; | 285 | p_slot->state = STATIC_STATE; |
285 | break; | 286 | break; |
@@ -290,10 +291,12 @@ static void handle_button_press_event(struct slot *p_slot) | |||
290 | * this means that the previous attention button action | 291 | * this means that the previous attention button action |
291 | * to hot-add or hot-remove is undergoing | 292 | * to hot-add or hot-remove is undergoing |
292 | */ | 293 | */ |
293 | ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot)); | 294 | ctrl_info(ctrl, "Slot(%s): Button ignored\n", |
295 | slot_name(p_slot)); | ||
294 | break; | 296 | break; |
295 | default: | 297 | default: |
296 | ctrl_warn(ctrl, "ignoring invalid state %#x\n", p_slot->state); | 298 | ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", |
299 | slot_name(p_slot), p_slot->state); | ||
297 | break; | 300 | break; |
298 | } | 301 | } |
299 | } | 302 | } |
@@ -301,20 +304,6 @@ static void handle_button_press_event(struct slot *p_slot) | |||
301 | /* | 304 | /* |
302 | * Note: This function must be called with slot->lock held | 305 | * Note: This function must be called with slot->lock held |
303 | */ | 306 | */ |
304 | static void handle_surprise_event(struct slot *p_slot) | ||
305 | { | ||
306 | u8 getstatus; | ||
307 | |||
308 | pciehp_get_adapter_status(p_slot, &getstatus); | ||
309 | if (!getstatus) | ||
310 | pciehp_queue_power_work(p_slot, DISABLE_REQ); | ||
311 | else | ||
312 | pciehp_queue_power_work(p_slot, ENABLE_REQ); | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * Note: This function must be called with slot->lock held | ||
317 | */ | ||
318 | static void handle_link_event(struct slot *p_slot, u32 event) | 307 | static void handle_link_event(struct slot *p_slot, u32 event) |
319 | { | 308 | { |
320 | struct controller *ctrl = p_slot->ctrl; | 309 | struct controller *ctrl = p_slot->ctrl; |
@@ -330,31 +319,27 @@ static void handle_link_event(struct slot *p_slot, u32 event) | |||
330 | break; | 319 | break; |
331 | case POWERON_STATE: | 320 | case POWERON_STATE: |
332 | if (event == INT_LINK_UP) { | 321 | if (event == INT_LINK_UP) { |
333 | ctrl_info(ctrl, | 322 | ctrl_info(ctrl, "Slot(%s): Link Up event ignored; already powering on\n", |
334 | "Link Up event ignored on slot(%s): already powering on\n", | ||
335 | slot_name(p_slot)); | 323 | slot_name(p_slot)); |
336 | } else { | 324 | } else { |
337 | ctrl_info(ctrl, | 325 | ctrl_info(ctrl, "Slot(%s): Link Down event queued; currently getting powered on\n", |
338 | "Link Down event queued on slot(%s): currently getting powered on\n", | ||
339 | slot_name(p_slot)); | 326 | slot_name(p_slot)); |
340 | pciehp_queue_power_work(p_slot, DISABLE_REQ); | 327 | pciehp_queue_power_work(p_slot, DISABLE_REQ); |
341 | } | 328 | } |
342 | break; | 329 | break; |
343 | case POWEROFF_STATE: | 330 | case POWEROFF_STATE: |
344 | if (event == INT_LINK_UP) { | 331 | if (event == INT_LINK_UP) { |
345 | ctrl_info(ctrl, | 332 | ctrl_info(ctrl, "Slot(%s): Link Up event queued; currently getting powered off\n", |
346 | "Link Up event queued on slot(%s): currently getting powered off\n", | ||
347 | slot_name(p_slot)); | 333 | slot_name(p_slot)); |
348 | pciehp_queue_power_work(p_slot, ENABLE_REQ); | 334 | pciehp_queue_power_work(p_slot, ENABLE_REQ); |
349 | } else { | 335 | } else { |
350 | ctrl_info(ctrl, | 336 | ctrl_info(ctrl, "Slot(%s): Link Down event ignored; already powering off\n", |
351 | "Link Down event ignored on slot(%s): already powering off\n", | ||
352 | slot_name(p_slot)); | 337 | slot_name(p_slot)); |
353 | } | 338 | } |
354 | break; | 339 | break; |
355 | default: | 340 | default: |
356 | ctrl_err(ctrl, "ignoring invalid state %#x on slot(%s)\n", | 341 | ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", |
357 | p_slot->state, slot_name(p_slot)); | 342 | slot_name(p_slot), p_slot->state); |
358 | break; | 343 | break; |
359 | } | 344 | } |
360 | } | 345 | } |
@@ -377,14 +362,14 @@ static void interrupt_event_handler(struct work_struct *work) | |||
377 | pciehp_green_led_off(p_slot); | 362 | pciehp_green_led_off(p_slot); |
378 | break; | 363 | break; |
379 | case INT_PRESENCE_ON: | 364 | case INT_PRESENCE_ON: |
380 | handle_surprise_event(p_slot); | 365 | pciehp_queue_power_work(p_slot, ENABLE_REQ); |
381 | break; | 366 | break; |
382 | case INT_PRESENCE_OFF: | 367 | case INT_PRESENCE_OFF: |
383 | /* | 368 | /* |
384 | * Regardless of surprise capability, we need to | 369 | * Regardless of surprise capability, we need to |
385 | * definitely remove a card that has been pulled out! | 370 | * definitely remove a card that has been pulled out! |
386 | */ | 371 | */ |
387 | handle_surprise_event(p_slot); | 372 | pciehp_queue_power_work(p_slot, DISABLE_REQ); |
388 | break; | 373 | break; |
389 | case INT_LINK_UP: | 374 | case INT_LINK_UP: |
390 | case INT_LINK_DOWN: | 375 | case INT_LINK_DOWN: |
@@ -404,18 +389,17 @@ static void interrupt_event_handler(struct work_struct *work) | |||
404 | int pciehp_enable_slot(struct slot *p_slot) | 389 | int pciehp_enable_slot(struct slot *p_slot) |
405 | { | 390 | { |
406 | u8 getstatus = 0; | 391 | u8 getstatus = 0; |
407 | int rc; | ||
408 | struct controller *ctrl = p_slot->ctrl; | 392 | struct controller *ctrl = p_slot->ctrl; |
409 | 393 | ||
410 | pciehp_get_adapter_status(p_slot, &getstatus); | 394 | pciehp_get_adapter_status(p_slot, &getstatus); |
411 | if (!getstatus) { | 395 | if (!getstatus) { |
412 | ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); | 396 | ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot)); |
413 | return -ENODEV; | 397 | return -ENODEV; |
414 | } | 398 | } |
415 | if (MRL_SENS(p_slot->ctrl)) { | 399 | if (MRL_SENS(p_slot->ctrl)) { |
416 | pciehp_get_latch_status(p_slot, &getstatus); | 400 | pciehp_get_latch_status(p_slot, &getstatus); |
417 | if (getstatus) { | 401 | if (getstatus) { |
418 | ctrl_info(ctrl, "Latch open on slot(%s)\n", | 402 | ctrl_info(ctrl, "Slot(%s): Latch open\n", |
419 | slot_name(p_slot)); | 403 | slot_name(p_slot)); |
420 | return -ENODEV; | 404 | return -ENODEV; |
421 | } | 405 | } |
@@ -424,19 +408,13 @@ int pciehp_enable_slot(struct slot *p_slot) | |||
424 | if (POWER_CTRL(p_slot->ctrl)) { | 408 | if (POWER_CTRL(p_slot->ctrl)) { |
425 | pciehp_get_power_status(p_slot, &getstatus); | 409 | pciehp_get_power_status(p_slot, &getstatus); |
426 | if (getstatus) { | 410 | if (getstatus) { |
427 | ctrl_info(ctrl, "Already enabled on slot(%s)\n", | 411 | ctrl_info(ctrl, "Slot(%s): Already enabled\n", |
428 | slot_name(p_slot)); | 412 | slot_name(p_slot)); |
429 | return -EINVAL; | 413 | return -EINVAL; |
430 | } | 414 | } |
431 | } | 415 | } |
432 | 416 | ||
433 | pciehp_get_latch_status(p_slot, &getstatus); | 417 | return board_added(p_slot); |
434 | |||
435 | rc = board_added(p_slot); | ||
436 | if (rc) | ||
437 | pciehp_get_latch_status(p_slot, &getstatus); | ||
438 | |||
439 | return rc; | ||
440 | } | 418 | } |
441 | 419 | ||
442 | /* | 420 | /* |
@@ -453,7 +431,7 @@ int pciehp_disable_slot(struct slot *p_slot) | |||
453 | if (POWER_CTRL(p_slot->ctrl)) { | 431 | if (POWER_CTRL(p_slot->ctrl)) { |
454 | pciehp_get_power_status(p_slot, &getstatus); | 432 | pciehp_get_power_status(p_slot, &getstatus); |
455 | if (!getstatus) { | 433 | if (!getstatus) { |
456 | ctrl_info(ctrl, "Already disabled on slot(%s)\n", | 434 | ctrl_info(ctrl, "Slot(%s): Already disabled\n", |
457 | slot_name(p_slot)); | 435 | slot_name(p_slot)); |
458 | return -EINVAL; | 436 | return -EINVAL; |
459 | } | 437 | } |
@@ -481,17 +459,17 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot) | |||
481 | p_slot->state = STATIC_STATE; | 459 | p_slot->state = STATIC_STATE; |
482 | break; | 460 | break; |
483 | case POWERON_STATE: | 461 | case POWERON_STATE: |
484 | ctrl_info(ctrl, "Slot %s is already in powering on state\n", | 462 | ctrl_info(ctrl, "Slot(%s): Already in powering on state\n", |
485 | slot_name(p_slot)); | 463 | slot_name(p_slot)); |
486 | break; | 464 | break; |
487 | case BLINKINGOFF_STATE: | 465 | case BLINKINGOFF_STATE: |
488 | case POWEROFF_STATE: | 466 | case POWEROFF_STATE: |
489 | ctrl_info(ctrl, "Already enabled on slot %s\n", | 467 | ctrl_info(ctrl, "Slot(%s): Already enabled\n", |
490 | slot_name(p_slot)); | 468 | slot_name(p_slot)); |
491 | break; | 469 | break; |
492 | default: | 470 | default: |
493 | ctrl_err(ctrl, "invalid state %#x on slot %s\n", | 471 | ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", |
494 | p_slot->state, slot_name(p_slot)); | 472 | slot_name(p_slot), p_slot->state); |
495 | break; | 473 | break; |
496 | } | 474 | } |
497 | mutex_unlock(&p_slot->lock); | 475 | mutex_unlock(&p_slot->lock); |
@@ -518,17 +496,17 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot) | |||
518 | p_slot->state = STATIC_STATE; | 496 | p_slot->state = STATIC_STATE; |
519 | break; | 497 | break; |
520 | case POWEROFF_STATE: | 498 | case POWEROFF_STATE: |
521 | ctrl_info(ctrl, "Slot %s is already in powering off state\n", | 499 | ctrl_info(ctrl, "Slot(%s): Already in powering off state\n", |
522 | slot_name(p_slot)); | 500 | slot_name(p_slot)); |
523 | break; | 501 | break; |
524 | case BLINKINGON_STATE: | 502 | case BLINKINGON_STATE: |
525 | case POWERON_STATE: | 503 | case POWERON_STATE: |
526 | ctrl_info(ctrl, "Already disabled on slot %s\n", | 504 | ctrl_info(ctrl, "Slot(%s): Already disabled\n", |
527 | slot_name(p_slot)); | 505 | slot_name(p_slot)); |
528 | break; | 506 | break; |
529 | default: | 507 | default: |
530 | ctrl_err(ctrl, "invalid state %#x on slot %s\n", | 508 | ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", |
531 | p_slot->state, slot_name(p_slot)); | 509 | slot_name(p_slot), p_slot->state); |
532 | break; | 510 | break; |
533 | } | 511 | } |
534 | mutex_unlock(&p_slot->lock); | 512 | mutex_unlock(&p_slot->lock); |
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 08e84d61874e..b57fc6d6e28a 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
@@ -355,6 +355,18 @@ static int pciehp_link_enable(struct controller *ctrl) | |||
355 | return __pciehp_link_set(ctrl, true); | 355 | return __pciehp_link_set(ctrl, true); |
356 | } | 356 | } |
357 | 357 | ||
358 | int pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, | ||
359 | u8 *status) | ||
360 | { | ||
361 | struct slot *slot = hotplug_slot->private; | ||
362 | struct pci_dev *pdev = ctrl_dev(slot->ctrl); | ||
363 | u16 slot_ctrl; | ||
364 | |||
365 | pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); | ||
366 | *status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6; | ||
367 | return 0; | ||
368 | } | ||
369 | |||
358 | void pciehp_get_attention_status(struct slot *slot, u8 *status) | 370 | void pciehp_get_attention_status(struct slot *slot, u8 *status) |
359 | { | 371 | { |
360 | struct controller *ctrl = slot->ctrl; | 372 | struct controller *ctrl = slot->ctrl; |
@@ -431,6 +443,17 @@ int pciehp_query_power_fault(struct slot *slot) | |||
431 | return !!(slot_status & PCI_EXP_SLTSTA_PFD); | 443 | return !!(slot_status & PCI_EXP_SLTSTA_PFD); |
432 | } | 444 | } |
433 | 445 | ||
446 | int pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot, | ||
447 | u8 status) | ||
448 | { | ||
449 | struct slot *slot = hotplug_slot->private; | ||
450 | struct controller *ctrl = slot->ctrl; | ||
451 | |||
452 | pcie_write_cmd_nowait(ctrl, status << 6, | ||
453 | PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC); | ||
454 | return 0; | ||
455 | } | ||
456 | |||
434 | void pciehp_set_attention_status(struct slot *slot, u8 value) | 457 | void pciehp_set_attention_status(struct slot *slot, u8 value) |
435 | { | 458 | { |
436 | struct controller *ctrl = slot->ctrl; | 459 | struct controller *ctrl = slot->ctrl; |
@@ -535,14 +558,14 @@ void pciehp_power_off_slot(struct slot *slot) | |||
535 | PCI_EXP_SLTCTL_PWR_OFF); | 558 | PCI_EXP_SLTCTL_PWR_OFF); |
536 | } | 559 | } |
537 | 560 | ||
538 | static irqreturn_t pcie_isr(int irq, void *dev_id) | 561 | static irqreturn_t pciehp_isr(int irq, void *dev_id) |
539 | { | 562 | { |
540 | struct controller *ctrl = (struct controller *)dev_id; | 563 | struct controller *ctrl = (struct controller *)dev_id; |
541 | struct pci_dev *pdev = ctrl_dev(ctrl); | 564 | struct pci_dev *pdev = ctrl_dev(ctrl); |
542 | struct pci_bus *subordinate = pdev->subordinate; | 565 | struct pci_bus *subordinate = pdev->subordinate; |
543 | struct pci_dev *dev; | 566 | struct pci_dev *dev; |
544 | struct slot *slot = ctrl->slot; | 567 | struct slot *slot = ctrl->slot; |
545 | u16 detected, intr_loc; | 568 | u16 status, events; |
546 | u8 present; | 569 | u8 present; |
547 | bool link; | 570 | bool link; |
548 | 571 | ||
@@ -550,36 +573,31 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) | |||
550 | if (pdev->current_state == PCI_D3cold) | 573 | if (pdev->current_state == PCI_D3cold) |
551 | return IRQ_NONE; | 574 | return IRQ_NONE; |
552 | 575 | ||
576 | pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status); | ||
577 | if (status == (u16) ~0) { | ||
578 | ctrl_info(ctrl, "%s: no response from device\n", __func__); | ||
579 | return IRQ_NONE; | ||
580 | } | ||
581 | |||
553 | /* | 582 | /* |
554 | * In order to guarantee that all interrupt events are | 583 | * Slot Status contains plain status bits as well as event |
555 | * serviced, we need to re-inspect Slot Status register after | 584 | * notification bits; right now we only want the event bits. |
556 | * clearing what is presumed to be the last pending interrupt. | ||
557 | */ | 585 | */ |
558 | intr_loc = 0; | 586 | events = status & (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | |
559 | do { | 587 | PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC | |
560 | pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &detected); | 588 | PCI_EXP_SLTSTA_DLLSC); |
561 | if (detected == (u16) ~0) { | 589 | if (!events) |
562 | ctrl_info(ctrl, "%s: no response from device\n", | 590 | return IRQ_NONE; |
563 | __func__); | ||
564 | return IRQ_HANDLED; | ||
565 | } | ||
566 | 591 | ||
567 | detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | | 592 | /* Capture link status before clearing interrupts */ |
568 | PCI_EXP_SLTSTA_PDC | | 593 | if (events & PCI_EXP_SLTSTA_DLLSC) |
569 | PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC); | 594 | link = pciehp_check_link_active(ctrl); |
570 | detected &= ~intr_loc; | ||
571 | intr_loc |= detected; | ||
572 | if (!intr_loc) | ||
573 | return IRQ_NONE; | ||
574 | if (detected) | ||
575 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, | ||
576 | intr_loc); | ||
577 | } while (detected); | ||
578 | 595 | ||
579 | ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", intr_loc); | 596 | pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events); |
597 | ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events); | ||
580 | 598 | ||
581 | /* Check Command Complete Interrupt Pending */ | 599 | /* Check Command Complete Interrupt Pending */ |
582 | if (intr_loc & PCI_EXP_SLTSTA_CC) { | 600 | if (events & PCI_EXP_SLTSTA_CC) { |
583 | ctrl->cmd_busy = 0; | 601 | ctrl->cmd_busy = 0; |
584 | smp_mb(); | 602 | smp_mb(); |
585 | wake_up(&ctrl->queue); | 603 | wake_up(&ctrl->queue); |
@@ -589,42 +607,38 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) | |||
589 | list_for_each_entry(dev, &subordinate->devices, bus_list) { | 607 | list_for_each_entry(dev, &subordinate->devices, bus_list) { |
590 | if (dev->ignore_hotplug) { | 608 | if (dev->ignore_hotplug) { |
591 | ctrl_dbg(ctrl, "ignoring hotplug event %#06x (%s requested no hotplug)\n", | 609 | ctrl_dbg(ctrl, "ignoring hotplug event %#06x (%s requested no hotplug)\n", |
592 | intr_loc, pci_name(dev)); | 610 | events, pci_name(dev)); |
593 | return IRQ_HANDLED; | 611 | return IRQ_HANDLED; |
594 | } | 612 | } |
595 | } | 613 | } |
596 | } | 614 | } |
597 | 615 | ||
598 | if (!(intr_loc & ~PCI_EXP_SLTSTA_CC)) | ||
599 | return IRQ_HANDLED; | ||
600 | |||
601 | /* Check Attention Button Pressed */ | 616 | /* Check Attention Button Pressed */ |
602 | if (intr_loc & PCI_EXP_SLTSTA_ABP) { | 617 | if (events & PCI_EXP_SLTSTA_ABP) { |
603 | ctrl_info(ctrl, "Button pressed on Slot(%s)\n", | 618 | ctrl_info(ctrl, "Slot(%s): Attention button pressed\n", |
604 | slot_name(slot)); | 619 | slot_name(slot)); |
605 | pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS); | 620 | pciehp_queue_interrupt_event(slot, INT_BUTTON_PRESS); |
606 | } | 621 | } |
607 | 622 | ||
608 | /* Check Presence Detect Changed */ | 623 | /* Check Presence Detect Changed */ |
609 | if (intr_loc & PCI_EXP_SLTSTA_PDC) { | 624 | if (events & PCI_EXP_SLTSTA_PDC) { |
610 | pciehp_get_adapter_status(slot, &present); | 625 | present = !!(status & PCI_EXP_SLTSTA_PDS); |
611 | ctrl_info(ctrl, "Card %spresent on Slot(%s)\n", | 626 | ctrl_info(ctrl, "Slot(%s): Card %spresent\n", slot_name(slot), |
612 | present ? "" : "not ", slot_name(slot)); | 627 | present ? "" : "not "); |
613 | pciehp_queue_interrupt_event(slot, present ? INT_PRESENCE_ON : | 628 | pciehp_queue_interrupt_event(slot, present ? INT_PRESENCE_ON : |
614 | INT_PRESENCE_OFF); | 629 | INT_PRESENCE_OFF); |
615 | } | 630 | } |
616 | 631 | ||
617 | /* Check Power Fault Detected */ | 632 | /* Check Power Fault Detected */ |
618 | if ((intr_loc & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { | 633 | if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { |
619 | ctrl->power_fault_detected = 1; | 634 | ctrl->power_fault_detected = 1; |
620 | ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(slot)); | 635 | ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(slot)); |
621 | pciehp_queue_interrupt_event(slot, INT_POWER_FAULT); | 636 | pciehp_queue_interrupt_event(slot, INT_POWER_FAULT); |
622 | } | 637 | } |
623 | 638 | ||
624 | if (intr_loc & PCI_EXP_SLTSTA_DLLSC) { | 639 | if (events & PCI_EXP_SLTSTA_DLLSC) { |
625 | link = pciehp_check_link_active(ctrl); | 640 | ctrl_info(ctrl, "Slot(%s): Link %s\n", slot_name(slot), |
626 | ctrl_info(ctrl, "slot(%s): Link %s event\n", | 641 | link ? "Up" : "Down"); |
627 | slot_name(slot), link ? "Up" : "Down"); | ||
628 | pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP : | 642 | pciehp_queue_interrupt_event(slot, link ? INT_LINK_UP : |
629 | INT_LINK_DOWN); | 643 | INT_LINK_DOWN); |
630 | } | 644 | } |
@@ -632,6 +646,25 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) | |||
632 | return IRQ_HANDLED; | 646 | return IRQ_HANDLED; |
633 | } | 647 | } |
634 | 648 | ||
649 | static irqreturn_t pcie_isr(int irq, void *dev_id) | ||
650 | { | ||
651 | irqreturn_t rc, handled = IRQ_NONE; | ||
652 | |||
653 | /* | ||
654 | * To guarantee that all interrupt events are serviced, we need to | ||
655 | * re-inspect Slot Status register after clearing what is presumed | ||
656 | * to be the last pending interrupt. | ||
657 | */ | ||
658 | do { | ||
659 | rc = pciehp_isr(irq, dev_id); | ||
660 | if (rc == IRQ_HANDLED) | ||
661 | handled = IRQ_HANDLED; | ||
662 | } while (rc == IRQ_HANDLED); | ||
663 | |||
664 | /* Return IRQ_HANDLED if we handled one or more events */ | ||
665 | return handled; | ||
666 | } | ||
667 | |||
635 | void pcie_enable_notification(struct controller *ctrl) | 668 | void pcie_enable_notification(struct controller *ctrl) |
636 | { | 669 | { |
637 | u16 cmd, mask; | 670 | u16 cmd, mask; |
@@ -804,6 +837,10 @@ struct controller *pcie_init(struct pcie_device *dev) | |||
804 | } | 837 | } |
805 | ctrl->pcie = dev; | 838 | ctrl->pcie = dev; |
806 | pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); | 839 | pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap); |
840 | |||
841 | if (pdev->hotplug_user_indicators) | ||
842 | slot_cap &= ~(PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_PIP); | ||
843 | |||
807 | ctrl->slot_cap = slot_cap; | 844 | ctrl->slot_cap = slot_cap; |
808 | mutex_init(&ctrl->ctrl_lock); | 845 | mutex_init(&ctrl->ctrl_lock); |
809 | init_waitqueue_head(&ctrl->queue); | 846 | init_waitqueue_head(&ctrl->queue); |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 84d222ad3c08..b0e4ed19b315 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -312,6 +312,9 @@ struct pci_dev { | |||
312 | powered on/off by the | 312 | powered on/off by the |
313 | corresponding bridge */ | 313 | corresponding bridge */ |
314 | unsigned int ignore_hotplug:1; /* Ignore hotplug events */ | 314 | unsigned int ignore_hotplug:1; /* Ignore hotplug events */ |
315 | unsigned int hotplug_user_indicators:1; /* SlotCtl indicators | ||
316 | controlled exclusively by | ||
317 | user sysfs */ | ||
315 | unsigned int d3_delay; /* D3->D0 transition time in ms */ | 318 | unsigned int d3_delay; /* D3->D0 transition time in ms */ |
316 | unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ | 319 | unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */ |
317 | 320 | ||