pciehp_ctrl.c 16.1 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 33 34
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/smp_lock.h>
#include <linux/pci.h>
K
Kenji Kaneshige 已提交
35
#include <linux/workqueue.h>
L
Linus Torvalds 已提交
36 37 38
#include "../pci.h"
#include "pciehp.h"

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

K
Kenji Kaneshige 已提交
41
static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
42
{
K
Kenji Kaneshige 已提交
43 44 45 46 47 48 49 50 51 52 53 54 55
	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);

	schedule_work(&info->work);

	return 0;
56 57
}

58
u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
L
Linus Torvalds 已提交
59 60
{
	struct slot *p_slot;
K
Kenji Kaneshige 已提交
61
	u32 event_type;
L
Linus Torvalds 已提交
62 63 64 65

	/* Attention Button Change */
	dbg("pciehp:  Attention button interrupt received.\n");

K
Kenji Kaneshige 已提交
66
	p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
L
Linus Torvalds 已提交
67 68 69 70

	/*
	 *  Button pressed - See if need to TAKE ACTION!!!
	 */
K
Kenji Kaneshige 已提交
71 72
	info("Button pressed on Slot(%s)\n", p_slot->name);
	event_type = INT_BUTTON_PRESS;
L
Linus Torvalds 已提交
73

K
Kenji Kaneshige 已提交
74
	queue_interrupt_event(p_slot, event_type);
L
Linus Torvalds 已提交
75 76 77 78

	return 0;
}

79
u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
L
Linus Torvalds 已提交
80 81 82
{
	struct slot *p_slot;
	u8 getstatus;
K
Kenji Kaneshige 已提交
83
	u32 event_type;
L
Linus Torvalds 已提交
84 85 86 87 88 89 90 91 92 93 94

	/* Switch Change */
	dbg("pciehp:  Switch interrupt received.\n");

	p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
	p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);

	if (getstatus) {
		/*
		 * Switch opened
		 */
K
Kenji Kaneshige 已提交
95 96
		info("Latch open on Slot(%s)\n", p_slot->name);
		event_type = INT_SWITCH_OPEN;
L
Linus Torvalds 已提交
97 98 99 100
	} else {
		/*
		 *  Switch closed
		 */
K
Kenji Kaneshige 已提交
101 102
		info("Latch close on Slot(%s)\n", p_slot->name);
		event_type = INT_SWITCH_CLOSE;
L
Linus Torvalds 已提交
103 104
	}

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

K
Kenji Kaneshige 已提交
107
	return 1;
L
Linus Torvalds 已提交
108 109
}

110
u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
L
Linus Torvalds 已提交
111 112
{
	struct slot *p_slot;
K
Kenji Kaneshige 已提交
113 114
	u32 event_type;
	u8 presence_save;
L
Linus Torvalds 已提交
115 116 117 118 119 120 121 122 123

	/* Presence Change */
	dbg("pciehp:  Presence/Notify input change.\n");

	p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);

	/* Switch is open, assume a presence change
	 * Save the presence state
	 */
124 125
	p_slot->hpc_ops->get_adapter_status(p_slot, &presence_save);
	if (presence_save) {
L
Linus Torvalds 已提交
126 127 128
		/*
		 * Card Present
		 */
K
Kenji Kaneshige 已提交
129 130
		info("Card present on Slot(%s)\n", p_slot->name);
		event_type = INT_PRESENCE_ON;
L
Linus Torvalds 已提交
131 132 133 134
	} else {
		/*
		 * Not Present
		 */
K
Kenji Kaneshige 已提交
135 136
		info("Card not present on Slot(%s)\n", p_slot->name);
		event_type = INT_PRESENCE_OFF;
L
Linus Torvalds 已提交
137 138
	}

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

K
Kenji Kaneshige 已提交
141
	return 1;
L
Linus Torvalds 已提交
142 143
}

