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 static inline char *slot_name(struct slot
*p_slot
)
48 return p_slot
->hotplug_slot
->name
;
51 u8
pciehp_handle_attention_button(u8 hp_slot
, struct controller
*ctrl
)
56 struct event_info
*taskInfo
;
58 /* Attention Button Change */
59 dbg("pciehp: Attention button interrupt received.\n");
61 /* This is the structure that tells the worker thread what to do */
62 taskInfo
= &(ctrl
->event_queue
[ctrl
->next_event
]);
63 p_slot
= pciehp_find_slot(ctrl
, hp_slot
+ ctrl
->slot_device_offset
);
65 p_slot
->hpc_ops
->get_latch_status(p_slot
, &getstatus
);
67 ctrl
->next_event
= (ctrl
->next_event
+ 1) % MAX_EVENTS
;
68 taskInfo
->hp_slot
= hp_slot
;
73 * Button pressed - See if need to TAKE ACTION!!!
75 info("Button pressed on Slot(%s)\n", slot_name(p_slot
));
76 taskInfo
->event_type
= INT_BUTTON_PRESS
;
78 if ((p_slot
->state
== BLINKINGON_STATE
)
79 || (p_slot
->state
== BLINKINGOFF_STATE
)) {
80 /* Cancel if we are still blinking; this means that we press the
81 * attention again before the 5 sec. limit expires to cancel hot-add
84 taskInfo
->event_type
= INT_BUTTON_CANCEL
;
85 info("Button cancel on Slot(%s)\n", slot_name(p_slot
));
86 } else if ((p_slot
->state
== POWERON_STATE
)
87 || (p_slot
->state
== POWEROFF_STATE
)) {
88 /* Ignore if the slot is on power-on or power-off state; this
89 * means that the previous attention button action to hot-add or
90 * hot-remove is undergoing
92 taskInfo
->event_type
= INT_BUTTON_IGNORE
;
93 info("Button ignore on Slot(%s)\n", slot_name(p_slot
));
97 up(&event_semaphore
); /* signal event thread that new event is posted */
103 u8
pciehp_handle_switch_change(u8 hp_slot
, struct controller
*ctrl
)
108 struct event_info
*taskInfo
;
111 dbg("pciehp: Switch interrupt received.\n");
113 /* This is the structure that tells the worker thread
116 taskInfo
= &(ctrl
->event_queue
[ctrl
->next_event
]);
117 ctrl
->next_event
= (ctrl
->next_event
+ 1) % MAX_EVENTS
;
118 taskInfo
->hp_slot
= hp_slot
;
121 p_slot
= pciehp_find_slot(ctrl
, hp_slot
+ ctrl
->slot_device_offset
);
122 p_slot
->hpc_ops
->get_latch_status(p_slot
, &getstatus
);
128 info("Latch open on Slot(%s)\n", slot_name(p_slot
));
129 taskInfo
->event_type
= INT_SWITCH_OPEN
;
134 info("Latch close on Slot(%s)\n", slot_name(p_slot
));
135 taskInfo
->event_type
= INT_SWITCH_CLOSE
;
139 up(&event_semaphore
); /* signal event thread that new event is posted */
144 u8
pciehp_handle_presence_change(u8 hp_slot
, struct controller
*ctrl
)
147 u8 presence_save
, rc
= 0;
148 struct event_info
*taskInfo
;
150 /* Presence Change */
151 dbg("pciehp: Presence/Notify input change.\n");
153 /* This is the structure that tells the worker thread
156 taskInfo
= &(ctrl
->event_queue
[ctrl
->next_event
]);
157 ctrl
->next_event
= (ctrl
->next_event
+ 1) % MAX_EVENTS
;
158 taskInfo
->hp_slot
= hp_slot
;
161 p_slot
= pciehp_find_slot(ctrl
, hp_slot
+ ctrl
->slot_device_offset
);
163 /* Switch is open, assume a presence change
164 * Save the presence state
166 p_slot
->hpc_ops
->get_adapter_status(p_slot
, &presence_save
);
171 info("Card present on Slot(%s)\n", slot_name(p_slot
));
172 taskInfo
->event_type
= INT_PRESENCE_ON
;
177 info("Card not present on Slot(%s)\n", slot_name(p_slot
));
178 taskInfo
->event_type
= INT_PRESENCE_OFF
;
182 up(&event_semaphore
); /* signal event thread that new event is posted */
187 u8
pciehp_handle_power_fault(u8 hp_slot
, struct controller
*ctrl
)
191 struct event_info
*taskInfo
;
194 dbg("pciehp: Power fault interrupt received.\n");
196 /* this is the structure that tells the worker thread
199 taskInfo
= &(ctrl
->event_queue
[ctrl
->next_event
]);
200 ctrl
->next_event
= (ctrl
->next_event
+ 1) % MAX_EVENTS
;
201 taskInfo
->hp_slot
= hp_slot
;
204 p_slot
= pciehp_find_slot(ctrl
, hp_slot
+ ctrl
->slot_device_offset
);
206 if ( !(p_slot
->hpc_ops
->query_power_fault(p_slot
))) {
208 * power fault Cleared
210 info("Power fault cleared on Slot(%s)\n", slot_name(p_slot
));
211 taskInfo
->event_type
= INT_POWER_FAULT_CLEAR
;
216 info("Power fault on Slot(%s)\n", slot_name(p_slot
));
217 taskInfo
->event_type
= INT_POWER_FAULT
;
218 info("power fault bit %x set\n", hp_slot
);
221 up(&event_semaphore
); /* signal event thread that new event is posted */
226 /* The following routines constitute the bulk of the
227 hotplug controller logic
230 static void set_slot_off(struct controller
*ctrl
, struct slot
* pslot
)
232 /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
233 if (POWER_CTRL(ctrl
->ctrlcap
)) {
234 if (pslot
->hpc_ops
->power_off_slot(pslot
)) {
235 err("%s: Issue of Slot Power Off command failed\n",
241 if (PWR_LED(ctrl
->ctrlcap
))
242 pslot
->hpc_ops
->green_led_off(pslot
);
244 if (ATTN_LED(ctrl
->ctrlcap
)) {
245 if (pslot
->hpc_ops
->set_attention_status(pslot
, 1)) {
246 err("%s: Issue of Set Attention Led command failed\n",
254 * board_added - Called after a board has been added to the system.
256 * Turns power on for the board
260 static int board_added(struct slot
*p_slot
)
264 struct controller
*ctrl
= p_slot
->ctrl
;
266 hp_slot
= p_slot
->device
- ctrl
->slot_device_offset
;
268 dbg("%s: slot device, slot offset, hp slot = %d, %d ,%d\n",
269 __FUNCTION__
, p_slot
->device
,
270 ctrl
->slot_device_offset
, hp_slot
);
272 if (POWER_CTRL(ctrl
->ctrlcap
)) {
274 retval
= p_slot
->hpc_ops
->power_on_slot(p_slot
);
279 if (PWR_LED(ctrl
->ctrlcap
))
280 p_slot
->hpc_ops
->green_led_blink(p_slot
);
282 /* Wait for ~1 second */
285 /* Check link training status */
286 retval
= p_slot
->hpc_ops
->check_lnk_status(ctrl
);
288 err("%s: Failed to check link status\n", __FUNCTION__
);
289 set_slot_off(ctrl
, p_slot
);
293 /* Check for a power fault */
294 if (p_slot
->hpc_ops
->query_power_fault(p_slot
)) {
295 dbg("%s: power fault detected\n", __FUNCTION__
);
296 retval
= POWER_FAILURE
;
300 retval
= pciehp_configure_device(p_slot
);
302 err("Cannot add device 0x%x:%x\n", p_slot
->bus
,
308 * Some PCI Express root ports require fixup after hot-plug operation.
311 pci_fixup_device(pci_fixup_final
, ctrl
->pci_dev
);
312 if (PWR_LED(ctrl
->ctrlcap
))
313 p_slot
->hpc_ops
->green_led_on(p_slot
);
318 set_slot_off(ctrl
, p_slot
);
323 * remove_board - Turns off slot and LED's
326 static int remove_board(struct slot
*p_slot
)
331 struct controller
*ctrl
= p_slot
->ctrl
;
333 retval
= pciehp_unconfigure_device(p_slot
);
337 device
= p_slot
->device
;
338 hp_slot
= p_slot
->device
- ctrl
->slot_device_offset
;
339 p_slot
= pciehp_find_slot(ctrl
, hp_slot
+ ctrl
->slot_device_offset
);
341 dbg("In %s, hp_slot = %d\n", __FUNCTION__
, hp_slot
);
343 if (POWER_CTRL(ctrl
->ctrlcap
)) {
345 retval
= p_slot
->hpc_ops
->power_off_slot(p_slot
);
347 err("%s: Issue of Slot Disable command failed\n",
353 if (PWR_LED(ctrl
->ctrlcap
))
354 /* turn off Green LED */
355 p_slot
->hpc_ops
->green_led_off(p_slot
);
361 static void pushbutton_helper_thread(unsigned long data
)
363 pushbutton_pending
= data
;
365 up(&event_semaphore
);
369 * pciehp_pushbutton_thread
371 * Scheduled procedure to handle blocking stuff for the pushbuttons
372 * Handles all pending events and exits.
375 static void pciehp_pushbutton_thread(unsigned long slot
)
377 struct slot
*p_slot
= (struct slot
*) slot
;
380 pushbutton_pending
= 0;
383 dbg("%s: Error! slot NULL\n", __FUNCTION__
);
387 p_slot
->hpc_ops
->get_power_status(p_slot
, &getstatus
);
389 p_slot
->state
= POWEROFF_STATE
;
390 dbg("%s: disabling bus:device(%x:%x)\n", __FUNCTION__
,
391 p_slot
->bus
, p_slot
->device
);
393 pciehp_disable_slot(p_slot
);
394 p_slot
->state
= STATIC_STATE
;
396 p_slot
->state
= POWERON_STATE
;
397 dbg("%s: adding bus:device(%x:%x)\n", __FUNCTION__
,
398 p_slot
->bus
, p_slot
->device
);
400 if (pciehp_enable_slot(p_slot
) &&
401 PWR_LED(p_slot
->ctrl
->ctrlcap
))
402 p_slot
->hpc_ops
->green_led_off(p_slot
);
404 p_slot
->state
= STATIC_STATE
;
411 * pciehp_surprise_rm_thread
413 * Scheduled procedure to handle blocking stuff for the surprise removal
414 * Handles all pending events and exits.
417 static void pciehp_surprise_rm_thread(unsigned long slot
)
419 struct slot
*p_slot
= (struct slot
*) slot
;
422 surprise_rm_pending
= 0;
425 dbg("%s: Error! slot NULL\n", __FUNCTION__
);
429 p_slot
->hpc_ops
->get_adapter_status(p_slot
, &getstatus
);
431 p_slot
->state
= POWEROFF_STATE
;
432 dbg("%s: removing bus:device(%x:%x)\n",
433 __FUNCTION__
, p_slot
->bus
, p_slot
->device
);
435 pciehp_disable_slot(p_slot
);
436 p_slot
->state
= STATIC_STATE
;
438 p_slot
->state
= POWERON_STATE
;
439 dbg("%s: adding bus:device(%x:%x)\n",
440 __FUNCTION__
, p_slot
->bus
, p_slot
->device
);
442 if (pciehp_enable_slot(p_slot
) &&
443 PWR_LED(p_slot
->ctrl
->ctrlcap
))
444 p_slot
->hpc_ops
->green_led_off(p_slot
);
446 p_slot
->state
= STATIC_STATE
;
454 /* this is the main worker thread */
455 static int event_thread(void* data
)
457 struct controller
*ctrl
;
459 daemonize("pciehpd_event");
464 dbg("!!!!event_thread sleeping\n");
465 down_interruptible (&event_semaphore
);
466 dbg("event_thread woken finished = %d\n", event_finished
);
467 if (event_finished
|| signal_pending(current
))
470 if (pushbutton_pending
)
471 pciehp_pushbutton_thread(pushbutton_pending
);
472 else if (surprise_rm_pending
)
473 pciehp_surprise_rm_thread(surprise_rm_pending
);
475 for (ctrl
= pciehp_ctrl_list
; ctrl
; ctrl
=ctrl
->next
)
476 interrupt_event_handler(ctrl
);
478 dbg("event_thread signals exit\n");
483 int pciehp_event_start_thread(void)
487 /* initialize our semaphores */
488 init_MUTEX_LOCKED(&event_exit
);
491 init_MUTEX_LOCKED(&event_semaphore
);
492 pid
= kernel_thread(event_thread
, NULL
, 0);
495 err ("Can't start up our event thread\n");
502 void pciehp_event_stop_thread(void)
505 up(&event_semaphore
);
510 static int update_slot_info(struct slot
*slot
)
512 struct hotplug_slot_info
*info
;
513 /* char buffer[SLOT_NAME_SIZE]; */
516 info
= kmalloc(sizeof(struct hotplug_slot_info
), GFP_KERNEL
);
520 /* make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); */
522 slot
->hpc_ops
->get_power_status(slot
, &(info
->power_status
));
523 slot
->hpc_ops
->get_attention_status(slot
, &(info
->attention_status
));
524 slot
->hpc_ops
->get_latch_status(slot
, &(info
->latch_status
));
525 slot
->hpc_ops
->get_adapter_status(slot
, &(info
->adapter_status
));
527 /* result = pci_hp_change_slot_info(buffer, info); */
528 result
= pci_hp_change_slot_info(slot
->hotplug_slot
, info
);
533 static void interrupt_event_handler(struct controller
*ctrl
)
544 for (loop
= 0; loop
< MAX_EVENTS
; loop
++) {
545 if (ctrl
->event_queue
[loop
].event_type
!= 0) {
546 hp_slot
= ctrl
->event_queue
[loop
].hp_slot
;
548 p_slot
= pciehp_find_slot(ctrl
, hp_slot
+ ctrl
->slot_device_offset
);
550 if (ctrl
->event_queue
[loop
].event_type
== INT_BUTTON_CANCEL
) {
551 dbg("button cancel\n");
552 del_timer(&p_slot
->task_event
);
554 switch (p_slot
->state
) {
555 case BLINKINGOFF_STATE
:
556 if (PWR_LED(ctrl
->ctrlcap
))
557 p_slot
->hpc_ops
->green_led_on(p_slot
);
559 if (ATTN_LED(ctrl
->ctrlcap
))
560 p_slot
->hpc_ops
->set_attention_status(p_slot
, 0);
562 case BLINKINGON_STATE
:
563 if (PWR_LED(ctrl
->ctrlcap
))
564 p_slot
->hpc_ops
->green_led_off(p_slot
);
566 if (ATTN_LED(ctrl
->ctrlcap
))
567 p_slot
->hpc_ops
->set_attention_status(p_slot
, 0);
570 warn("Not a valid state\n");
573 info("PCI slot #%s - action canceled due to button press.\n", slot_name(p_slot
));
574 p_slot
->state
= STATIC_STATE
;
576 /* ***********Button Pressed (No action on 1st press...) */
577 else if (ctrl
->event_queue
[loop
].event_type
== INT_BUTTON_PRESS
) {
579 if (ATTN_BUTTN(ctrl
->ctrlcap
)) {
580 dbg("Button pressed\n");
581 p_slot
->hpc_ops
->get_power_status(p_slot
, &getstatus
);
585 p_slot
->state
= BLINKINGOFF_STATE
;
586 info("PCI slot #%s - powering off due to button press.\n", slot_name(p_slot
));
589 dbg("slot is off\n");
590 p_slot
->state
= BLINKINGON_STATE
;
591 info("PCI slot #%s - powering on due to button press.\n", slot_name(p_slot
));
594 /* blink green LED and turn off amber */
595 if (PWR_LED(ctrl
->ctrlcap
))
596 p_slot
->hpc_ops
->green_led_blink(p_slot
);
598 if (ATTN_LED(ctrl
->ctrlcap
))
599 p_slot
->hpc_ops
->set_attention_status(p_slot
, 0);
601 init_timer(&p_slot
->task_event
);
602 p_slot
->task_event
.expires
= jiffies
+ 5 * HZ
; /* 5 second delay */
603 p_slot
->task_event
.function
= (void (*)(unsigned long)) pushbutton_helper_thread
;
604 p_slot
->task_event
.data
= (unsigned long) p_slot
;
606 add_timer(&p_slot
->task_event
);
609 /***********POWER FAULT********************/
610 else if (ctrl
->event_queue
[loop
].event_type
== INT_POWER_FAULT
) {
611 if (POWER_CTRL(ctrl
->ctrlcap
)) {
612 dbg("power fault\n");
613 if (ATTN_LED(ctrl
->ctrlcap
))
614 p_slot
->hpc_ops
->set_attention_status(p_slot
, 1);
616 if (PWR_LED(ctrl
->ctrlcap
))
617 p_slot
->hpc_ops
->green_led_off(p_slot
);
620 /***********SURPRISE REMOVAL********************/
621 else if ((ctrl
->event_queue
[loop
].event_type
== INT_PRESENCE_ON
) ||
622 (ctrl
->event_queue
[loop
].event_type
== INT_PRESENCE_OFF
)) {
623 if (HP_SUPR_RM(ctrl
->ctrlcap
)) {
624 dbg("Surprise Removal\n");
626 surprise_rm_pending
= (unsigned long) p_slot
;
627 up(&event_semaphore
);
628 update_slot_info(p_slot
);
632 /* refresh notification */
634 update_slot_info(p_slot
);
637 ctrl
->event_queue
[loop
].event_type
= 0;
641 } /* End of FOR loop */
645 int pciehp_enable_slot(struct slot
*p_slot
)
650 /* Check to see if (latch closed, card present, power off) */
651 mutex_lock(&p_slot
->ctrl
->crit_sect
);
653 rc
= p_slot
->hpc_ops
->get_adapter_status(p_slot
, &getstatus
);
654 if (rc
|| !getstatus
) {
655 info("%s: no adapter on slot(%s)\n", __FUNCTION__
,
657 mutex_unlock(&p_slot
->ctrl
->crit_sect
);
660 if (MRL_SENS(p_slot
->ctrl
->ctrlcap
)) {
661 rc
= p_slot
->hpc_ops
->get_latch_status(p_slot
, &getstatus
);
662 if (rc
|| getstatus
) {
663 info("%s: latch open on slot(%s)\n", __FUNCTION__
,
665 mutex_unlock(&p_slot
->ctrl
->crit_sect
);
670 if (POWER_CTRL(p_slot
->ctrl
->ctrlcap
)) {
671 rc
= p_slot
->hpc_ops
->get_power_status(p_slot
, &getstatus
);
672 if (rc
|| getstatus
) {
673 info("%s: already enabled on slot(%s)\n", __FUNCTION__
,
675 mutex_unlock(&p_slot
->ctrl
->crit_sect
);
680 p_slot
->hpc_ops
->get_latch_status(p_slot
, &getstatus
);
682 rc
= board_added(p_slot
);
684 p_slot
->hpc_ops
->get_latch_status(p_slot
, &getstatus
);
687 update_slot_info(p_slot
);
689 mutex_unlock(&p_slot
->ctrl
->crit_sect
);
694 int pciehp_disable_slot(struct slot
*p_slot
)
702 /* Check to see if (latch closed, card present, power on) */
703 mutex_lock(&p_slot
->ctrl
->crit_sect
);
705 if (!HP_SUPR_RM(p_slot
->ctrl
->ctrlcap
)) {
706 ret
= p_slot
->hpc_ops
->get_adapter_status(p_slot
, &getstatus
);
707 if (ret
|| !getstatus
) {
708 info("%s: no adapter on slot(%s)\n", __FUNCTION__
,
710 mutex_unlock(&p_slot
->ctrl
->crit_sect
);
715 if (MRL_SENS(p_slot
->ctrl
->ctrlcap
)) {
716 ret
= p_slot
->hpc_ops
->get_latch_status(p_slot
, &getstatus
);
717 if (ret
|| getstatus
) {
718 info("%s: latch open on slot(%s)\n", __FUNCTION__
,
720 mutex_unlock(&p_slot
->ctrl
->crit_sect
);
725 if (POWER_CTRL(p_slot
->ctrl
->ctrlcap
)) {
726 ret
= p_slot
->hpc_ops
->get_power_status(p_slot
, &getstatus
);
727 if (ret
|| !getstatus
) {
728 info("%s: already disabled slot(%s)\n", __FUNCTION__
,
730 mutex_unlock(&p_slot
->ctrl
->crit_sect
);
735 ret
= remove_board(p_slot
);
736 update_slot_info(p_slot
);
738 mutex_unlock(&p_slot
->ctrl
->crit_sect
);