pciehp_ctrl.c 16.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * PCI Express Hot Plug Controller Driver
 *
 * Copyright (C) 1995,2001 Compaq Computer Corporation
 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (C) 2001 IBM Corp.
 * Copyright (C) 2003-2004 Intel Corporation
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 * NON INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
26
 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
L
Linus Torvalds 已提交
27 28 29 30 31 32
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
33
#include <linux/slab.h>
L
Linus Torvalds 已提交
34 35 36 37
#include <linux/pci.h>
#include "../pci.h"
#include "pciehp.h"

K
Kenji Kaneshige 已提交
38
static void interrupt_event_handler(struct work_struct *work);
L
Linus Torvalds 已提交
39

K
Kenji Kaneshige 已提交
40
static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
41
{
K
Kenji Kaneshige 已提交
42 43 44 45 46 47 48 49 50 51
	struct event_info *info;

	info = kmalloc(sizeof(*info), GFP_ATOMIC);
	if (!info)
		return -ENOMEM;

	info->event_type = event_type;
	info->p_slot = p_slot;
	INIT_WORK(&info->work, interrupt_event_handler);

52
	queue_work(p_slot->wq, &info->work);
K
Kenji Kaneshige 已提交
53 54

	return 0;
55 56
}

57
u8 pciehp_handle_attention_button(struct slot *p_slot)
L
Linus Torvalds 已提交
58
{
K
Kenji Kaneshige 已提交
59
	u32 event_type;
60
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
61 62

	/* Attention Button Change */
63
	ctrl_dbg(ctrl, "Attention button interrupt received\n");
L
Linus Torvalds 已提交
64 65 66 67

	/*
	 *  Button pressed - See if need to TAKE ACTION!!!
	 */
A
Alex Chiang 已提交
68
	ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot));
K
Kenji Kaneshige 已提交
69
	event_type = INT_BUTTON_PRESS;
L
Linus Torvalds 已提交
70

K
Kenji Kaneshige 已提交
71
	queue_interrupt_event(p_slot, event_type);
L
Linus Torvalds 已提交
72 73 74 75

	return 0;
}

76
u8 pciehp_handle_switch_change(struct slot *p_slot)
L
Linus Torvalds 已提交
77 78
{
	u8 getstatus;
K
Kenji Kaneshige 已提交
79
	u32 event_type;
80
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
81 82

	/* Switch Change */
83
	ctrl_dbg(ctrl, "Switch interrupt received\n");
L
Linus Torvalds 已提交
84

K
Kenji Kaneshige 已提交
85
	pciehp_get_latch_status(p_slot, &getstatus);
L
Linus Torvalds 已提交
86 87 88 89
	if (getstatus) {
		/*
		 * Switch opened
		 */
A
Alex Chiang 已提交
90
		ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot));
K
Kenji Kaneshige 已提交
91
		event_type = INT_SWITCH_OPEN;
L
Linus Torvalds 已提交
92 93 94 95
	} else {
		/*
		 *  Switch closed
		 */
A
Alex Chiang 已提交
96
		ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot));
K
Kenji Kaneshige 已提交
97
		event_type = INT_SWITCH_CLOSE;
L
Linus Torvalds 已提交
98 99
	}

K
Kenji Kaneshige 已提交
100
	queue_interrupt_event(p_slot, event_type);
L
Linus Torvalds 已提交
101

K
Kenji Kaneshige 已提交
102
	return 1;
L
Linus Torvalds 已提交
103 104
}

