pciehp_ctrl.c 16.7 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 40 41
static void interrupt_event_handler(struct work_struct *work);
static int pciehp_enable_slot(struct slot *p_slot);
static int pciehp_disable_slot(struct slot *p_slot);
L
Linus Torvalds 已提交
42

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

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

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

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

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

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

	return 0;
}

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

	/* 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 已提交
97 98
		info("Latch open on Slot(%s)\n", p_slot->name);
		event_type = INT_SWITCH_OPEN;
L
Linus Torvalds 已提交
99 100 101 102
	} else {
		/*
		 *  Switch closed
		 */
K
Kenji Kaneshige 已提交
103 104
		info("Latch close on Slot(%s)\n", p_slot->name);
		event_type = INT_SWITCH_CLOSE;
L
Linus Torvalds 已提交
105 106
	}

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

K
Kenji Kaneshige 已提交
109
	return 1;
L
Linus Torvalds 已提交
110 111
}

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

	/* 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
	 */
126 127
	p_slot->hpc_ops->get_adapter_status(p_slot, &presence_save);
	if (presence_save) {
L
Linus Torvalds 已提交
128 129 130
		/*
		 * Card Present
		 */
K
Kenji Kaneshige 已提交
131 132
		info("Card present on Slot(%s)\n", p_slot->name);
		event_type = INT_PRESENCE_ON;
L
Linus Torvalds 已提交
133 134 135 136
	} else {
		/*
		 * Not Present
		 */
K
Kenji Kaneshige 已提交
137 138
		info("Card not present on Slot(%s)\n", p_slot->name);
		event_type = INT_PRESENCE_OFF;
L
Linus Torvalds 已提交
139 140
	}

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

K
Kenji Kaneshige 已提交
143
	return 1;
L
Linus Torvalds 已提交
144 145
}

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

	/* 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 已提交
160 161
		info("Power fault cleared on Slot(%s)\n", p_slot->name);
		event_type = INT_POWER_FAULT_CLEAR;
L
Linus Torvalds 已提交
162 163 164 165
	} else {
		/*
		 *   power fault
		 */
K
Kenji Kaneshige 已提交
166 167
		info("Power fault on Slot(%s)\n", p_slot->name);
		event_type = INT_POWER_FAULT;
L
Linus Torvalds 已提交
168 169 170
		info("power fault bit %x set\n", hp_slot);
	}

K
Kenji Kaneshige 已提交
171 172 173
	queue_interrupt_event(p_slot, event_type);

	return 1;
L
Linus Torvalds 已提交
174 175
}

176
/* The following routines constitute the bulk of the
L
Linus Torvalds 已提交
177 178 179 180 181 182 183
   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)) {
184
		if (pslot->hpc_ops->power_off_slot(pslot)) {
185 186
			err("%s: Issue of Slot Power Off command failed\n",
			    __FUNCTION__);
L
Linus Torvalds 已提交
187 188 189 190
			return;
		}
	}

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

194 195 196 197
	if (ATTN_LED(ctrl->ctrlcap)) {
		if (pslot->hpc_ops->set_attention_status(pslot, 1)) {
			err("%s: Issue of Set Attention Led command failed\n",
			    __FUNCTION__);
L
Linus Torvalds 已提交
198 199
			return;
		}
200 201 202 203 204 205
		/*
		 * 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 已提交
206 207 208 209 210 211 212 213 214 215
	}
}

/**
 * board_added - Called after a board has been added to the system.
 *
 * Turns power on for the board
 * Configures board
 *
 */