144
u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
L
Linus Torvalds 已提交
145 146
{
	struct slot *p_slot;
K
Kenji Kaneshige 已提交
147
	u32 event_type;
L
Linus Torvalds 已提交
148 149 150 151 152 153 154 155 156 157

	/* power fault */
	dbg("pciehp:  Power fault interrupt received.\n");

	p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);

	if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
		/*
		 * power fault Cleared
		 */
K
Kenji Kaneshige 已提交
158 159
		info("Power fault cleared on Slot(%s)\n", p_slot->name);
		event_type = INT_POWER_FAULT_CLEAR;
L
Linus Torvalds 已提交
160 161 162 163
	} else {
		/*
		 *   power fault
		 */
K
Kenji Kaneshige 已提交
164 165
		info("Power fault on Slot(%s)\n", p_slot->name);
		event_type = INT_POWER_FAULT;
L
Linus Torvalds 已提交
166 167 168
		info("power fault bit %x set\n", hp_slot);
	}

K
Kenji Kaneshige 已提交
169 170 171
	queue_interrupt_event(p_slot, event_type);

	return 1;
L
Linus Torvalds 已提交
172 173
}

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

static void set_slot_off(struct controller *ctrl, struct slot * pslot)
{
	/* turn off slot, turn on Amber LED, turn off Green LED if supported*/
	if (POWER_CTRL(ctrl->ctrlcap)) {
182
		if (pslot->hpc_ops->power_off_slot(pslot)) {
183
			err("%s: Issue of Slot Power Off command failed\n",
184
			    __func__);
L
Linus Torvalds 已提交
185 186 187 188
			return;
		}
	}

189
	if (PWR_LED(ctrl->ctrlcap))
190
		pslot->hpc_ops->green_led_off(pslot);
L
Linus Torvalds 已提交
191

192 193 194
	if (ATTN_LED(ctrl->ctrlcap)) {
		if (pslot->hpc_ops->set_attention_status(pslot, 1)) {
			err("%s: Issue of Set Attention Led command failed\n",
195
			    __func__);
L
Linus Torvalds 已提交
196 197 198 199 200 201 202
			return;
		}
	}
}

/**
 * board_added - Called after a board has been added to the system.
R
Randy Dunlap 已提交
203
 * @p_slot: &slot where board is added
L
Linus Torvalds 已提交
204
 *
R
Randy Dunlap 已提交
205 206
 * Turns power on for the board.
 * Configures board.
L
Linus Torvalds 已提交
207
 */
208
static int board_added(struct slot *p_slot)
L
Linus Torvalds 已提交
209
{
210
	int retval = 0;
211
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
212

213
	dbg("%s: slot device, slot offset, hp slot = %d, %d ,%d\n",
214
			__func__, p_slot->device,
215
			ctrl->slot_device_offset, p_slot->hp_slot);
L
Linus Torvalds 已提交
216 217 218

	if (POWER_CTRL(ctrl->ctrlcap)) {
		/* Power on slot */
219 220 221
		retval = p_slot->hpc_ops->power_on_slot(p_slot);
		if (retval)
			return retval;
L
Linus Torvalds 已提交
222
	}
223

224
	if (PWR_LED(ctrl->ctrlcap))
L
Linus Torvalds 已提交
225 226 227
		p_slot->hpc_ops->green_led_blink(p_slot);

	/* Wait for ~1 second */
228
	msleep(1000);
L
Linus Torvalds 已提交
229

230 231 232
	/* Check link training status */
	retval = p_slot->hpc_ops->check_lnk_status(ctrl);
	if (retval) {
233
		err("%s: Failed to check link status\n", __func__);
L
Linus Torvalds 已提交
234
		set_slot_off(ctrl, p_slot);
235
		return retval;
L
Linus Torvalds 已提交
236 237 238
	}

	/* Check for a power fault */
239
	if (p_slot->hpc_ops->query_power_fault(p_slot)) {
240
		dbg("%s: power fault detected\n", __func__);
241
		retval = POWER_FAILURE;
242
		goto err_exit;
L
Linus Torvalds 已提交
243 244
	}

245 246
	retval = pciehp_configure_device(p_slot);
	if (retval) {
247
		err("Cannot add device 0x%x:%x\n", p_slot->bus,
248
		    p_slot->device);
249 250
		goto err_exit;
	}
L
Linus Torvalds 已提交
251

252 253 254 255 256
	/*
	 * Some PCI Express root ports require fixup after hot-plug operation.
	 */
	if (pcie_mch_quirk)
		pci_fixup_device(pci_fixup_final, ctrl->pci_dev);
257
	if (PWR_LED(ctrl->ctrlcap))
258
  		p_slot->hpc_ops->green_led_on(p_slot);
259

L
Linus Torvalds 已提交
260
	return 0;
261 262 263

err_exit:
	set_slot_off(ctrl, p_slot);
264
	return retval;
L
Linus Torvalds 已提交
265 266 267
}