105
u8 pciehp_handle_presence_change(struct slot *p_slot)
L
Linus Torvalds 已提交
106
{
K
Kenji Kaneshige 已提交
107 108
	u32 event_type;
	u8 presence_save;
109
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
110 111

	/* Presence Change */
112
	ctrl_dbg(ctrl, "Presence/Notify input change\n");
L
Linus Torvalds 已提交
113 114 115 116

	/* Switch is open, assume a presence change
	 * Save the presence state
	 */
K
Kenji Kaneshige 已提交
117
	pciehp_get_adapter_status(p_slot, &presence_save);
118
	if (presence_save) {
L
Linus Torvalds 已提交
119 120 121
		/*
		 * Card Present
		 */
A
Alex Chiang 已提交
122
		ctrl_info(ctrl, "Card present on Slot(%s)\n", slot_name(p_slot));
K
Kenji Kaneshige 已提交
123
		event_type = INT_PRESENCE_ON;
L
Linus Torvalds 已提交
124 125 126 127
	} else {
		/*
		 * Not Present
		 */
A
Alex Chiang 已提交
128 129
		ctrl_info(ctrl, "Card not present on Slot(%s)\n",
			  slot_name(p_slot));
K
Kenji Kaneshige 已提交
130
		event_type = INT_PRESENCE_OFF;
L
Linus Torvalds 已提交
131 132
	}

K
Kenji Kaneshige 已提交
133
	queue_interrupt_event(p_slot, event_type);
L
Linus Torvalds 已提交
134

K
Kenji Kaneshige 已提交
135
	return 1;
L
Linus Torvalds 已提交
136 137
}

138
u8 pciehp_handle_power_fault(struct slot *p_slot)
L
Linus Torvalds 已提交
139
{
K
Kenji Kaneshige 已提交
140
	u32 event_type;
141
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
142 143

	/* power fault */
144
	ctrl_dbg(ctrl, "Power fault interrupt received\n");
145 146 147
	ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
	event_type = INT_POWER_FAULT;
	ctrl_info(ctrl, "Power fault bit %x set\n", 0);
K
Kenji Kaneshige 已提交
148 149 150
	queue_interrupt_event(p_slot, event_type);

	return 1;
L
Linus Torvalds 已提交
151 152
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
void pciehp_handle_linkstate_change(struct slot *p_slot)
{
	u32 event_type;
	struct controller *ctrl = p_slot->ctrl;

	/* Link Status Change */
	ctrl_dbg(ctrl, "Data Link Layer State change\n");

	if (pciehp_check_link_active(ctrl)) {
		ctrl_info(ctrl, "slot(%s): Link Up event\n",
			  slot_name(p_slot));
		event_type = INT_LINK_UP;
	} else {
		ctrl_info(ctrl, "slot(%s): Link Down event\n",
			  slot_name(p_slot));
		event_type = INT_LINK_DOWN;
	}

	queue_interrupt_event(p_slot, event_type);
}

174
/* The following routines constitute the bulk of the
L
Linus Torvalds 已提交
175 176 177
   hotplug controller logic
 */

R
Ryan Desfosses 已提交
178
static void set_slot_off(struct controller *ctrl, struct slot *pslot)
L
Linus Torvalds 已提交
179 180
{
	/* turn off slot, turn on Amber LED, turn off Green LED if supported*/
181
	if (POWER_CTRL(ctrl)) {
182 183
		pciehp_power_off_slot(pslot);

184 185 186 187 188 189
		/*
		 * After turning power off, we must wait for at least 1 second
		 * before taking any action that relies on power having been
		 * removed from the slot/adapter.
		 */
		msleep(1000);
L
Linus Torvalds 已提交
190 191
	}

192 193
	pciehp_green_led_off(pslot);
	pciehp_set_attention_status(pslot, 1);
L
Linus Torvalds 已提交
194 195 196 197
}

/**
 * board_added - Called after a board has been added to the system.
R
Randy Dunlap 已提交
198
 * @p_slot: &slot where board is added
L
Linus Torvalds 已提交
199
 *
R
Randy Dunlap 已提交
200 201
 * Turns power on for the board.
 * Configures board.
L
Linus Torvalds 已提交
202
 */
203
static int board_added(struct slot *p_slot)
L
Linus Torvalds 已提交
204
{
205
	int retval = 0;
206
	struct controller *ctrl = p_slot->ctrl;
207
	struct pci_bus *parent = ctrl->pcie->port->subordinate;
L
Linus Torvalds 已提交
208

209
	if (POWER_CTRL(ctrl)) {
L
Linus Torvalds 已提交
210
		/* Power on slot */
K
Kenji Kaneshige 已提交
211
		retval = pciehp_power_on_slot(p_slot);
212 213
		if (retval)
			return retval;
L
Linus Torvalds 已提交
214
	}
215

216
	pciehp_green_led_blink(p_slot);
L
Linus Torvalds 已提交
217

218
	/* Check link training status */
K
Kenji Kaneshige 已提交
219
	retval = pciehp_check_link_status(ctrl);
220
	if (retval) {
221
		ctrl_err(ctrl, "Failed to check link status\n");
222
		goto err_exit;
L
Linus Torvalds 已提交
223 224 225
	}

	/* Check for a power fault */
226 227
	if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
		ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
228
		retval = -EIO;
229
		goto err_exit;
L
Linus Torvalds 已提交
230 231
	}

232 233
	retval = pciehp_configure_device(p_slot);
	if (retval) {
234 235
		ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
			 pci_domain_nr(parent), parent->number);
236 237
		if (retval != -EEXIST)
			goto err_exit;
238
	}