216
static int board_added(struct slot *p_slot)
L
Linus Torvalds 已提交
217 218
{
	u8 hp_slot;
219
	int retval = 0;
220
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
221

222
	hp_slot = p_slot->device - ctrl->slot_device_offset;
L
Linus Torvalds 已提交
223

224 225 226
	dbg("%s: slot device, slot offset, hp slot = %d, %d ,%d\n",
			__FUNCTION__, p_slot->device,
			ctrl->slot_device_offset, hp_slot);
L
Linus Torvalds 已提交
227 228 229

	if (POWER_CTRL(ctrl->ctrlcap)) {
		/* Power on slot */
230 231 232
		retval = p_slot->hpc_ops->power_on_slot(p_slot);
		if (retval)
			return retval;
L
Linus Torvalds 已提交
233
	}
234

235
	if (PWR_LED(ctrl->ctrlcap))
L
Linus Torvalds 已提交
236 237 238
		p_slot->hpc_ops->green_led_blink(p_slot);

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

241 242 243
	/* Check link training status */
	retval = p_slot->hpc_ops->check_lnk_status(ctrl);
	if (retval) {
L
Linus Torvalds 已提交
244 245
		err("%s: Failed to check link status\n", __FUNCTION__);
		set_slot_off(ctrl, p_slot);
246
		return retval;
L
Linus Torvalds 已提交
247 248 249
	}

	/* Check for a power fault */
250 251
	if (p_slot->hpc_ops->query_power_fault(p_slot)) {
		dbg("%s: power fault detected\n", __FUNCTION__);
252
		retval = POWER_FAILURE;
253
		goto err_exit;
L
Linus Torvalds 已提交
254 255
	}

256 257
	retval = pciehp_configure_device(p_slot);
	if (retval) {
258
		err("Cannot add device 0x%x:%x\n", p_slot->bus,
259
		    p_slot->device);
260 261
		goto err_exit;
	}
L
Linus Torvalds 已提交
262

263 264 265 266 267
	/*
	 * Some PCI Express root ports require fixup after hot-plug operation.
	 */
	if (pcie_mch_quirk)
		pci_fixup_device(pci_fixup_final, ctrl->pci_dev);
268
	if (PWR_LED(ctrl->ctrlcap))
269
  		p_slot->hpc_ops->green_led_on(p_slot);
270

L
Linus Torvalds 已提交
271
	return 0;
272 273 274

err_exit:
	set_slot_off(ctrl, p_slot);
275
	return retval;
L
Linus Torvalds 已提交
276 277 278 279 280 281
}

/**
 * remove_board - Turns off slot and LED's
 *
 */
282
static int remove_board(struct slot *p_slot)
L
Linus Torvalds 已提交
283 284 285
{
	u8 device;
	u8 hp_slot;
286
	int retval = 0;
287
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
288

289 290 291
	retval = pciehp_unconfigure_device(p_slot);
	if (retval)
		return retval;
L
Linus Torvalds 已提交
292

293 294
	device = p_slot->device;
	hp_slot = p_slot->device - ctrl->slot_device_offset;
L
Linus Torvalds 已提交
295 296 297 298 299 300
	p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);

	dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);

	if (POWER_CTRL(ctrl->ctrlcap)) {
		/* power off slot */
301 302 303 304 305
		retval = p_slot->hpc_ops->power_off_slot(p_slot);
		if (retval) {
			err("%s: Issue of Slot Disable command failed\n",
			    __FUNCTION__);
			return retval;
L
Linus Torvalds 已提交
306 307 308
		}
	}

309
	if (PWR_LED(ctrl->ctrlcap))
L
Linus Torvalds 已提交
310 311 312 313 314 315
		/* turn off Green LED */
		p_slot->hpc_ops->green_led_off(p_slot);

	return 0;
}

K
Kenji Kaneshige 已提交
316 317 318 319
struct power_work_info {
	struct slot *p_slot;
	struct work_struct work;
};
L
Linus Torvalds 已提交
320 321 322 323 324 325 326 327

/**
 * pciehp_pushbutton_thread
 *
 * Scheduled procedure to handle blocking stuff for the pushbuttons
 * Handles all pending events and exits.
 *
 */
