2 * PCI Express Hot Plug Controller Driver
4 * Copyright (C) 1995,2001 Compaq Computer Corporation
5 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
6 * Copyright (C) 2001 IBM Corp.
7 * Copyright (C) 2003-2004 Intel Corporation
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or (at
14 * your option) any later version.
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
19 * NON INFRINGEMENT. See the GNU General Public License for more
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <linux/types.h>
33 #include <linux/smp_lock.h>
34 #include <linux/pci.h>
38 static void interrupt_event_handler(struct controller *ctrl);
40 static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
41 static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */
42 static int event_finished;
43 static unsigned long pushbutton_pending; /* = 0 */
44 static unsigned long surprise_rm_pending; /* = 0 */
46 u8 pciehp_handle_attention_button(u8 hp_slot, void *inst_id)
48 struct controller *ctrl = (struct controller *) inst_id;
52 struct event_info *taskInfo;
54 /* Attention Button Change */
55 dbg("pciehp: Attention button interrupt received.\n");
57 /* This is the structure that tells the worker thread what to do */
58 taskInfo = &(ctrl->event_queue[ctrl->next_event]);
59 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
61 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
63 ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS;
64 taskInfo->hp_slot = hp_slot;
69 * Button pressed - See if need to TAKE ACTION!!!
71 info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot);
72 taskInfo->event_type = INT_BUTTON_PRESS;
74 if ((p_slot->state == BLINKINGON_STATE)
75 || (p_slot->state == BLINKINGOFF_STATE)) {
76 /* Cancel if we are still blinking; this means that we press the
77 * attention again before the 5 sec. limit expires to cancel hot-add
80 taskInfo->event_type = INT_BUTTON_CANCEL;
81 info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot);
82 } else if ((p_slot->state == POWERON_STATE)
83 || (p_slot->state == POWEROFF_STATE)) {
84 /* Ignore if the slot is on power-on or power-off state; this
85 * means that the previous attention button action to hot-add or
86 * hot-remove is undergoing
88 taskInfo->event_type = INT_BUTTON_IGNORE;
89 info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot);
93 up(&event_semaphore); /* signal event thread that new event is posted */
99 u8 pciehp_handle_switch_change(u8 hp_slot, void *inst_id)
101 struct controller *ctrl = (struct controller *) inst_id;
105 struct event_info *taskInfo;
108 dbg("pciehp: Switch interrupt received.\n");
110 /* This is the structure that tells the worker thread
113 taskInfo = &(ctrl->event_queue[ctrl->next_event]);
114 ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS;
115 taskInfo->hp_slot = hp_slot;
118 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
119 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
125 info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot);
126 taskInfo->event_type = INT_SWITCH_OPEN;
131 info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot);
132 taskInfo->event_type = INT_SWITCH_CLOSE;
136 up(&event_semaphore); /* signal event thread that new event is posted */
141 u8 pciehp_handle_presence_change(u8 hp_slot, void *inst_id)
143 struct controller *ctrl = (struct controller *) inst_id;
145 u8 presence_save, rc = 0;
146 struct event_info *taskInfo;
148 /* Presence Change */
149 dbg("pciehp: Presence/Notify input change.\n");
151 /* This is the structure that tells the worker thread
154 taskInfo = &(ctrl->event_queue[ctrl->next_event]);
155 ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS;
156 taskInfo->hp_slot = hp_slot;
159 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
161 /* Switch is open, assume a presence change
162 * Save the presence state
164 p_slot->hpc_ops->get_adapter_status(p_slot, &presence_save);
169 info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot);
170 taskInfo->event_type = INT_PRESENCE_ON;
175 info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot);
176 taskInfo->event_type = INT_PRESENCE_OFF;
180 up(&event_semaphore); /* signal event thread that new event is posted */
185 u8 pciehp_handle_power_fault(u8 hp_slot, void *inst_id)
187 struct controller *ctrl = (struct controller *) inst_id;
190 struct event_info *taskInfo;
193 dbg("pciehp: Power fault interrupt received.\n");
195 /* this is the structure that tells the worker thread
198 taskInfo = &(ctrl->event_queue[ctrl->next_event]);
199 ctrl->next_event = (ctrl->next_event + 1) % MAX_EVENTS;
200 taskInfo->hp_slot = hp_slot;
203 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
205 if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
207 * power fault Cleared
209 info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot);
210 p_slot->status = 0x00;
211 taskInfo->event_type = INT_POWER_FAULT_CLEAR;
216 info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot);
217 taskInfo->event_type = INT_POWER_FAULT;
218 /* set power fault status for this board */
219 p_slot->status = 0xFF;
220 info("power fault bit %x set\n", hp_slot);
223 up(&event_semaphore); /* signal event thread that new event is posted */
228 /* The following routines constitute the bulk of the
229 hotplug controller logic
232 static void set_slot_off(struct controller *ctrl, struct slot * pslot)
234 /* Wait for exclusive access to hardware */
235 down(&ctrl->crit_sect);
237 /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
238 if (POWER_CTRL(ctrl->ctrlcap)) {
239 if (pslot->hpc_ops->power_off_slot(pslot)) {
240 err("%s: Issue of Slot Power Off command failed\n", __FUNCTION__);
241 up(&ctrl->crit_sect);
244 wait_for_ctrl_irq (ctrl);
247 if (PWR_LED(ctrl->ctrlcap)) {
248 pslot->hpc_ops->green_led_off(pslot);
249 wait_for_ctrl_irq (ctrl);
252 if (ATTN_LED(ctrl->ctrlcap)) {
253 if (pslot->hpc_ops->set_attention_status(pslot, 1)) {
254 err("%s: Issue of Set Attention Led command failed\n", __FUNCTION__);
255 up(&ctrl->crit_sect);
258 wait_for_ctrl_irq (ctrl);
261 /* Done with exclusive hardware access */
262 up(&ctrl->crit_sect);
266 * board_added - Called after a board has been added to the system.
268 * Turns power on for the board
272 static int board_added(struct slot *p_slot)
276 struct controller *ctrl = p_slot->ctrl;
278 hp_slot = p_slot->device - ctrl->slot_device_offset;
280 dbg("%s: slot device, slot offset, hp slot = %d, %d ,%d\n",
281 __FUNCTION__, p_slot->device,
282 ctrl->slot_device_offset, hp_slot);
284 /* Wait for exclusive access to hardware */
285 down(&ctrl->crit_sect);
287 if (POWER_CTRL(ctrl->ctrlcap)) {
289 rc = p_slot->hpc_ops->power_on_slot(p_slot);
291 up(&ctrl->crit_sect);
295 /* Wait for the command to complete */
296 wait_for_ctrl_irq (ctrl);
299 if (PWR_LED(ctrl->ctrlcap)) {
300 p_slot->hpc_ops->green_led_blink(p_slot);
302 /* Wait for the command to complete */
303 wait_for_ctrl_irq (ctrl);
306 /* Done with exclusive hardware access */
307 up(&ctrl->crit_sect);
309 /* Wait for ~1 second */
310 wait_for_ctrl_irq (ctrl);
312 /* Check link training status */
313 rc = p_slot->hpc_ops->check_lnk_status(ctrl);
315 err("%s: Failed to check link status\n", __FUNCTION__);
316 set_slot_off(ctrl, p_slot);
320 dbg("%s: slot status = %x\n", __FUNCTION__, p_slot->status);
322 /* Check for a power fault */
323 if (p_slot->status == 0xFF) {
324 /* power fault occurred, but it was benign */
330 rc = pciehp_configure_device(p_slot);
332 err("Cannot add device 0x%x:%x\n", p_slot->bus,
340 * Some PCI Express root ports require fixup after hot-plug operation.
343 pci_fixup_device(pci_fixup_final, ctrl->pci_dev);
344 if (PWR_LED(ctrl->ctrlcap)) {
345 /* Wait for exclusive access to hardware */
346 down(&ctrl->crit_sect);
348 p_slot->hpc_ops->green_led_on(p_slot);
350 /* Wait for the command to complete */
351 wait_for_ctrl_irq (ctrl);
353 /* Done with exclusive hardware access */
354 up(&ctrl->crit_sect);
359 set_slot_off(ctrl, p_slot);
365 * remove_board - Turns off slot and LED's
368 static int remove_board(struct slot *p_slot)
373 struct controller *ctrl = p_slot->ctrl;
375 if (pciehp_unconfigure_device(p_slot))
378 device = p_slot->device;
380 hp_slot = p_slot->device - ctrl->slot_device_offset;
381 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
383 dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
385 /* Change status to shutdown */
386 p_slot->status = 0x01;
388 /* Wait for exclusive access to hardware */
389 down(&ctrl->crit_sect);
391 if (POWER_CTRL(ctrl->ctrlcap)) {
393 rc = p_slot->hpc_ops->power_off_slot(p_slot);
395 err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
396 up(&ctrl->crit_sect);
399 /* Wait for the command to complete */
400 wait_for_ctrl_irq (ctrl);
403 if (PWR_LED(ctrl->ctrlcap)) {
404 /* turn off Green LED */
405 p_slot->hpc_ops->green_led_off(p_slot);
407 /* Wait for the command to complete */
408 wait_for_ctrl_irq (ctrl);
411 /* Done with exclusive hardware access */
412 up(&ctrl->crit_sect);
418 static void pushbutton_helper_thread(unsigned long data)
420 pushbutton_pending = data;
422 up(&event_semaphore);
426 * pciehp_pushbutton_thread
428 * Scheduled procedure to handle blocking stuff for the pushbuttons
429 * Handles all pending events and exits.
432 static void pciehp_pushbutton_thread(unsigned long slot)
434 struct slot *p_slot = (struct slot *) slot;
437 pushbutton_pending = 0;
440 dbg("%s: Error! slot NULL\n", __FUNCTION__);
444 p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
446 p_slot->state = POWEROFF_STATE;
447 dbg("%s: disabling bus:device(%x:%x)\n", __FUNCTION__,
448 p_slot->bus, p_slot->device);
450 pciehp_disable_slot(p_slot);
451 p_slot->state = STATIC_STATE;
453 p_slot->state = POWERON_STATE;
454 dbg("%s: adding bus:device(%x:%x)\n", __FUNCTION__,
455 p_slot->bus, p_slot->device);
457 if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) {
458 /* Wait for exclusive access to hardware */
459 down(&p_slot->ctrl->crit_sect);
461 p_slot->hpc_ops->green_led_off(p_slot);
463 /* Wait for the command to complete */
464 wait_for_ctrl_irq (p_slot->ctrl);
466 /* Done with exclusive hardware access */
467 up(&p_slot->ctrl->crit_sect);
469 p_slot->state = STATIC_STATE;
476 * pciehp_surprise_rm_thread
478 * Scheduled procedure to handle blocking stuff for the surprise removal
479 * Handles all pending events and exits.
482 static void pciehp_surprise_rm_thread(unsigned long slot)
484 struct slot *p_slot = (struct slot *) slot;
487 surprise_rm_pending = 0;
490 dbg("%s: Error! slot NULL\n", __FUNCTION__);
494 p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
496 p_slot->state = POWEROFF_STATE;
497 dbg("%s: removing bus:device(%x:%x)\n",
498 __FUNCTION__, p_slot->bus, p_slot->device);
500 pciehp_disable_slot(p_slot);
501 p_slot->state = STATIC_STATE;
503 p_slot->state = POWERON_STATE;
504 dbg("%s: adding bus:device(%x:%x)\n",
505 __FUNCTION__, p_slot->bus, p_slot->device);
507 if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl->ctrlcap)) {
508 /* Wait for exclusive access to hardware */
509 down(&p_slot->ctrl->crit_sect);
511 p_slot->hpc_ops->green_led_off(p_slot);
513 /* Wait for the command to complete */
514 wait_for_ctrl_irq (p_slot->ctrl);
516 /* Done with exclusive hardware access */
517 up(&p_slot->ctrl->crit_sect);
519 p_slot->state = STATIC_STATE;
527 /* this is the main worker thread */
528 static int event_thread(void* data)
530 struct controller *ctrl;
532 daemonize("pciehpd_event");
537 dbg("!!!!event_thread sleeping\n");
538 down_interruptible (&event_semaphore);
539 dbg("event_thread woken finished = %d\n", event_finished);
540 if (event_finished || signal_pending(current))
543 if (pushbutton_pending)
544 pciehp_pushbutton_thread(pushbutton_pending);
545 else if (surprise_rm_pending)
546 pciehp_surprise_rm_thread(surprise_rm_pending);
548 for (ctrl = pciehp_ctrl_list; ctrl; ctrl=ctrl->next)
549 interrupt_event_handler(ctrl);
551 dbg("event_thread signals exit\n");
556 int pciehp_event_start_thread(void)
560 /* initialize our semaphores */
561 init_MUTEX_LOCKED(&event_exit);
564 init_MUTEX_LOCKED(&event_semaphore);
565 pid = kernel_thread(event_thread, NULL, 0);
568 err ("Can't start up our event thread\n");
575 void pciehp_event_stop_thread(void)
578 up(&event_semaphore);
583 static int update_slot_info(struct slot *slot)
585 struct hotplug_slot_info *info;
586 /* char buffer[SLOT_NAME_SIZE]; */
589 info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
593 /* make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); */
595 slot->hpc_ops->get_power_status(slot, &(info->power_status));
596 slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
597 slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
598 slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
600 /* result = pci_hp_change_slot_info(buffer, info); */
601 result = pci_hp_change_slot_info(slot->hotplug_slot, info);
606 static void interrupt_event_handler(struct controller *ctrl)
617 for (loop = 0; loop < MAX_EVENTS; loop++) {
618 if (ctrl->event_queue[loop].event_type != 0) {
619 hp_slot = ctrl->event_queue[loop].hp_slot;
621 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
623 if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) {
624 dbg("button cancel\n");
625 del_timer(&p_slot->task_event);
627 switch (p_slot->state) {
628 case BLINKINGOFF_STATE:
629 /* Wait for exclusive access to hardware */
630 down(&ctrl->crit_sect);
632 if (PWR_LED(ctrl->ctrlcap)) {
633 p_slot->hpc_ops->green_led_on(p_slot);
634 /* Wait for the command to complete */
635 wait_for_ctrl_irq (ctrl);
637 if (ATTN_LED(ctrl->ctrlcap)) {
638 p_slot->hpc_ops->set_attention_status(p_slot, 0);
640 /* Wait for the command to complete */
641 wait_for_ctrl_irq (ctrl);
643 /* Done with exclusive hardware access */
644 up(&ctrl->crit_sect);
646 case BLINKINGON_STATE:
647 /* Wait for exclusive access to hardware */
648 down(&ctrl->crit_sect);
650 if (PWR_LED(ctrl->ctrlcap)) {
651 p_slot->hpc_ops->green_led_off(p_slot);
652 /* Wait for the command to complete */
653 wait_for_ctrl_irq (ctrl);
655 if (ATTN_LED(ctrl->ctrlcap)){
656 p_slot->hpc_ops->set_attention_status(p_slot, 0);
657 /* Wait for the command to complete */
658 wait_for_ctrl_irq (ctrl);
660 /* Done with exclusive hardware access */
661 up(&ctrl->crit_sect);
665 warn("Not a valid state\n");
668 info(msg_button_cancel, p_slot->number);
669 p_slot->state = STATIC_STATE;
671 /* ***********Button Pressed (No action on 1st press...) */
672 else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
674 if (ATTN_BUTTN(ctrl->ctrlcap)) {
675 dbg("Button pressed\n");
676 p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
680 p_slot->state = BLINKINGOFF_STATE;
681 info(msg_button_off, p_slot->number);
684 dbg("slot is off\n");
685 p_slot->state = BLINKINGON_STATE;
686 info(msg_button_on, p_slot->number);
689 /* Wait for exclusive access to hardware */
690 down(&ctrl->crit_sect);
692 /* blink green LED and turn off amber */
693 if (PWR_LED(ctrl->ctrlcap)) {
694 p_slot->hpc_ops->green_led_blink(p_slot);
695 /* Wait for the command to complete */
696 wait_for_ctrl_irq (ctrl);
699 if (ATTN_LED(ctrl->ctrlcap)) {
700 p_slot->hpc_ops->set_attention_status(p_slot, 0);
702 /* Wait for the command to complete */
703 wait_for_ctrl_irq (ctrl);
706 /* Done with exclusive hardware access */
707 up(&ctrl->crit_sect);
709 init_timer(&p_slot->task_event);
710 p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */
711 p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread;
712 p_slot->task_event.data = (unsigned long) p_slot;
714 add_timer(&p_slot->task_event);
717 /***********POWER FAULT********************/
718 else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
719 if (POWER_CTRL(ctrl->ctrlcap)) {
720 dbg("power fault\n");
721 /* Wait for exclusive access to hardware */
722 down(&ctrl->crit_sect);
724 if (ATTN_LED(ctrl->ctrlcap)) {
725 p_slot->hpc_ops->set_attention_status(p_slot, 1);
726 wait_for_ctrl_irq (ctrl);
729 if (PWR_LED(ctrl->ctrlcap)) {
730 p_slot->hpc_ops->green_led_off(p_slot);
731 wait_for_ctrl_irq (ctrl);
734 /* Done with exclusive hardware access */
735 up(&ctrl->crit_sect);
738 /***********SURPRISE REMOVAL********************/
739 else if ((ctrl->event_queue[loop].event_type == INT_PRESENCE_ON) ||
740 (ctrl->event_queue[loop].event_type == INT_PRESENCE_OFF)) {
741 if (HP_SUPR_RM(ctrl->ctrlcap)) {
742 dbg("Surprise Removal\n");
744 surprise_rm_pending = (unsigned long) p_slot;
745 up(&event_semaphore);
746 update_slot_info(p_slot);
750 /* refresh notification */
752 update_slot_info(p_slot);
755 ctrl->event_queue[loop].event_type = 0;
759 } /* End of FOR loop */
764 int pciehp_enable_slot(struct slot *p_slot)
769 /* Check to see if (latch closed, card present, power off) */
770 down(&p_slot->ctrl->crit_sect);
772 rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
773 if (rc || !getstatus) {
774 info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
775 up(&p_slot->ctrl->crit_sect);
778 if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
779 rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
780 if (rc || getstatus) {
781 info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
782 up(&p_slot->ctrl->crit_sect);
787 if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
788 rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
789 if (rc || getstatus) {
790 info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number);
791 up(&p_slot->ctrl->crit_sect);
795 up(&p_slot->ctrl->crit_sect);
797 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
799 rc = board_added(p_slot);
801 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
805 update_slot_info(p_slot);
811 int pciehp_disable_slot(struct slot *p_slot)
819 /* Check to see if (latch closed, card present, power on) */
820 down(&p_slot->ctrl->crit_sect);
822 if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) {
823 ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
824 if (ret || !getstatus) {
825 info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
826 up(&p_slot->ctrl->crit_sect);
831 if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
832 ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
833 if (ret || getstatus) {
834 info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
835 up(&p_slot->ctrl->crit_sect);
840 if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
841 ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
842 if (ret || !getstatus) {
843 info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number);
844 up(&p_slot->ctrl->crit_sect);
849 up(&p_slot->ctrl->crit_sect);
851 ret = remove_board(p_slot);
852 update_slot_info(p_slot);