L
Linus Torvalds 已提交
239

240
	pciehp_green_led_on(p_slot);
L
Linus Torvalds 已提交
241
	return 0;
242 243 244

err_exit:
	set_slot_off(ctrl, p_slot);
245
	return retval;
L
Linus Torvalds 已提交
246 247 248
}

/**
R
Randy Dunlap 已提交
249 250
 * remove_board - Turns off slot and LEDs
 * @p_slot: slot where board is being removed
L
Linus Torvalds 已提交
251
 */
252
static int remove_board(struct slot *p_slot)
L
Linus Torvalds 已提交
253
{
254
	int retval;
255
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
256

257 258 259
	retval = pciehp_unconfigure_device(p_slot);
	if (retval)
		return retval;
L
Linus Torvalds 已提交
260

261
	if (POWER_CTRL(ctrl)) {
262 263
		pciehp_power_off_slot(p_slot);

264 265 266 267 268 269
		/*
		 * After turning power off, we must wait for at least 1 second
		 * before taking any action that relies on power having been
		 * removed from the slot/adapter.
		 */
		msleep(1000);
L
Linus Torvalds 已提交
270 271
	}

K
Kenji Kaneshige 已提交
272
	/* turn off Green LED */
273
	pciehp_green_led_off(p_slot);
L
Linus Torvalds 已提交
274 275 276
	return 0;
}

K
Kenji Kaneshige 已提交
277 278 279
struct power_work_info {
	struct slot *p_slot;
	struct work_struct work;
280 281 282
	unsigned int req;
#define DISABLE_REQ 0
#define ENABLE_REQ  1
K
Kenji Kaneshige 已提交
283
};
L
Linus Torvalds 已提交
284 285

/**
R
Randy Dunlap 已提交
286 287
 * pciehp_power_thread - handle pushbutton events
 * @work: &struct work_struct describing work to be done
L
Linus Torvalds 已提交
288
 *
R
Randy Dunlap 已提交
289
 * Scheduled procedure to handle blocking stuff for the pushbuttons.
L
Linus Torvalds 已提交
290 291
 * Handles all pending events and exits.
 */
K
Kenji Kaneshige 已提交
292
static void pciehp_power_thread(struct work_struct *work)
L
Linus Torvalds 已提交
293
{
K
Kenji Kaneshige 已提交
294 295 296
	struct power_work_info *info =
		container_of(work, struct power_work_info, work);
	struct slot *p_slot = info->p_slot;
297
	int ret;
K
Kenji Kaneshige 已提交
298

299 300
	switch (info->req) {
	case DISABLE_REQ:
301
		ctrl_dbg(p_slot->ctrl,
302
			 "Disabling domain:bus:device=%04x:%02x:00\n",
303 304
			 pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
			 p_slot->ctrl->pcie->port->subordinate->number);
305
		mutex_lock(&p_slot->hotplug_lock);
L
Linus Torvalds 已提交
306
		pciehp_disable_slot(p_slot);
307
		mutex_unlock(&p_slot->hotplug_lock);
K
Kenji Kaneshige 已提交
308
		mutex_lock(&p_slot->lock);
L
Linus Torvalds 已提交
309
		p_slot->state = STATIC_STATE;
K
Kenji Kaneshige 已提交
310
		mutex_unlock(&p_slot->lock);
311 312 313 314 315 316
		break;
	case ENABLE_REQ:
		ctrl_dbg(p_slot->ctrl,
			 "Enabling domain:bus:device=%04x:%02x:00\n",
			 pci_domain_nr(p_slot->ctrl->pcie->port->subordinate),
			 p_slot->ctrl->pcie->port->subordinate->number);
317 318 319 320
		mutex_lock(&p_slot->hotplug_lock);
		ret = pciehp_enable_slot(p_slot);
		mutex_unlock(&p_slot->hotplug_lock);
		if (ret)
K
Kenji Kaneshige 已提交
321
			pciehp_green_led_off(p_slot);
K
Kenji Kaneshige 已提交
322
		mutex_lock(&p_slot->lock);
L
Linus Torvalds 已提交
323
		p_slot->state = STATIC_STATE;
324
		mutex_unlock(&p_slot->lock);
K
Kenji Kaneshige 已提交
325 326 327
		break;
	default:
		break;
L
Linus Torvalds 已提交
328 329
	}

K
Kenji Kaneshige 已提交
330
	kfree(info);
L
Linus Torvalds 已提交
331 332
}