/**
R
Randy Dunlap 已提交
268 269
 * remove_board - Turns off slot and LEDs
 * @p_slot: slot where board is being removed
L
Linus Torvalds 已提交
270
 */
271
static int remove_board(struct slot *p_slot)
L
Linus Torvalds 已提交
272
{
273
	int retval = 0;
274
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
275

276 277 278
	retval = pciehp_unconfigure_device(p_slot);
	if (retval)
		return retval;
L
Linus Torvalds 已提交
279

280
	dbg("In %s, hp_slot = %d\n", __func__, p_slot->hp_slot);
L
Linus Torvalds 已提交
281 282 283

	if (POWER_CTRL(ctrl->ctrlcap)) {
		/* power off slot */
284 285 286
		retval = p_slot->hpc_ops->power_off_slot(p_slot);
		if (retval) {
			err("%s: Issue of Slot Disable command failed\n",
287
			    __func__);
288
			return retval;
L
Linus Torvalds 已提交
289 290 291
		}
	}

292
	if (PWR_LED(ctrl->ctrlcap))
L
Linus Torvalds 已提交
293 294 295 296 297 298
		/* turn off Green LED */
		p_slot->hpc_ops->green_led_off(p_slot);

	return 0;
}

K
Kenji Kaneshige 已提交
299 300 301 302
struct power_work_info {
	struct slot *p_slot;
	struct work_struct work;
};
L
Linus Torvalds 已提交
303 304

/**
R
Randy Dunlap 已提交
305 306
 * pciehp_power_thread - handle pushbutton events
 * @work: &struct work_struct describing work to be done
L
Linus Torvalds 已提交
307
 *
R
Randy Dunlap 已提交
308
 * Scheduled procedure to handle blocking stuff for the pushbuttons.
L
Linus Torvalds 已提交
309 310
 * Handles all pending events and exits.
 */
K
Kenji Kaneshige 已提交
311
static void pciehp_power_thread(struct work_struct *work)
L
Linus Torvalds 已提交
312
{
K
Kenji Kaneshige 已提交
313 314 315 316 317 318 319 320 321
	struct power_work_info *info =
		container_of(work, struct power_work_info, work);
	struct slot *p_slot = info->p_slot;

	mutex_lock(&p_slot->lock);
	switch (p_slot->state) {
	case POWEROFF_STATE:
		mutex_unlock(&p_slot->lock);
		dbg("%s: disabling bus:device(%x:%x)\n",
322
		    __func__, p_slot->bus, p_slot->device);
L
Linus Torvalds 已提交
323
		pciehp_disable_slot(p_slot);
K
Kenji Kaneshige 已提交
324
		mutex_lock(&p_slot->lock);
L
Linus Torvalds 已提交
325
		p_slot->state = STATIC_STATE;
K
Kenji Kaneshige 已提交
326 327 328
		break;
	case POWERON_STATE:
		mutex_unlock(&p_slot->lock);
329 330
		if (pciehp_enable_slot(p_slot) &&
		    PWR_LED(p_slot->ctrl->ctrlcap))
L
Linus Torvalds 已提交
331
			p_slot->hpc_ops->green_led_off(p_slot);
K
Kenji Kaneshige 已提交
332
		mutex_lock(&p_slot->lock);
L
Linus Torvalds 已提交
333
		p_slot->state = STATIC_STATE;
K
Kenji Kaneshige 已提交
334 335 336
		break;
	default:
		break;
L
Linus Torvalds 已提交
337
	}
K
Kenji Kaneshige 已提交
338
	mutex_unlock(&p_slot->lock);
L
Linus Torvalds 已提交
339

K
Kenji Kaneshige 已提交
340
	kfree(info);
L
Linus Torvalds 已提交
341 342
}

