shpchp_ctrl.c revision f7391f5325ea744f0632f7ef39a90085162743ac
1/*
2 * Standard Hot Plug Controller Driver
3 *
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
8 *
9 * All rights reserved.
10 *
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.
15 *
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
20 * details.
21 *
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.
25 *
26 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
27 *
28 */
29
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>
35#include <linux/workqueue.h>
36#include "../pci.h"
37#include "shpchp.h"
38
39static void interrupt_event_handler(void *data);
40
41static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
42{
43	struct event_info *info;
44
45	info = kmalloc(sizeof(*info), GFP_ATOMIC);
46	if (!info)
47		return -ENOMEM;
48
49	info->event_type = event_type;
50	info->p_slot = p_slot;
51	INIT_WORK(&info->work, interrupt_event_handler, info);
52
53	queue_work(shpchp_wq, &info->work);
54
55	return 0;
56}
57
58u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id)
59{
60	struct controller *ctrl = (struct controller *) inst_id;
61	struct slot *p_slot;
62	u32 event_type;
63
64	/* Attention Button Change */
65	dbg("shpchp:  Attention button interrupt received.\n");
66
67	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
68	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
69
70	/*
71	 *  Button pressed - See if need to TAKE ACTION!!!
72	 */
73	info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot);
74	event_type = INT_BUTTON_PRESS;
75
76	if ((p_slot->state == BLINKINGON_STATE)
77	    || (p_slot->state == BLINKINGOFF_STATE)) {
78		/* Cancel if we are still blinking; this means that we press the
79		 * attention again before the 5 sec. limit expires to cancel hot-add
80		 * or hot-remove
81		 */
82		event_type = INT_BUTTON_CANCEL;
83		info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot);
84	} else if ((p_slot->state == POWERON_STATE)
85		   || (p_slot->state == POWEROFF_STATE)) {
86		/* Ignore if the slot is on power-on or power-off state; this
87		 * means that the previous attention button action to hot-add or
88		 * hot-remove is undergoing
89		 */
90		event_type = INT_BUTTON_IGNORE;
91		info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot);
92	}
93
94	queue_interrupt_event(p_slot, event_type);
95
96	return 0;
97
98}
99
100u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id)
101{
102	struct controller *ctrl = (struct controller *) inst_id;
103	struct slot *p_slot;
104	u8 getstatus;
105	u32 event_type;
106
107	/* Switch Change */
108	dbg("shpchp:  Switch interrupt received.\n");
109
110	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
111	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
112	p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
113	dbg("%s: Card present %x Power status %x\n", __FUNCTION__,
114		p_slot->presence_save, p_slot->pwr_save);
115
116	if (getstatus) {
117		/*
118		 * Switch opened
119		 */
120		info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot);
121		event_type = INT_SWITCH_OPEN;
122		if (p_slot->pwr_save && p_slot->presence_save) {
123			event_type = INT_POWER_FAULT;
124			err("Surprise Removal of card\n");
125		}
126	} else {
127		/*
128		 *  Switch closed
129		 */
130		info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot);
131		event_type = INT_SWITCH_CLOSE;
132	}
133
134	queue_interrupt_event(p_slot, event_type);
135
136	return 1;
137}
138
139u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id)
140{
141	struct controller *ctrl = (struct controller *) inst_id;
142	struct slot *p_slot;
143	u32 event_type;
144
145	/* Presence Change */
146	dbg("shpchp:  Presence/Notify input change.\n");
147
148	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
149
150	/*
151	 * Save the presence state
152	 */
153	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
154	if (p_slot->presence_save) {
155		/*
156		 * Card Present
157		 */
158		info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot);
159		event_type = INT_PRESENCE_ON;
160	} else {
161		/*
162		 * Not Present
163		 */
164		info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot);
165		event_type = INT_PRESENCE_OFF;
166	}
167
168	queue_interrupt_event(p_slot, event_type);
169
170	return 1;
171}
172
173u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id)
174{
175	struct controller *ctrl = (struct controller *) inst_id;
176	struct slot *p_slot;
177	u32 event_type;
178
179	/* Power fault */
180	dbg("shpchp:  Power fault interrupt received.\n");
181
182	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
183
184	if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
185		/*
186		 * Power fault Cleared
187		 */
188		info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot);
189		p_slot->status = 0x00;
190		event_type = INT_POWER_FAULT_CLEAR;
191	} else {
192		/*
193		 *   Power fault
194		 */
195		info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot);
196		event_type = INT_POWER_FAULT;
197		/* set power fault status for this board */
198		p_slot->status = 0xFF;
199		info("power fault bit %x set\n", hp_slot);
200	}
201
202	queue_interrupt_event(p_slot, event_type);
203
204	return 1;
205}
206
207/* The following routines constitute the bulk of the
208   hotplug controller logic
209 */
210static int change_bus_speed(struct controller *ctrl, struct slot *p_slot,
211		enum pci_bus_speed speed)
212{
213	int rc = 0;
214
215	dbg("%s: change to speed %d\n", __FUNCTION__, speed);
216	if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) {
217		err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
218		return WRONG_BUS_FREQUENCY;
219	}
220	return rc;
221}
222
223static int fix_bus_speed(struct controller *ctrl, struct slot *pslot,
224		u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp,
225		enum pci_bus_speed msp)
226{
227	int rc = 0;
228
229	if (flag != 0) { /* Other slots on the same bus are occupied */
230		if ( asp < bsp ) {
231			err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bsp, asp);
232			return WRONG_BUS_FREQUENCY;
233		}
234	} else {
235		/* Other slots on the same bus are empty */
236		if (msp == bsp) {
237		/* if adapter_speed >= bus_speed, do nothing */
238			if (asp < bsp) {
239				/*
240				* Try to lower bus speed to accommodate the adapter if other slots
241				* on the same controller are empty
242				*/
243				if ((rc = change_bus_speed(ctrl, pslot, asp)))
244					return rc;
245			}
246		} else {
247			if (asp < msp) {
248				if ((rc = change_bus_speed(ctrl, pslot, asp)))
249					return rc;
250			} else {
251				if ((rc = change_bus_speed(ctrl, pslot, msp)))
252					return rc;
253			}
254		}
255	}
256	return rc;
257}
258
259/**
260 * board_added - Called after a board has been added to the system.
261 *
262 * Turns power on for the board
263 * Configures board
264 *
265 */
266static int board_added(struct slot *p_slot)
267{
268	u8 hp_slot;
269	u8 slots_not_empty = 0;
270	int rc = 0;
271	enum pci_bus_speed adapter_speed, bus_speed, max_bus_speed;
272	u8 pi, mode;
273	struct controller *ctrl = p_slot->ctrl;
274
275	hp_slot = p_slot->device - ctrl->slot_device_offset;
276
277	dbg("%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n",
278			__FUNCTION__, p_slot->device,
279			ctrl->slot_device_offset, hp_slot);
280
281	/* Power on slot without connecting to bus */
282	rc = p_slot->hpc_ops->power_on_slot(p_slot);
283	if (rc) {
284		err("%s: Failed to power on slot\n", __FUNCTION__);
285		return -1;
286	}
287
288	if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {
289		if (slots_not_empty)
290			return WRONG_BUS_FREQUENCY;
291
292		if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) {
293			err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
294			return WRONG_BUS_FREQUENCY;
295		}
296
297		/* turn on board, blink green LED, turn off Amber LED */
298		if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
299			err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
300			return rc;
301		}
302	}
303
304	rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &adapter_speed);
305	/* 0 = PCI 33Mhz, 1 = PCI 66 Mhz, 2 = PCI-X 66 PA, 4 = PCI-X 66 ECC, */
306	/* 5 = PCI-X 133 PA, 7 = PCI-X 133 ECC,  0xa = PCI-X 133 Mhz 266, */
307	/* 0xd = PCI-X 133 Mhz 533 */
308	/* This encoding is different from the one used in cur_bus_speed & */
309	/* max_bus_speed */
310
311	if (rc  || adapter_speed == PCI_SPEED_UNKNOWN) {
312		err("%s: Can't get adapter speed or bus mode mismatch\n", __FUNCTION__);
313		return WRONG_BUS_FREQUENCY;
314	}
315
316	rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bus_speed);
317	if (rc || bus_speed == PCI_SPEED_UNKNOWN) {
318		err("%s: Can't get bus operation speed\n", __FUNCTION__);
319		return WRONG_BUS_FREQUENCY;
320	}
321
322	rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &max_bus_speed);
323	if (rc || max_bus_speed == PCI_SPEED_UNKNOWN) {
324		err("%s: Can't get max bus operation speed\n", __FUNCTION__);
325		max_bus_speed = bus_speed;
326	}
327
328	if ((rc  = p_slot->hpc_ops->get_prog_int(p_slot, &pi))) {
329		err("%s: Can't get controller programming interface, set it to 1\n", __FUNCTION__);
330		pi = 1;
331	}
332
333	/* Check if there are other slots or devices on the same bus */
334	if (!list_empty(&ctrl->pci_dev->subordinate->devices))
335		slots_not_empty = 1;
336
337	dbg("%s: slots_not_empty %d, pi %d\n", __FUNCTION__,
338		slots_not_empty, pi);
339	dbg("adapter_speed %d, bus_speed %d, max_bus_speed %d\n",
340		adapter_speed, bus_speed, max_bus_speed);
341
342	if (pi == 2) {
343		dbg("%s: In PI = %d\n", __FUNCTION__, pi);
344		if ((rc = p_slot->hpc_ops->get_mode1_ECC_cap(p_slot, &mode))) {
345			err("%s: Can't get Mode1_ECC, set mode to 0\n", __FUNCTION__);
346			mode = 0;
347		}
348
349		switch (adapter_speed) {
350		case PCI_SPEED_133MHz_PCIX_533:
351		case PCI_SPEED_133MHz_PCIX_266:
352			if ((bus_speed != adapter_speed) &&
353			   ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
354				return rc;
355			break;
356		case PCI_SPEED_133MHz_PCIX_ECC:
357		case PCI_SPEED_133MHz_PCIX:
358			if (mode) { /* Bus - Mode 1 ECC */
359				if ((bus_speed != 0x7) &&
360				   ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
361					return rc;
362			} else {
363				if ((bus_speed != 0x4) &&
364				   ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
365					return rc;
366			}
367			break;
368		case PCI_SPEED_66MHz_PCIX_ECC:
369		case PCI_SPEED_66MHz_PCIX:
370			if (mode) { /* Bus - Mode 1 ECC */
371				if ((bus_speed != 0x5) &&
372				   ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
373					return rc;
374			} else {
375				if ((bus_speed != 0x2) &&
376				   ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
377					return rc;
378			}
379			break;
380		case PCI_SPEED_66MHz:
381			if ((bus_speed != 0x1) &&
382			   ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
383				return rc;
384			break;
385		case PCI_SPEED_33MHz:
386			if (bus_speed > 0x0) {
387				if (slots_not_empty == 0) {
388					if ((rc = change_bus_speed(ctrl, p_slot, adapter_speed)))
389						return rc;
390				} else {
391					err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
392					return WRONG_BUS_FREQUENCY;
393				}
394			}
395			break;
396		default:
397			err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
398			return WRONG_BUS_FREQUENCY;
399		}
400	} else {
401		/* If adpater_speed == bus_speed, nothing to do here */
402		dbg("%s: In PI = %d\n", __FUNCTION__, pi);
403		if ((adapter_speed != bus_speed) &&
404		   ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
405				return rc;
406	}
407
408	/* turn on board, blink green LED, turn off Amber LED */
409	if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
410		err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
411		return rc;
412	}
413
414	/* Wait for ~1 second */
415	msleep(1000);
416
417	dbg("%s: slot status = %x\n", __FUNCTION__, p_slot->status);
418	/* Check for a power fault */
419	if (p_slot->status == 0xFF) {
420		/* power fault occurred, but it was benign */
421		dbg("%s: power fault\n", __FUNCTION__);
422		rc = POWER_FAILURE;
423		p_slot->status = 0;
424		goto err_exit;
425	}
426
427	if (shpchp_configure_device(p_slot)) {
428		err("Cannot add device at 0x%x:0x%x\n", p_slot->bus,
429				p_slot->device);
430		goto err_exit;
431	}
432
433	p_slot->status = 0;
434	p_slot->is_a_board = 0x01;
435	p_slot->pwr_save = 1;
436
437	p_slot->hpc_ops->green_led_on(p_slot);
438
439	return 0;
440
441err_exit:
442	/* turn off slot, turn on Amber LED, turn off Green LED */
443	rc = p_slot->hpc_ops->slot_disable(p_slot);
444	if (rc) {
445		err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
446		return rc;
447	}
448
449	return(rc);
450}
451
452
453/**
454 * remove_board - Turns off slot and LED's
455 *
456 */
457static int remove_board(struct slot *p_slot)
458{
459	struct controller *ctrl = p_slot->ctrl;
460	u8 hp_slot;
461	int rc;
462
463	if (shpchp_unconfigure_device(p_slot))
464		return(1);
465
466	hp_slot = p_slot->device - ctrl->slot_device_offset;
467	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
468
469	dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
470
471	/* Change status to shutdown */
472	if (p_slot->is_a_board)
473		p_slot->status = 0x01;
474
475	/* turn off slot, turn on Amber LED, turn off Green LED */
476	rc = p_slot->hpc_ops->slot_disable(p_slot);
477	if (rc) {
478		err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
479		return rc;
480	}
481
482	rc = p_slot->hpc_ops->set_attention_status(p_slot, 0);
483	if (rc) {
484		err("%s: Issue of Set Attention command failed\n", __FUNCTION__);
485		return rc;
486	}
487
488	p_slot->pwr_save = 0;
489	p_slot->is_a_board = 0;
490
491	return 0;
492}
493
494
495/**
496 * shpchp_pushbutton_thread
497 *
498 * Scheduled procedure to handle blocking stuff for the pushbuttons
499 * Handles all pending events and exits.
500 *
501 */
502void shpchp_pushbutton_thread(void *data)
503{
504	struct slot *p_slot = data;
505	u8 getstatus;
506
507	p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
508	if (getstatus) {
509		p_slot->state = POWEROFF_STATE;
510		shpchp_disable_slot(p_slot);
511		p_slot->state = STATIC_STATE;
512	} else {
513		p_slot->state = POWERON_STATE;
514		if (shpchp_enable_slot(p_slot))
515			p_slot->hpc_ops->green_led_off(p_slot);
516		p_slot->state = STATIC_STATE;
517	}
518}
519
520static int update_slot_info (struct slot *slot)
521{
522	struct hotplug_slot_info *info;
523	int result;
524
525	info = kmalloc(sizeof(*info), GFP_KERNEL);
526	if (!info)
527		return -ENOMEM;
528
529	slot->hpc_ops->get_power_status(slot, &(info->power_status));
530	slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
531	slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
532	slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
533
534	result = pci_hp_change_slot_info(slot->hotplug_slot, info);
535	kfree (info);
536	return result;
537}
538
539static void interrupt_event_handler(void *data)
540{
541	struct event_info *info = data;
542	struct slot *p_slot = info->p_slot;
543	u8 getstatus;
544
545	switch (info->event_type) {
546	case INT_BUTTON_CANCEL:
547		dbg("%s: button cancel\n", __FUNCTION__);
548		cancel_delayed_work(&p_slot->work);
549		switch (p_slot->state) {
550		case BLINKINGOFF_STATE:
551			p_slot->hpc_ops->green_led_on(p_slot);
552			p_slot->hpc_ops->set_attention_status(p_slot, 0);
553			break;
554		case BLINKINGON_STATE:
555			p_slot->hpc_ops->green_led_off(p_slot);
556			p_slot->hpc_ops->set_attention_status(p_slot, 0);
557			break;
558		default:
559			warn("Not a valid state\n");
560			return;
561		}
562		info(msg_button_cancel, p_slot->number);
563		p_slot->state = STATIC_STATE;
564		break;
565	case INT_BUTTON_PRESS:
566		dbg("%s: Button pressed\n", __FUNCTION__);
567		p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
568		if (getstatus) {
569			p_slot->state = BLINKINGOFF_STATE;
570			info(msg_button_off, p_slot->number);
571		} else {
572			p_slot->state = BLINKINGON_STATE;
573			info(msg_button_on, p_slot->number);
574		}
575		/* blink green LED and turn off amber */
576		p_slot->hpc_ops->green_led_blink(p_slot);
577		p_slot->hpc_ops->set_attention_status(p_slot, 0);
578
579		queue_delayed_work(shpchp_wq, &p_slot->work, 5*HZ);
580		break;
581	case INT_POWER_FAULT:
582		dbg("%s: power fault\n", __FUNCTION__);
583		p_slot->hpc_ops->set_attention_status(p_slot, 1);
584		p_slot->hpc_ops->green_led_off(p_slot);
585		break;
586	default:
587		update_slot_info(p_slot);
588		break;
589	}
590
591	kfree(info);
592}
593
594
595int shpchp_enable_slot (struct slot *p_slot)
596{
597	u8 getstatus = 0;
598	int rc, retval = -ENODEV;
599
600	/* Check to see if (latch closed, card present, power off) */
601	mutex_lock(&p_slot->ctrl->crit_sect);
602	rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
603	if (rc || !getstatus) {
604		info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
605		goto out;
606	}
607	rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
608	if (rc || getstatus) {
609		info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
610		goto out;
611	}
612	rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
613	if (rc || getstatus) {
614		info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number);
615		goto out;
616	}
617
618	p_slot->is_a_board = 1;
619
620	/* We have to save the presence info for these slots */
621	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
622	p_slot->hpc_ops->get_power_status(p_slot, &(p_slot->pwr_save));
623	dbg("%s: p_slot->pwr_save %x\n", __FUNCTION__, p_slot->pwr_save);
624	p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
625
626	if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) ||
627	    (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458))
628	     && p_slot->ctrl->num_slots == 1) {
629		/* handle amd pogo errata; this must be done before enable  */
630		amd_pogo_errata_save_misc_reg(p_slot);
631		retval = board_added(p_slot);
632		/* handle amd pogo errata; this must be done after enable  */
633		amd_pogo_errata_restore_misc_reg(p_slot);
634	} else
635		retval = board_added(p_slot);
636
637	if (retval) {
638		p_slot->hpc_ops->get_adapter_status(p_slot,
639				&(p_slot->presence_save));
640		p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
641	}
642
643	update_slot_info(p_slot);
644 out:
645	mutex_unlock(&p_slot->ctrl->crit_sect);
646	return retval;
647}
648
649
650int shpchp_disable_slot (struct slot *p_slot)
651{
652	u8 getstatus = 0;
653	int rc, retval = -ENODEV;
654
655	if (!p_slot->ctrl)
656		return -ENODEV;
657
658	/* Check to see if (latch closed, card present, power on) */
659	mutex_lock(&p_slot->ctrl->crit_sect);
660
661	rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
662	if (rc || !getstatus) {
663		info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
664		goto out;
665	}
666	rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
667	if (rc || getstatus) {
668		info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
669		goto out;
670	}
671	rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
672	if (rc || !getstatus) {
673		info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number);
674		goto out;
675	}
676
677	retval = remove_board(p_slot);
678	update_slot_info(p_slot);
679 out:
680	mutex_unlock(&p_slot->ctrl->crit_sect);
681	return retval;
682}
683
684