K
Kenji Kaneshige 已提交
328
static void pciehp_power_thread(struct work_struct *work)
L
Linus Torvalds 已提交
329
{
K
Kenji Kaneshige 已提交
330 331 332 333 334 335 336 337 338 339
	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",
		    __FUNCTION__, p_slot->bus, p_slot->device);
L
Linus Torvalds 已提交
340
		pciehp_disable_slot(p_slot);
K
Kenji Kaneshige 已提交
341
		mutex_lock(&p_slot->lock);
L
Linus Torvalds 已提交
342
		p_slot->state = STATIC_STATE;
K
Kenji Kaneshige 已提交
343 344 345
		break;
	case POWERON_STATE:
		mutex_unlock(&p_slot->lock);
346 347
		if (pciehp_enable_slot(p_slot) &&
		    PWR_LED(p_slot->ctrl->ctrlcap))
L
Linus Torvalds 已提交
348
			p_slot->hpc_ops->green_led_off(p_slot);
K
Kenji Kaneshige 已提交
349
		mutex_lock(&p_slot->lock);
L
Linus Torvalds 已提交
350
		p_slot->state = STATIC_STATE;
K
Kenji Kaneshige 已提交
351 352 353
		break;
	default:
		break;
L
Linus Torvalds 已提交
354
	}
K
Kenji Kaneshige 已提交
355
	mutex_unlock(&p_slot->lock);
L
Linus Torvalds 已提交
356

K
Kenji Kaneshige 已提交
357
	kfree(info);
L
Linus Torvalds 已提交
358 359
}

360
void pciehp_queue_pushbutton_work(struct work_struct *work)
L
Linus Torvalds 已提交
361
{
K
Kenji Kaneshige 已提交
362 363
	struct slot *p_slot = container_of(work, struct slot, work.work);
	struct power_work_info *info;
L
Linus Torvalds 已提交
364

K
Kenji Kaneshige 已提交
365 366 367
	info = kmalloc(sizeof(*info), GFP_KERNEL);
	if (!info) {
		err("%s: Cannot allocate memory\n", __FUNCTION__);
L
Linus Torvalds 已提交
368 369
		return;
	}
K
Kenji Kaneshige 已提交
370 371
	info->p_slot = p_slot;
	INIT_WORK(&info->work, pciehp_power_thread);
L
Linus Torvalds 已提交
372

K
Kenji Kaneshige 已提交
373 374 375
	mutex_lock(&p_slot->lock);
	switch (p_slot->state) {
	case BLINKINGOFF_STATE:
L
Linus Torvalds 已提交
376
		p_slot->state = POWEROFF_STATE;
K
Kenji Kaneshige 已提交
377 378
		break;
	case BLINKINGON_STATE:
L
Linus Torvalds 已提交
379
		p_slot->state = POWERON_STATE;
K
Kenji Kaneshige 已提交
380 381 382
		break;
	default:
		goto out;
L
Linus Torvalds 已提交
383
	}
K
Kenji Kaneshige 已提交
384 385 386
	queue_work(pciehp_wq, &info->work);
 out:
	mutex_unlock(&p_slot->lock);
L
Linus Torvalds 已提交
387 388 389 390 391 392 393
}

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

K
Kenji Kaneshige 已提交
394
	info = kmalloc(sizeof(*info), GFP_KERNEL);
L
Linus Torvalds 已提交
395 396 397 398 399 400 401 402 403 404 405 406 407
	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 已提交
408 409 410 411
/*
 * Note: This function must be called with slot->lock held
 */
static void handle_button_press_event(struct slot *p_slot)
L
Linus Torvalds 已提交
412
{
K
Kenji Kaneshige 已提交
413
	struct controller *ctrl = p_slot->ctrl;
L
Linus Torvalds 已提交
414 415
	u8 getstatus;

K
Kenji Kaneshige 已提交
416 417 418 419 420 421 422 423 424 425 426 427 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 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
	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);
		dbg("%s: button cancel\n", __FUNCTION__);
		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 已提交
472 473 474
	}
}