343
void pciehp_queue_pushbutton_work(struct work_struct *work)
L
Linus Torvalds 已提交
344
{
K
Kenji Kaneshige 已提交
345 346
	struct slot *p_slot = container_of(work, struct slot, work.work);
	struct power_work_info *info;
L
Linus Torvalds 已提交
347

K
Kenji Kaneshige 已提交
348 349
	info = kmalloc(sizeof(*info), GFP_KERNEL);
	if (!info) {
350
		err("%s: Cannot allocate memory\n", __func__);
L
Linus Torvalds 已提交
351 352
		return;
	}
K
Kenji Kaneshige 已提交
353 354
	info->p_slot = p_slot;
	INIT_WORK(&info->work, pciehp_power_thread);
L
Linus Torvalds 已提交
355

K
Kenji Kaneshige 已提交
356 357 358
	mutex_lock(&p_slot->lock);
	switch (p_slot->state) {
	case BLINKINGOFF_STATE:
L
Linus Torvalds 已提交
359
		p_slot->state = POWEROFF_STATE;
K
Kenji Kaneshige 已提交
360 361
		break;
	case BLINKINGON_STATE:
L
Linus Torvalds 已提交
362
		p_slot->state = POWERON_STATE;
K
Kenji Kaneshige 已提交
363 364 365
		break;
	default:
		goto out;
L
Linus Torvalds 已提交
366
	}
K
Kenji Kaneshige 已提交
367 368 369
	queue_work(pciehp_wq, &info->work);
 out:
	mutex_unlock(&p_slot->lock);
L
Linus Torvalds 已提交
370 371 372 373 374 375 376
}

static int update_slot_info(struct slot *slot)
{
	struct hotplug_slot_info *info;
	int result;

K
Kenji Kaneshige 已提交
377
	info = kmalloc(sizeof(*info), GFP_KERNEL);
L
Linus Torvalds 已提交
378 379 380 381 382 383 384 385 386 387 388 389 390
	if (!info)
		return -ENOMEM;

	slot->hpc_ops->get_power_status(slot, &(info->power_status));
	slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
	slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
	slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));

	result = pci_hp_change_slot_info(slot->hotplug_slot, info);
	kfree (info);
	return result;
}

K
Kenji Kaneshige 已提交
391 392 393 394
/*
 * Note: This function must be called with slot->lock held
 */