333
void pciehp_queue_pushbutton_work(struct work_struct *work)
L
Linus Torvalds 已提交
334
{
K
Kenji Kaneshige 已提交
335 336
	struct slot *p_slot = container_of(work, struct slot, work.work);
	struct power_work_info *info;
L
Linus Torvalds 已提交
337

K
Kenji Kaneshige 已提交
338 339
	info = kmalloc(sizeof(*info), GFP_KERNEL);
	if (!info) {
340 341
		ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
			 __func__);
L
Linus Torvalds 已提交
342 343
		return;
	}
K
Kenji Kaneshige 已提交
344 345
	info->p_slot = p_slot;
	INIT_WORK(&info->work, pciehp_power_thread);
L
Linus Torvalds 已提交
346

K
Kenji Kaneshige 已提交
347 348 349
	mutex_lock(&p_slot->lock);
	switch (p_slot->state) {
	case BLINKINGOFF_STATE:
L
Linus Torvalds 已提交
350
		p_slot->state = POWEROFF_STATE;
351
		info->req = DISABLE_REQ;
K
Kenji Kaneshige 已提交
352 353
		break;
	case BLINKINGON_STATE:
L
Linus Torvalds 已提交
354
		p_slot->state = POWERON_STATE;
355
		info->req = ENABLE_REQ;
K
Kenji Kaneshige 已提交
356 357
		break;
	default:
J
Jiri Slaby 已提交
358
		kfree(info);
K
Kenji Kaneshige 已提交
359
		goto out;
L
Linus Torvalds 已提交
360
	}
361
	queue_work(p_slot->wq, &info->work);
K
Kenji Kaneshige 已提交
362 363
 out:
	mutex_unlock(&p_slot->lock);
L
Linus Torvalds 已提交
364 365
}

K
Kenji Kaneshige 已提交
366 367 368 369
/*
 * Note: This function must be called with slot->lock held
 */
static void handle_button_press_event(struct slot *p_slot)
L
Linus Torvalds 已提交
370
{
K
Kenji Kaneshige 已提交
371
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
372 373
	u8 getstatus;

K
Kenji Kaneshige 已提交
374 375
	switch (p_slot->state) {
	case STATIC_STATE:
K
Kenji Kaneshige 已提交
376
		pciehp_get_power_status(p_slot, &getstatus);
K
Kenji Kaneshige 已提交
377 378
		if (getstatus) {
			p_slot->state = BLINKINGOFF_STATE;
379 380
			ctrl_info(ctrl,
				  "PCI slot #%s - powering off due to button "
A
Alex Chiang 已提交
381
				  "press.\n", slot_name(p_slot));
K
Kenji Kaneshige 已提交
382 383
		} else {
			p_slot->state = BLINKINGON_STATE;
384 385
			ctrl_info(ctrl,
				  "PCI slot #%s - powering on due to button "
A
Alex Chiang 已提交
386
				  "press.\n", slot_name(p_slot));
K
Kenji Kaneshige 已提交
387 388
		}
		/* blink green LED and turn off amber */
389 390
		pciehp_green_led_blink(p_slot);
		pciehp_set_attention_status(p_slot, 0);
391
		queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
K
Kenji Kaneshige 已提交
392 393 394 395 396 397 398 399
		break;
	case BLINKINGOFF_STATE:
	case BLINKINGON_STATE:
		/*
		 * Cancel if we are still blinking; this means that we
		 * press the attention again before the 5 sec. limit
		 * expires to cancel hot-add or hot-remove
		 */
A
Alex Chiang 已提交
400
		ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot));
