aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/hotplug/pciehp_ctrl.c
diff options
context:
space:
mode:
authorRajat Jain <rajatxjain@gmail.com>2014-02-04 21:29:10 -0500
committerBjorn Helgaas <bhelgaas@google.com>2014-02-10 20:12:44 -0500
commite48f1b67f668762003e8888eccd7acb71109e874 (patch)
treed8b98a5fd5c5a2ba1340558a59467f31499a971c /drivers/pci/hotplug/pciehp_ctrl.c
parent4703389f7df70518cc4ea584f3e64cb11f28aa7c (diff)
PCI: pciehp: Use link change notifications for hot-plug and removal
A lot of systems do not have the fancy buttons and LEDs, and instead want to rely only on the Link state change events to drive the hotplug and removal state machinery. (http://www.spinics.net/lists/hotplug/msg05802.html) This patch adds support for that functionality. Here are the details about the patch itself: * Define and use interrupt events for linkup / linkdown. * Make the pcie_isr() also look at link events, and direct control to corresponding (new) link state change handler function. * Introduce the functions to handle link-up and link-down events and queue the add / removal work in the slot->wq to be processed by pciehp_power_thread() As a side note, this patch also fixes the bug https://bugzilla.kernel.org/show_bug.cgi?id=65521 "pciehp ignores Data Link Layer State Changed bit." Signed-off-by: Rajat Jain <rajatxjain@gmail.com> Signed-off-by: Rajat Jain <rajatjain@juniper.net> Signed-off-by: Guenter Roeck <groeck@juniper.net> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci/hotplug/pciehp_ctrl.c')
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index 50628487597d..56082842b265 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -150,6 +150,27 @@ u8 pciehp_handle_power_fault(struct slot *p_slot)
150 return 1; 150 return 1;
151} 151}
152 152
153void pciehp_handle_linkstate_change(struct slot *p_slot)
154{
155 u32 event_type;
156 struct controller *ctrl = p_slot->ctrl;
157
158 /* Link Status Change */
159 ctrl_dbg(ctrl, "Data Link Layer State change\n");
160
161 if (pciehp_check_link_active(ctrl)) {
162 ctrl_info(ctrl, "slot(%s): Link Up event\n",
163 slot_name(p_slot));
164 event_type = INT_LINK_UP;
165 } else {
166 ctrl_info(ctrl, "slot(%s): Link Down event\n",
167 slot_name(p_slot));
168 event_type = INT_LINK_DOWN;
169 }
170
171 queue_interrupt_event(p_slot, event_type);
172}
173
153/* The following routines constitute the bulk of the 174/* The following routines constitute the bulk of the
154 hotplug controller logic 175 hotplug controller logic
155 */ 176 */
@@ -415,6 +436,69 @@ static void handle_surprise_event(struct slot *p_slot)
415 queue_work(p_slot->wq, &info->work); 436 queue_work(p_slot->wq, &info->work);
416} 437}
417 438
439/*
440 * Note: This function must be called with slot->lock held
441 */
442static void handle_link_event(struct slot *p_slot, u32 event)
443{
444 struct controller *ctrl = p_slot->ctrl;
445 struct power_work_info *info;
446
447 info = kmalloc(sizeof(*info), GFP_KERNEL);
448 if (!info) {
449 ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
450 __func__);
451 return;
452 }
453 info->p_slot = p_slot;
454 INIT_WORK(&info->work, pciehp_power_thread);
455
456 switch (p_slot->state) {
457 case BLINKINGON_STATE:
458 case BLINKINGOFF_STATE:
459 cancel_delayed_work(&p_slot->work);
460 /* Fall through */
461 case STATIC_STATE:
462 p_slot->state = event == INT_LINK_UP ?
463 POWERON_STATE : POWEROFF_STATE;
464 queue_work(p_slot->wq, &info->work);
465 break;
466 case POWERON_STATE:
467 if (event == INT_LINK_UP) {
468 ctrl_info(ctrl,
469 "Link Up event ignored on slot(%s): already powering on\n",
470 slot_name(p_slot));
471 kfree(info);
472 } else {
473 ctrl_info(ctrl,
474 "Link Down event queued on slot(%s): currently getting powered on\n",
475 slot_name(p_slot));
476 p_slot->state = POWEROFF_STATE;
477 queue_work(p_slot->wq, &info->work);
478 }
479 break;
480 case POWEROFF_STATE:
481 if (event == INT_LINK_UP) {
482 ctrl_info(ctrl,
483 "Link Up event queued on slot(%s): currently getting powered off\n",
484 slot_name(p_slot));
485 p_slot->state = POWERON_STATE;
486 queue_work(p_slot->wq, &info->work);
487 } else {
488 ctrl_info(ctrl,
489 "Link Down event ignored on slot(%s): already powering off\n",
490 slot_name(p_slot));
491 kfree(info);
492 }
493 break;
494 default:
495 ctrl_err(ctrl, "Not a valid state on slot(%s)\n",
496 slot_name(p_slot));
497 kfree(info);
498 break;
499 }
500}
501
418static void interrupt_event_handler(struct work_struct *work) 502static void interrupt_event_handler(struct work_struct *work)
419{ 503{
420 struct event_info *info = container_of(work, struct event_info, work); 504 struct event_info *info = container_of(work, struct event_info, work);
@@ -439,6 +523,10 @@ static void interrupt_event_handler(struct work_struct *work)
439 ctrl_dbg(ctrl, "Surprise Removal\n"); 523 ctrl_dbg(ctrl, "Surprise Removal\n");
440 handle_surprise_event(p_slot); 524 handle_surprise_event(p_slot);
441 break; 525 break;
526 case INT_LINK_UP:
527 case INT_LINK_DOWN:
528 handle_link_event(p_slot, info->event_type);
529 break;
442 default: 530 default:
443 break; 531 break;
444 } 532 }