static void handle_button_press_event(struct slot *p_slot)
L
Linus Torvalds 已提交
395
{
K
Kenji Kaneshige 已提交
396
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
397 398
	u8 getstatus;

K
Kenji Kaneshige 已提交
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
	switch (p_slot->state) {
	case STATIC_STATE:
		p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
		if (getstatus) {
			p_slot->state = BLINKINGOFF_STATE;
			info("PCI slot #%s - powering off due to button "
			     "press.\n", p_slot->name);
		} else {
			p_slot->state = BLINKINGON_STATE;
			info("PCI slot #%s - powering on due to button "
			     "press.\n", p_slot->name);
		}
		/* blink green LED and turn off amber */
		if (PWR_LED(ctrl->ctrlcap))
			p_slot->hpc_ops->green_led_blink(p_slot);
		if (ATTN_LED(ctrl->ctrlcap))
			p_slot->hpc_ops->set_attention_status(p_slot, 0);

		schedule_delayed_work(&p_slot->work, 5*HZ);
		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
		 */
		info("Button cancel on Slot(%s)\n", p_slot->name);
427
		dbg("%s: button cancel\n", __func__);
K
Kenji Kaneshige 已提交
428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
		cancel_delayed_work(&p_slot->work);
		if (p_slot->state == BLINKINGOFF_STATE) {
			if (PWR_LED(ctrl->ctrlcap))
				p_slot->hpc_ops->green_led_on(p_slot);
		} else {
			if (PWR_LED(ctrl->ctrlcap))
				p_slot->hpc_ops->green_led_off(p_slot);
		}
		if (ATTN_LED(ctrl->ctrlcap))
			p_slot->hpc_ops->set_attention_status(p_slot, 0);
		info("PCI slot #%s - action canceled due to button press\n",
		     p_slot->name);
		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
		 */
		info("Button ignore on Slot(%s)\n", p_slot->name);
		update_slot_info(p_slot);
		break;
	default:
		warn("Not a valid state\n");
		break;
L
Linus Torvalds 已提交
455 456 457
	}
}

K
Kenji Kaneshige 已提交
458 459 460 461 462 463 464 465 466 467
/*
 * 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) {
468
		err("%s: Cannot allocate memory\n", __func__);
K
Kenji Kaneshige 已提交
469 470 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
		return;
	}
	info->p_slot = p_slot;
	INIT_WORK(&info->work, pciehp_power_thread);

	p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
	if (!getstatus)
		p_slot->state = POWEROFF_STATE;
	else
		p_slot->state = POWERON_STATE;

	queue_work(pciehp_wq, &info->work);
}

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:
		if (!POWER_CTRL(ctrl->ctrlcap))
			break;
		if (ATTN_LED(ctrl->ctrlcap))
			p_slot->hpc_ops->set_attention_status(p_slot, 1);
		if (PWR_LED(ctrl->ctrlcap))
			p_slot->hpc_ops->green_led_off(p_slot);
		break;
	case INT_PRESENCE_ON:
	case INT_PRESENCE_OFF:
		if (!HP_SUPR_RM(ctrl->ctrlcap))
			break;
		dbg("Surprise Removal\n");
		update_slot_info(p_slot);
		handle_surprise_event(p_slot);
		break;
	default:
		update_slot_info(p_slot);
		break;
	}
	mutex_unlock(&p_slot->lock);

	kfree(info);
}

L
Linus Torvalds 已提交
519 520 521 522 523 524
int pciehp_enable_slot(struct slot *p_slot)
{
	u8 getstatus = 0;
	int rc;

	/* Check to see if (latch closed, card present, power off) */
525
	mutex_lock(&p_slot->ctrl->crit_sect);
L
Linus Torvalds 已提交
526 527 528

	rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
	if (rc || !getstatus) {
529
		info("%s: no adapter on slot(%s)\n", __func__,
K
Kenji Kaneshige 已提交
530
		     p_slot->name);
531
		mutex_unlock(&p_slot->ctrl->crit_sect);
532
		return -ENODEV;
L
Linus Torvalds 已提交
533
	}
534
	if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
L
Linus Torvalds 已提交
535 536
		rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
		if (rc || getstatus) {
537
			info("%s: latch open on slot(%s)\n", __func__,
K
Kenji Kaneshige 已提交
538
			     p_slot->name);
539
			mutex_unlock(&p_slot->ctrl->crit_sect);
540
			return -ENODEV;
L
Linus Torvalds 已提交
541 542
		}
	}
543 544

	if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