K
Kenji Kaneshige 已提交
401
		cancel_delayed_work(&p_slot->work);
B
Bjorn Helgaas 已提交
402
		if (p_slot->state == BLINKINGOFF_STATE)
403
			pciehp_green_led_on(p_slot);
B
Bjorn Helgaas 已提交
404
		else
405 406
			pciehp_green_led_off(p_slot);
		pciehp_set_attention_status(p_slot, 0);
407
		ctrl_info(ctrl, "PCI slot #%s - action canceled "
A
Alex Chiang 已提交
408
			  "due to button press\n", slot_name(p_slot));
K
Kenji Kaneshige 已提交
409 410 411 412 413 414 415 416 417
		p_slot->state = STATIC_STATE;
		break;
	case POWEROFF_STATE:
	case POWERON_STATE:
		/*
		 * Ignore if the slot is on power-on or power-off state;
		 * this means that the previous attention button action
		 * to hot-add or hot-remove is undergoing
		 */
A
Alex Chiang 已提交
418
		ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
K
Kenji Kaneshige 已提交
419 420
		break;
	default:
421
		ctrl_warn(ctrl, "Not a valid state\n");
K
Kenji Kaneshige 已提交
422
		break;
L
Linus Torvalds 已提交
423 424 425
	}
}

K
Kenji Kaneshige 已提交
426 427 428 429 430 431 432 433 434 435
/*
 * Note: This function must be called with slot->lock held
 */
static void handle_surprise_event(struct slot *p_slot)
{
	u8 getstatus;
	struct power_work_info *info;

	info = kmalloc(sizeof(*info), GFP_KERNEL);
	if (!info) {
436 437
		ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
			 __func__);
K
Kenji Kaneshige 已提交
438 439 440 441 442
		return;
	}
	info->p_slot = p_slot;
	INIT_WORK(&info->work, pciehp_power_thread);

K
Kenji Kaneshige 已提交
443
	pciehp_get_adapter_status(p_slot, &getstatus);
444
	if (!getstatus) {
K
Kenji Kaneshige 已提交
445
		p_slot->state = POWEROFF_STATE;
446 447
		info->req = DISABLE_REQ;
	} else {
K
Kenji Kaneshige 已提交
448
		p_slot->state = POWERON_STATE;
449 450
		info->req = ENABLE_REQ;
	}
K
Kenji Kaneshige 已提交
451

452
	queue_work(p_slot->wq, &info->work);
K
Kenji Kaneshige 已提交
453 454
}

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
/*
 * Note: This function must be called with slot->lock held
 */
static void handle_link_event(struct slot *p_slot, u32 event)
{
	struct controller *ctrl = p_slot->ctrl;
	struct power_work_info *info;

	info = kmalloc(sizeof(*info), GFP_KERNEL);
	if (!info) {
		ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
			 __func__);
		return;
	}
	info->p_slot = p_slot;
470
	info->req = event == INT_LINK_UP ? ENABLE_REQ : DISABLE_REQ;
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
	INIT_WORK(&info->work, pciehp_power_thread);

	switch (p_slot->state) {
	case BLINKINGON_STATE:
	case BLINKINGOFF_STATE:
		cancel_delayed_work(&p_slot->work);
		/* Fall through */
	case STATIC_STATE:
		p_slot->state = event == INT_LINK_UP ?
		    POWERON_STATE : POWEROFF_STATE;
		queue_work(p_slot->wq, &info->work);
		break;
	case POWERON_STATE:
		if (event == INT_LINK_UP) {
			ctrl_info(ctrl,
				  "Link Up event ignored on slot(%s): already powering on\n",
				  slot_name(p_slot));
			kfree(info);
		} else {
			ctrl_info(ctrl,
				  "Link Down event queued on slot(%s): currently getting powered on\n",
				  slot_name(p_slot));
			p_slot->state = POWEROFF_STATE;
			queue_work(p_slot->wq, &info->work);
		}
		break;
	case POWEROFF_STATE:
		if (event == INT_LINK_UP) {
			ctrl_info(ctrl,
				  "Link Up event queued on slot(%s): currently getting powered off\n",
				  slot_name(p_slot));
			p_slot->state = POWERON_STATE;
			queue_work(p_slot->wq, &info->work);
		} else {
			ctrl_info(ctrl,
				  "Link Down event ignored on slot(%s): already powering off\n",
				  slot_name(p_slot));
			kfree(info);
		}
		break;
	default:
		ctrl_err(ctrl, "Not a valid state on slot(%s)\n",
			 slot_name(p_slot));
		kfree(info);
		break;
	}
}