K
Kenji Kaneshige 已提交
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 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
/*
 * 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) {
		err("%s: Cannot allocate memory\n", __FUNCTION__);
		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 已提交
536 537 538 539 540 541
int pciehp_enable_slot(struct slot *p_slot)
{
	u8 getstatus = 0;
	int rc;

	/* Check to see if (latch closed, card present, power off) */
542
	mutex_lock(&p_slot->ctrl->crit_sect);
L
Linus Torvalds 已提交
543 544 545

	rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
	if (rc || !getstatus) {
546
		info("%s: no adapter on slot(%s)\n", __FUNCTION__,
K
Kenji Kaneshige 已提交
547
		     p_slot->name);
548
		mutex_unlock(&p_slot->ctrl->crit_sect);
549
		return -ENODEV;
L
Linus Torvalds 已提交
550
	}
551
	if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
L
Linus Torvalds 已提交
552 553
		rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
		if (rc || getstatus) {
554
			info("%s: latch open on slot(%s)\n", __FUNCTION__,
K
Kenji Kaneshige 已提交
555
			     p_slot->name);
556
			mutex_unlock(&p_slot->ctrl->crit_sect);
557
			return -ENODEV;
L
Linus Torvalds 已提交
558 559
		}
	}
560 561

	if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
L
Linus Torvalds 已提交
562 563
		rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
		if (rc || getstatus) {
564
			info("%s: already enabled on slot(%s)\n", __FUNCTION__,
K
Kenji Kaneshige 已提交
565
			     p_slot->name);
566
			mutex_unlock(&p_slot->ctrl->crit_sect);
567
			return -EINVAL;
L
Linus Torvalds 已提交
568 569 570 571 572
		}
	}

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

573
	rc = board_added(p_slot);
L
Linus Torvalds 已提交
574 575 576 577
	if (rc) {
		p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
	}

578
	update_slot_info(p_slot);
L
Linus Torvalds 已提交
579

K
Kenji Kaneshige 已提交
580
	mutex_unlock(&p_slot->ctrl->crit_sect);
L
Linus Torvalds 已提交
581 582 583 584 585 586 587 588 589 590 591 592 593
	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) */
594
	mutex_lock(&p_slot->ctrl->crit_sect);
L
Linus Torvalds 已提交
595

596
	if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) {
L
Linus Torvalds 已提交
597 598
		ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
		if (ret || !getstatus) {
599
			info("%s: no adapter on slot(%s)\n", __FUNCTION__,
K
Kenji Kaneshige 已提交
600
			     p_slot->name);
601
			mutex_unlock(&p_slot->ctrl->crit_sect);
602
			return -ENODEV;
L
Linus Torvalds 已提交
603 604 605
		}
	}

606
	if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
L
Linus Torvalds 已提交
607 608
		ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
		if (ret || getstatus) {
609
			info("%s: latch open on slot(%s)\n", __FUNCTION__,
K
Kenji Kaneshige 已提交
610
			     p_slot->name);
611
			mutex_unlock(&p_slot->ctrl->crit_sect);
612
			return -ENODEV;
L
Linus Torvalds 已提交
613 614 615
		}
	}

616
	if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
L
Linus Torvalds 已提交
617 618
		ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
		if (ret || !getstatus) {
619
			info("%s: already disabled slot(%s)\n", __FUNCTION__,
K
Kenji Kaneshige 已提交
620
			     p_slot->name);
621
			mutex_unlock(&p_slot->ctrl->crit_sect);
622
			return -EINVAL;
L
Linus Torvalds 已提交
623
		}
624 625 626 627 628 629
		/*
		 * 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 已提交
630 631
	}

632 633
	ret = remove_board(p_slot);
	update_slot_info(p_slot);
K
Kenji Kaneshige 已提交
634 635

	mutex_unlock(&p_slot->ctrl->crit_sect);
636
	return ret;
L
Linus Torvalds 已提交
637 638
}

K
Kenji Kaneshige 已提交
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 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
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;
}