L
Linus Torvalds 已提交
545 546
		rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
		if (rc || getstatus) {
547
			info("%s: already enabled on slot(%s)\n", __func__,
K
Kenji Kaneshige 已提交
548
			     p_slot->name);
549
			mutex_unlock(&p_slot->ctrl->crit_sect);
550
			return -EINVAL;
L
Linus Torvalds 已提交
551 552 553 554 555
		}
	}

	p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);

556
	rc = board_added(p_slot);
L
Linus Torvalds 已提交
557 558 559 560
	if (rc) {
		p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
	}

561
	update_slot_info(p_slot);
L
Linus Torvalds 已提交
562

K
Kenji Kaneshige 已提交
563
	mutex_unlock(&p_slot->ctrl->crit_sect);
L
Linus Torvalds 已提交
564 565 566 567 568 569 570 571 572 573 574 575 576
	return rc;
}


int pciehp_disable_slot(struct slot *p_slot)
{
	u8 getstatus = 0;
	int ret = 0;

	if (!p_slot->ctrl)
		return 1;

	/* Check to see if (latch closed, card present, power on) */
577
	mutex_lock(&p_slot->ctrl->crit_sect);
L
Linus Torvalds 已提交
578

579
	if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) {
L
Linus Torvalds 已提交
580 581
		ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
		if (ret || !getstatus) {
582
			info("%s: no adapter on slot(%s)\n", __func__,
K
Kenji Kaneshige 已提交
583
			     p_slot->name);
584
			mutex_unlock(&p_slot->ctrl->crit_sect);
585
			return -ENODEV;
L
Linus Torvalds 已提交
586 587 588
		}
	}

589
	if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
L
Linus Torvalds 已提交
590 591
		ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
		if (ret || getstatus) {
592
			info("%s: latch open on slot(%s)\n", __func__,
K
Kenji Kaneshige 已提交
593
			     p_slot->name);
594
			mutex_unlock(&p_slot->ctrl->crit_sect);
595
			return -ENODEV;
L
Linus Torvalds 已提交
596 597 598
		}
	}

599
	if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
L
Linus Torvalds 已提交
600 601
		ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
		if (ret || !getstatus) {
602
			info("%s: already disabled slot(%s)\n", __func__,
K
Kenji Kaneshige 已提交
603
			     p_slot->name);
604
			mutex_unlock(&p_slot->ctrl->crit_sect);
605
			return -EINVAL;
L
Linus Torvalds 已提交
606 607 608
		}
	}

609 610
	ret = remove_board(p_slot);
	update_slot_info(p_slot);
K
Kenji Kaneshige 已提交
611 612

	mutex_unlock(&p_slot->ctrl->crit_sect);
613
	return ret;
L
Linus Torvalds 已提交
614 615
}

K
Kenji Kaneshige 已提交
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
int pciehp_sysfs_enable_slot(struct slot *p_slot)
{
	int retval = -ENODEV;

	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);
		retval = pciehp_enable_slot(p_slot);
		mutex_lock(&p_slot->lock);
		p_slot->state = STATIC_STATE;
		break;
	case POWERON_STATE:
		info("Slot %s is already in powering on state\n",
		     p_slot->name);
		break;
	case BLINKINGOFF_STATE:
	case POWEROFF_STATE:
		info("Already enabled on slot %s\n", p_slot->name);
		break;
	default:
		err("Not a valid state on slot %s\n", p_slot->name);
		break;
	}
	mutex_unlock(&p_slot->lock);

	return retval;
}

int pciehp_sysfs_disable_slot(struct slot *p_slot)
{
	int retval = -ENODEV;

	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:
		info("Slot %s is already in powering off state\n",
		     p_slot->name);
		break;
	case BLINKINGON_STATE:
	case POWERON_STATE:
		info("Already disabled on slot %s\n", p_slot->name);
		break;
	default:
		err("Not a valid state on slot %s\n", p_slot->name);
		break;
	}
	mutex_unlock(&p_slot->lock);

	return retval;
}