K
Kenji Kaneshige 已提交
519 520 521 522 523 524 525 526 527 528 529 530
static void interrupt_event_handler(struct work_struct *work)
{
	struct event_info *info = container_of(work, struct event_info, work);
	struct slot *p_slot = info->p_slot;
	struct controller *ctrl = p_slot->ctrl;

	mutex_lock(&p_slot->lock);
	switch (info->event_type) {
	case INT_BUTTON_PRESS:
		handle_button_press_event(p_slot);
		break;
	case INT_POWER_FAULT:
531
		if (!POWER_CTRL(ctrl))
K
Kenji Kaneshige 已提交
532
			break;
533 534
		pciehp_set_attention_status(p_slot, 1);
		pciehp_green_led_off(p_slot);
K
Kenji Kaneshige 已提交
535 536
		break;
	case INT_PRESENCE_ON:
537
		if (!HP_SUPR_RM(ctrl))
K
Kenji Kaneshige 已提交
538
			break;
539 540 541 542 543 544 545 546
		ctrl_dbg(ctrl, "Surprise Insertion\n");
		handle_surprise_event(p_slot);
		break;
	case INT_PRESENCE_OFF:
		/*
		 * Regardless of surprise capability, we need to
		 * definitely remove a card that has been pulled out!
		 */
547
		ctrl_dbg(ctrl, "Surprise Removal\n");
K
Kenji Kaneshige 已提交
548 549
		handle_surprise_event(p_slot);
		break;
550 551 552 553
	case INT_LINK_UP:
	case INT_LINK_DOWN:
		handle_link_event(p_slot, info->event_type);
		break;
K
Kenji Kaneshige 已提交
554 555 556 557 558 559 560 561
	default:
		break;
	}
	mutex_unlock(&p_slot->lock);

	kfree(info);
}

562 563 564
/*
 * Note: This function must be called with slot->hotplug_lock held
 */
L
Linus Torvalds 已提交
565 566 567 568
int pciehp_enable_slot(struct slot *p_slot)
{
	u8 getstatus = 0;
	int rc;
569
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
570

571 572
	pciehp_get_adapter_status(p_slot, &getstatus);
	if (!getstatus) {
573
		ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
574
		return -ENODEV;
L
Linus Torvalds 已提交
575
	}
576
	if (MRL_SENS(p_slot->ctrl)) {
577 578
		pciehp_get_latch_status(p_slot, &getstatus);
		if (getstatus) {
579 580
			ctrl_info(ctrl, "Latch open on slot(%s)\n",
				  slot_name(p_slot));
581
			return -ENODEV;
L
Linus Torvalds 已提交
582 583
		}
	}
584

585
	if (POWER_CTRL(p_slot->ctrl)) {
586 587
		pciehp_get_power_status(p_slot, &getstatus);
		if (getstatus) {
588 589
			ctrl_info(ctrl, "Already enabled on slot(%s)\n",
				  slot_name(p_slot));
590
			return -EINVAL;
L
Linus Torvalds 已提交
591 592 593
		}
	}

K
Kenji Kaneshige 已提交
594
	pciehp_get_latch_status(p_slot, &getstatus);
L
Linus Torvalds 已提交
595

596
	rc = board_added(p_slot);
B
Bjorn Helgaas 已提交
597
	if (rc)
K
Kenji Kaneshige 已提交
598
		pciehp_get_latch_status(p_slot, &getstatus);
B
Bjorn Helgaas 已提交
599

L
Linus Torvalds 已提交
600 601 602
	return rc;
}

603 604 605
/*
 * Note: This function must be called with slot->hotplug_lock held
 */
L
Linus Torvalds 已提交
606 607 608
int pciehp_disable_slot(struct slot *p_slot)
{
	u8 getstatus = 0;
609
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
610 611 612 613

	if (!p_slot->ctrl)
		return 1;

614
	if (POWER_CTRL(p_slot->ctrl)) {
615 616
		pciehp_get_power_status(p_slot, &getstatus);
		if (!getstatus) {
617 618
			ctrl_info(ctrl, "Already disabled on slot(%s)\n",
				  slot_name(p_slot));
619
			return -EINVAL;
L
Linus Torvalds 已提交
620 621 622
		}
	}

623
	return remove_board(p_slot);
L
Linus Torvalds 已提交
624 625
}

K
Kenji Kaneshige 已提交
626 627 628
int pciehp_sysfs_enable_slot(struct slot *p_slot)
{
	int retval = -ENODEV;
629
	struct controller *ctrl = p_slot->ctrl;
K
Kenji Kaneshige 已提交
630 631 632 633 634 635 636 637

	mutex_lock(&p_slot->lock);
	switch (p_slot->state) {
	case BLINKINGON_STATE:
		cancel_delayed_work(&p_slot->work);
	case STATIC_STATE:
		p_slot->state = POWERON_STATE;
		mutex_unlock(&p_slot->lock);
638
		mutex_lock(&p_slot->hotplug_lock);
K
Kenji Kaneshige 已提交
639
		retval = pciehp_enable_slot(p_slot);
640
		mutex_unlock(&p_slot->hotplug_lock);
K
Kenji Kaneshige 已提交
641 642 643 644
		mutex_lock(&p_slot->lock);
		p_slot->state = STATIC_STATE;
		break;
	case POWERON_STATE:
645
		ctrl_info(ctrl, "Slot %s is already in powering on state\n",
A
Alex Chiang 已提交
646
			  slot_name(p_slot));
K
Kenji Kaneshige 已提交
647 648 649
		break;
	case BLINKINGOFF_STATE:
	case POWEROFF_STATE:
A
Alex Chiang 已提交
650 651
		ctrl_info(ctrl, "Already enabled on slot %s\n",
			  slot_name(p_slot));
K
Kenji Kaneshige 已提交
652 653
		break;
	default:
A
Alex Chiang 已提交
654 655
		ctrl_err(ctrl, "Not a valid state on slot %s\n",
			 slot_name(p_slot));
K
Kenji Kaneshige 已提交
656 657 658 659 660 661 662 663 664 665
		break;
	}
	mutex_unlock(&p_slot->lock);

	return retval;
}

int pciehp_sysfs_disable_slot(struct slot *p_slot)
{
	int retval = -ENODEV;
666
	struct controller *ctrl = p_slot->ctrl;
K
Kenji Kaneshige 已提交
667 668 669 670 671 672 673 674 675 676 677 678 679

	mutex_lock(&p_slot->lock);
	switch (p_slot->state) {
	case BLINKINGOFF_STATE:
		cancel_delayed_work(&p_slot->work);
	case STATIC_STATE:
		p_slot->state = POWEROFF_STATE;
		mutex_unlock(&p_slot->lock);
		retval = pciehp_disable_slot(p_slot);
		mutex_lock(&p_slot->lock);
		p_slot->state = STATIC_STATE;
		break;
	case POWEROFF_STATE:
680
		ctrl_info(ctrl, "Slot %s is already in powering off state\n",
A
Alex Chiang 已提交
681
			  slot_name(p_slot));
K
Kenji Kaneshige 已提交
682 683 684
		break;
	case BLINKINGON_STATE:
	case POWERON_STATE:
A
Alex Chiang 已提交
685 686
		ctrl_info(ctrl, "Already disabled on slot %s\n",
			  slot_name(p_slot));
K
Kenji Kaneshige 已提交
687 688
		break;
	default:
A
Alex Chiang 已提交
689 690
		ctrl_err(ctrl, "Not a valid state on slot %s\n",
			 slot_name(p_slot));
K
Kenji Kaneshige 已提交
691 692 693 694 695 696
		break;
	}
	mutex_unlock(&p_slot->lock);

	return retval;
}