shpchp_ctrl.c 18.3 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
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
/*
 * Standard 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>
Linus Torvalds's avatar
Linus Torvalds committed
27
28
29
30
31
32
 *
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
33
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
34
#include <linux/pci.h>
35
#include "../pci.h"
Linus Torvalds's avatar
Linus Torvalds committed
36
37
#include "shpchp.h"

David Howells's avatar
David Howells committed
38
static void interrupt_event_handler(struct work_struct *work);
39
40
static int shpchp_enable_slot(struct slot *p_slot);
static int shpchp_disable_slot(struct slot *p_slot);
Linus Torvalds's avatar
Linus Torvalds committed
41

42
43
44
45
46
47
48
49
50
51
static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
{
	struct event_info *info;

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

	info->event_type = event_type;
	info->p_slot = p_slot;
David Howells's avatar
David Howells committed
52
	INIT_WORK(&info->work, interrupt_event_handler);
53

54
	queue_work(p_slot->wq, &info->work);
55
56
57

	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
58

59
u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
Linus Torvalds's avatar
Linus Torvalds committed
60
61
{
	struct slot *p_slot;
62
	u32 event_type;
Linus Torvalds's avatar
Linus Torvalds committed
63
64

	/* Attention Button Change */
65
	ctrl_dbg(ctrl, "Attention button interrupt received\n");
66

Linus Torvalds's avatar
Linus Torvalds committed
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));
Linus Torvalds's avatar
Linus Torvalds committed
69
70
71
72

	/*
	 *  Button pressed - See if need to TAKE ACTION!!!
	 */
73
	ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot));
74
	event_type = INT_BUTTON_PRESS;
Linus Torvalds's avatar
Linus Torvalds committed
75

76
	queue_interrupt_event(p_slot, event_type);
Linus Torvalds's avatar
Linus Torvalds committed
77
78
79
80
81

	return 0;

}

82
u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
Linus Torvalds's avatar
Linus Torvalds committed
83
84
85
{
	struct slot *p_slot;
	u8 getstatus;
86
	u32 event_type;
Linus Torvalds's avatar
Linus Torvalds committed
87
88

	/* Switch Change */
89
	ctrl_dbg(ctrl, "Switch interrupt received\n");
Linus Torvalds's avatar
Linus Torvalds committed
90
91

	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
92
	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
Linus Torvalds's avatar
Linus Torvalds committed
93
	p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
94
95
	ctrl_dbg(ctrl, "Card present %x Power status %x\n",
		 p_slot->presence_save, p_slot->pwr_save);
Linus Torvalds's avatar
Linus Torvalds committed
96
97
98
99
100

	if (getstatus) {
		/*
		 * Switch opened
		 */
101
		ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot));
102
		event_type = INT_SWITCH_OPEN;
103
		if (p_slot->pwr_save && p_slot->presence_save) {
104
			event_type = INT_POWER_FAULT;
105
			ctrl_err(ctrl, "Surprise Removal of card\n");
Linus Torvalds's avatar
Linus Torvalds committed
106
107
108
109
110
		}
	} else {
		/*
		 *  Switch closed
		 */
111
		ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot));
112
		event_type = INT_SWITCH_CLOSE;
Linus Torvalds's avatar
Linus Torvalds committed
113
114
	}

115
	queue_interrupt_event(p_slot, event_type);
Linus Torvalds's avatar
Linus Torvalds committed
116

117
	return 1;
Linus Torvalds's avatar
Linus Torvalds committed
118
119
}

120
u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
Linus Torvalds's avatar
Linus Torvalds committed
121
122
{
	struct slot *p_slot;
123
	u32 event_type;
Linus Torvalds's avatar
Linus Torvalds committed
124
125

	/* Presence Change */
126
	ctrl_dbg(ctrl, "Presence/Notify input change\n");
Linus Torvalds's avatar
Linus Torvalds committed
127
128
129

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

130
	/*
Linus Torvalds's avatar
Linus Torvalds committed
131
132
	 * Save the presence state
	 */
133
134
	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
	if (p_slot->presence_save) {
Linus Torvalds's avatar
Linus Torvalds committed
135
136
137
		/*
		 * Card Present
		 */
138
139
		ctrl_info(ctrl, "Card present on Slot(%s)\n",
			  slot_name(p_slot));
140
		event_type = INT_PRESENCE_ON;
Linus Torvalds's avatar
Linus Torvalds committed
141
142
143
144
	} else {
		/*
		 * Not Present
		 */
145
146
		ctrl_info(ctrl, "Card not present on Slot(%s)\n",
			  slot_name(p_slot));
147
		event_type = INT_PRESENCE_OFF;
Linus Torvalds's avatar
Linus Torvalds committed
148
149
	}

150
	queue_interrupt_event(p_slot, event_type);
Linus Torvalds's avatar
Linus Torvalds committed
151

152
	return 1;
Linus Torvalds's avatar
Linus Torvalds committed
153
154
}

155
u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
Linus Torvalds's avatar
Linus Torvalds committed
156
157
{
	struct slot *p_slot;
158
	u32 event_type;
Linus Torvalds's avatar
Linus Torvalds committed
159
160

	/* Power fault */
161
	ctrl_dbg(ctrl, "Power fault interrupt received\n");
Linus Torvalds's avatar
Linus Torvalds committed
162
163
164
165
166
167
168

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

	if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
		/*
		 * Power fault Cleared
		 */
169
170
		ctrl_info(ctrl, "Power fault cleared on Slot(%s)\n",
			  slot_name(p_slot));
171
		p_slot->status = 0x00;
172
		event_type = INT_POWER_FAULT_CLEAR;
Linus Torvalds's avatar
Linus Torvalds committed
173
174
175
176
	} else {
		/*
		 *   Power fault
		 */
177
		ctrl_info(ctrl, "Power fault on Slot(%s)\n", slot_name(p_slot));
178
		event_type = INT_POWER_FAULT;
Linus Torvalds's avatar
Linus Torvalds committed
179
		/* set power fault status for this board */
180
		p_slot->status = 0xFF;
181
		ctrl_info(ctrl, "Power fault bit %x set\n", hp_slot);
Linus Torvalds's avatar
Linus Torvalds committed
182
183
	}

184
185
186
	queue_interrupt_event(p_slot, event_type);

	return 1;
Linus Torvalds's avatar
Linus Torvalds committed
187
188
}

189
/* The following routines constitute the bulk of the
Linus Torvalds's avatar
Linus Torvalds committed
190
191
   hotplug controller logic
 */
192
193
static int change_bus_speed(struct controller *ctrl, struct slot *p_slot,
		enum pci_bus_speed speed)
194
{
195
	int rc = 0;
Linus Torvalds's avatar
Linus Torvalds committed
196

197
	ctrl_dbg(ctrl, "Change speed to %d\n", speed);
Linus Torvalds's avatar
Linus Torvalds committed
198
	if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) {
199
200
		ctrl_err(ctrl, "%s: Issue of set bus speed mode command "
			 "failed\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
201
202
203
204
205
		return WRONG_BUS_FREQUENCY;
	}
	return rc;
}

206
207
208
static int fix_bus_speed(struct controller *ctrl, struct slot *pslot,
		u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp,
		enum pci_bus_speed msp)
209
{
210
	int rc = 0;
211
212
213
214
215
216
217

	/*
	 * If other slots on the same bus are occupied, we cannot
	 * change the bus speed.
	 */
	if (flag) {
		if (asp < bsp) {
218
219
			ctrl_err(ctrl, "Speed of bus %x and adapter %x "
				 "mismatch\n", bsp, asp);
220
			rc = WRONG_BUS_FREQUENCY;
Linus Torvalds's avatar
Linus Torvalds committed
221
		}
222
223
224
225
226
227
		return rc;
	}

	if (asp < msp) {
		if (bsp != asp)
			rc = change_bus_speed(ctrl, pslot, asp);
Linus Torvalds's avatar
Linus Torvalds committed
228
	} else {
229
230
		if (bsp != msp)
			rc = change_bus_speed(ctrl, pslot, msp);
Linus Torvalds's avatar
Linus Torvalds committed
231
232
233
234
235
236
	}
	return rc;
}

/**
 * board_added - Called after a board has been added to the system.
Randy Dunlap's avatar
Randy Dunlap committed
237
 * @p_slot: target &slot
Linus Torvalds's avatar
Linus Torvalds committed
238
 *
Randy Dunlap's avatar
Randy Dunlap committed
239
240
 * Turns power on for the board.
 * Configures board.
Linus Torvalds's avatar
Linus Torvalds committed
241
 */
242
static int board_added(struct slot *p_slot)
Linus Torvalds's avatar
Linus Torvalds committed
243
244
245
{
	u8 hp_slot;
	u8 slots_not_empty = 0;
246
	int rc = 0;
247
	enum pci_bus_speed asp, bsp, msp;
248
	struct controller *ctrl = p_slot->ctrl;
249
	struct pci_bus *parent = ctrl->pci_dev->subordinate;
Linus Torvalds's avatar
Linus Torvalds committed
250

251
	hp_slot = p_slot->device - ctrl->slot_device_offset;
Linus Torvalds's avatar
Linus Torvalds committed
252

253
254
255
	ctrl_dbg(ctrl,
		 "%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n",
		 __func__, p_slot->device, ctrl->slot_device_offset, hp_slot);
Linus Torvalds's avatar
Linus Torvalds committed
256
257
258
259

	/* Power on slot without connecting to bus */
	rc = p_slot->hpc_ops->power_on_slot(p_slot);
	if (rc) {
260
		ctrl_err(ctrl, "Failed to power on slot\n");
Linus Torvalds's avatar
Linus Torvalds committed
261
262
		return -1;
	}
263

Linus Torvalds's avatar
Linus Torvalds committed
264
265
	if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {
		if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) {
266
267
			ctrl_err(ctrl, "%s: Issue of set bus speed mode command"
				 " failed\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
268
269
			return WRONG_BUS_FREQUENCY;
		}
270

Linus Torvalds's avatar
Linus Torvalds committed
271
272
		/* turn on board, blink green LED, turn off Amber LED */
		if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
273
			ctrl_err(ctrl, "Issue of Slot Enable command failed\n");
Linus Torvalds's avatar
Linus Torvalds committed
274
275
276
			return rc;
		}
	}
277

278
279
	rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &asp);
	if (rc) {
280
281
		ctrl_err(ctrl, "Can't get adapter speed or "
			 "bus mode mismatch\n");
Linus Torvalds's avatar
Linus Torvalds committed
282
283
284
		return WRONG_BUS_FREQUENCY;
	}

285
286
	bsp = ctrl->pci_dev->bus->cur_bus_speed;
	msp = ctrl->pci_dev->bus->max_bus_speed;
Linus Torvalds's avatar
Linus Torvalds committed
287
288
289
290
291

	/* Check if there are other slots or devices on the same bus */
	if (!list_empty(&ctrl->pci_dev->subordinate->devices))
		slots_not_empty = 1;

292
293
294
	ctrl_dbg(ctrl, "%s: slots_not_empty %d, adapter_speed %d, bus_speed %d,"
		 " max_bus_speed %d\n", __func__, slots_not_empty, asp,
		 bsp, msp);
Linus Torvalds's avatar
Linus Torvalds committed
295

296
297
298
	rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp);
	if (rc)
		return rc;
Linus Torvalds's avatar
Linus Torvalds committed
299
300
301

	/* turn on board, blink green LED, turn off Amber LED */
	if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
302
		ctrl_err(ctrl, "Issue of Slot Enable command failed\n");
Linus Torvalds's avatar
Linus Torvalds committed
303
304
305
306
		return rc;
	}

	/* Wait for ~1 second */
307
	msleep(1000);
Linus Torvalds's avatar
Linus Torvalds committed
308

309
	ctrl_dbg(ctrl, "%s: slot status = %x\n", __func__, p_slot->status);
Linus Torvalds's avatar
Linus Torvalds committed
310
	/* Check for a power fault */
311
	if (p_slot->status == 0xFF) {
Linus Torvalds's avatar
Linus Torvalds committed
312
		/* power fault occurred, but it was benign */
313
		ctrl_dbg(ctrl, "%s: Power fault\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
314
		rc = POWER_FAILURE;
315
		p_slot->status = 0;
316
		goto err_exit;
Linus Torvalds's avatar
Linus Torvalds committed
317
318
	}

319
	if (shpchp_configure_device(p_slot)) {
320
321
		ctrl_err(ctrl, "Cannot add device at %04x:%02x:%02x\n",
			 pci_domain_nr(parent), p_slot->bus, p_slot->device);
322
323
		goto err_exit;
	}
Linus Torvalds's avatar
Linus Torvalds committed
324

325
326
327
	p_slot->status = 0;
	p_slot->is_a_board = 0x01;
	p_slot->pwr_save = 1;
Linus Torvalds's avatar
Linus Torvalds committed
328

329
	p_slot->hpc_ops->green_led_on(p_slot);
Linus Torvalds's avatar
Linus Torvalds committed
330

331
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
332

333
334
335
336
err_exit:
	/* turn off slot, turn on Amber LED, turn off Green LED */
	rc = p_slot->hpc_ops->slot_disable(p_slot);
	if (rc) {
337
338
		ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n",
			 __func__);
339
340
		return rc;
	}
Linus Torvalds's avatar
Linus Torvalds committed
341

342
	return(rc);
Linus Torvalds's avatar
Linus Torvalds committed
343
344
345
346
}


/**
Randy Dunlap's avatar
Randy Dunlap committed
347
348
 * remove_board - Turns off slot and LEDs
 * @p_slot: target &slot
Linus Torvalds's avatar
Linus Torvalds committed
349
 */
350
static int remove_board(struct slot *p_slot)
Linus Torvalds's avatar
Linus Torvalds committed
351
{
352
	struct controller *ctrl = p_slot->ctrl;
Linus Torvalds's avatar
Linus Torvalds committed
353
	u8 hp_slot;
354
	int rc;
355

356
	if (shpchp_unconfigure_device(p_slot))
Linus Torvalds's avatar
Linus Torvalds committed
357
358
		return(1);

359
	hp_slot = p_slot->device - ctrl->slot_device_offset;
Linus Torvalds's avatar
Linus Torvalds committed
360
361
	p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);

362
	ctrl_dbg(ctrl, "%s: hp_slot = %d\n", __func__, hp_slot);
Linus Torvalds's avatar
Linus Torvalds committed
363
364

	/* Change status to shutdown */
365
366
	if (p_slot->is_a_board)
		p_slot->status = 0x01;
Linus Torvalds's avatar
Linus Torvalds committed
367
368
369
370

	/* turn off slot, turn on Amber LED, turn off Green LED */
	rc = p_slot->hpc_ops->slot_disable(p_slot);
	if (rc) {
371
372
		ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n",
			 __func__);
Linus Torvalds's avatar
Linus Torvalds committed
373
374
		return rc;
	}
375

Linus Torvalds's avatar
Linus Torvalds committed
376
377
	rc = p_slot->hpc_ops->set_attention_status(p_slot, 0);
	if (rc) {
378
		ctrl_err(ctrl, "Issue of Set Attention command failed\n");
Linus Torvalds's avatar
Linus Torvalds committed
379
380
381
		return rc;
	}

382
383
	p_slot->pwr_save = 0;
	p_slot->is_a_board = 0;
Linus Torvalds's avatar
Linus Torvalds committed
384
385
386
387
388

	return 0;
}


389
390
391
392
393
struct pushbutton_work_info {
	struct slot *p_slot;
	struct work_struct work;
};

Linus Torvalds's avatar
Linus Torvalds committed
394
/**
Randy Dunlap's avatar
Randy Dunlap committed
395
396
 * shpchp_pushbutton_thread - handle pushbutton events
 * @work: &struct work_struct to be handled
Linus Torvalds's avatar
Linus Torvalds committed
397
 *
Randy Dunlap's avatar
Randy Dunlap committed
398
 * Scheduled procedure to handle blocking stuff for the pushbuttons.
Linus Torvalds's avatar
Linus Torvalds committed
399
400
 * Handles all pending events and exits.
 */
David Howells's avatar
David Howells committed
401
static void shpchp_pushbutton_thread(struct work_struct *work)
Linus Torvalds's avatar
Linus Torvalds committed
402
{
David Howells's avatar
David Howells committed
403
404
	struct pushbutton_work_info *info =
		container_of(work, struct pushbutton_work_info, work);
405
	struct slot *p_slot = info->p_slot;
Linus Torvalds's avatar
Linus Torvalds committed
406

407
408
409
410
	mutex_lock(&p_slot->lock);
	switch (p_slot->state) {
	case POWEROFF_STATE:
		mutex_unlock(&p_slot->lock);
Linus Torvalds's avatar
Linus Torvalds committed
411
		shpchp_disable_slot(p_slot);
412
		mutex_lock(&p_slot->lock);
Linus Torvalds's avatar
Linus Torvalds committed
413
		p_slot->state = STATIC_STATE;
414
415
416
		break;
	case POWERON_STATE:
		mutex_unlock(&p_slot->lock);
417
		if (shpchp_enable_slot(p_slot))
Linus Torvalds's avatar
Linus Torvalds committed
418
			p_slot->hpc_ops->green_led_off(p_slot);
419
		mutex_lock(&p_slot->lock);
Linus Torvalds's avatar
Linus Torvalds committed
420
		p_slot->state = STATIC_STATE;
421
422
423
424
425
426
427
428
429
		break;
	default:
		break;
	}
	mutex_unlock(&p_slot->lock);

	kfree(info);
}

430
void shpchp_queue_pushbutton_work(struct work_struct *work)
431
{
David Howells's avatar
David Howells committed
432
	struct slot *p_slot = container_of(work, struct slot, work.work);
433
434
435
436
	struct pushbutton_work_info *info;

	info = kmalloc(sizeof(*info), GFP_KERNEL);
	if (!info) {
437
438
		ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
			 __func__);
439
440
441
		return;
	}
	info->p_slot = p_slot;
David Howells's avatar
David Howells committed
442
	INIT_WORK(&info->work, shpchp_pushbutton_thread);
443
444
445
446
447
448
449
450
451
452

	mutex_lock(&p_slot->lock);
	switch (p_slot->state) {
	case BLINKINGOFF_STATE:
		p_slot->state = POWEROFF_STATE;
		break;
	case BLINKINGON_STATE:
		p_slot->state = POWERON_STATE;
		break;
	default:
Jiri Slaby's avatar
Jiri Slaby committed
453
		kfree(info);
454
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
455
	}
456
	queue_work(p_slot->wq, &info->work);
457
458
 out:
	mutex_unlock(&p_slot->lock);
Linus Torvalds's avatar
Linus Torvalds committed
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
}

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

	info = kmalloc(sizeof(*info), GFP_KERNEL);
	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;
}

480
481
482
483
/*
 * Note: This function must be called with slot->lock held
 */
static void handle_button_press_event(struct slot *p_slot)
Linus Torvalds's avatar
Linus Torvalds committed
484
485
{
	u8 getstatus;
486
	struct controller *ctrl = p_slot->ctrl;
Linus Torvalds's avatar
Linus Torvalds committed
487

488
489
	switch (p_slot->state) {
	case STATIC_STATE:
490
491
492
		p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
		if (getstatus) {
			p_slot->state = BLINKINGOFF_STATE;
493
494
			ctrl_info(ctrl, "PCI slot #%s - powering off due to "
				  "button press.\n", slot_name(p_slot));
495
496
		} else {
			p_slot->state = BLINKINGON_STATE;
497
498
			ctrl_info(ctrl, "PCI slot #%s - powering on due to "
				  "button press.\n", slot_name(p_slot));
499
500
501
502
		}
		/* blink green LED and turn off amber */
		p_slot->hpc_ops->green_led_blink(p_slot);
		p_slot->hpc_ops->set_attention_status(p_slot, 0);
Linus Torvalds's avatar
Linus Torvalds committed
503

504
		queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
505
506
507
508
509
510
511
512
		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
		 */
513
514
		ctrl_info(ctrl, "Button cancel on Slot(%s)\n",
			  slot_name(p_slot));
515
516
517
518
519
520
		cancel_delayed_work(&p_slot->work);
		if (p_slot->state == BLINKINGOFF_STATE)
			p_slot->hpc_ops->green_led_on(p_slot);
		else
			p_slot->hpc_ops->green_led_off(p_slot);
		p_slot->hpc_ops->set_attention_status(p_slot, 0);
521
522
		ctrl_info(ctrl, "PCI slot #%s - action canceled due to "
			  "button press\n", slot_name(p_slot));
523
524
525
526
527
528
529
530
531
		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
		 */
532
533
		ctrl_info(ctrl, "Button ignore on Slot(%s)\n",
			  slot_name(p_slot));
534
535
536
		update_slot_info(p_slot);
		break;
	default:
537
		ctrl_warn(ctrl, "Not a valid state\n");
538
539
540
541
		break;
	}
}

David Howells's avatar
David Howells committed
542
static void interrupt_event_handler(struct work_struct *work)
543
{
David Howells's avatar
David Howells committed
544
	struct event_info *info = container_of(work, struct event_info, work);
545
546
547
548
549
550
	struct slot *p_slot = info->p_slot;

	mutex_lock(&p_slot->lock);
	switch (info->event_type) {
	case INT_BUTTON_PRESS:
		handle_button_press_event(p_slot);
551
552
		break;
	case INT_POWER_FAULT:
553
		ctrl_dbg(p_slot->ctrl, "%s: Power fault\n", __func__);
554
555
556
557
558
559
		p_slot->hpc_ops->set_attention_status(p_slot, 1);
		p_slot->hpc_ops->green_led_off(p_slot);
		break;
	default:
		update_slot_info(p_slot);
		break;
Linus Torvalds's avatar
Linus Torvalds committed
560
	}
561
	mutex_unlock(&p_slot->lock);
Linus Torvalds's avatar
Linus Torvalds committed
562

563
	kfree(info);
Linus Torvalds's avatar
Linus Torvalds committed
564
565
566
}


567
static int shpchp_enable_slot (struct slot *p_slot)
Linus Torvalds's avatar
Linus Torvalds committed
568
569
{
	u8 getstatus = 0;
570
	int rc, retval = -ENODEV;
571
	struct controller *ctrl = p_slot->ctrl;
Linus Torvalds's avatar
Linus Torvalds committed
572
573

	/* Check to see if (latch closed, card present, power off) */
574
	mutex_lock(&p_slot->ctrl->crit_sect);
Linus Torvalds's avatar
Linus Torvalds committed
575
576
	rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
	if (rc || !getstatus) {
577
		ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
578
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
579
580
581
	}
	rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
	if (rc || getstatus) {
582
		ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot));
583
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
584
585
586
	}
	rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
	if (rc || getstatus) {
587
588
		ctrl_info(ctrl, "Already enabled on slot(%s)\n",
			  slot_name(p_slot));
589
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
590
591
	}

592
	p_slot->is_a_board = 1;
Linus Torvalds's avatar
Linus Torvalds committed
593
594

	/* We have to save the presence info for these slots */
595
596
	p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save));
	p_slot->hpc_ops->get_power_status(p_slot, &(p_slot->pwr_save));
597
	ctrl_dbg(ctrl, "%s: p_slot->pwr_save %x\n", __func__, p_slot->pwr_save);
Linus Torvalds's avatar
Linus Torvalds committed
598
599
	p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);

600
601
602
603
604
	if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) ||
	    (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458))
	     && p_slot->ctrl->num_slots == 1) {
		/* handle amd pogo errata; this must be done before enable  */
		amd_pogo_errata_save_misc_reg(p_slot);
605
		retval = board_added(p_slot);
606
607
608
		/* handle amd pogo errata; this must be done after enable  */
		amd_pogo_errata_restore_misc_reg(p_slot);
	} else
609
		retval = board_added(p_slot);
610

611
	if (retval) {
612
613
		p_slot->hpc_ops->get_adapter_status(p_slot,
				&(p_slot->presence_save));
Linus Torvalds's avatar
Linus Torvalds committed
614
615
616
		p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
	}

617
	update_slot_info(p_slot);
618
619
620
 out:
	mutex_unlock(&p_slot->ctrl->crit_sect);
	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
621
622
623
}


624
static int shpchp_disable_slot (struct slot *p_slot)
Linus Torvalds's avatar
Linus Torvalds committed
625
626
{
	u8 getstatus = 0;
627
	int rc, retval = -ENODEV;
628
	struct controller *ctrl = p_slot->ctrl;
Linus Torvalds's avatar
Linus Torvalds committed
629
630

	if (!p_slot->ctrl)
631
		return -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
632
633

	/* Check to see if (latch closed, card present, power on) */
634
	mutex_lock(&p_slot->ctrl->crit_sect);
Linus Torvalds's avatar
Linus Torvalds committed
635

636
637
	rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
	if (rc || !getstatus) {
638
		ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot));
639
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
640
	}
641
642
	rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
	if (rc || getstatus) {
643
		ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot));
644
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
645
	}
646
647
	rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
	if (rc || !getstatus) {
648
		ctrl_info(ctrl, "Already disabled on slot(%s)\n",
649
			  slot_name(p_slot));
650
		goto out;
Linus Torvalds's avatar
Linus Torvalds committed
651
652
	}

653
	retval = remove_board(p_slot);
654
	update_slot_info(p_slot);
655
656
657
 out:
	mutex_unlock(&p_slot->ctrl->crit_sect);
	return retval;
Linus Torvalds's avatar
Linus Torvalds committed
658
659
}

660
661
662
int shpchp_sysfs_enable_slot(struct slot *p_slot)
{
	int retval = -ENODEV;
663
	struct controller *ctrl = p_slot->ctrl;
664
665
666
667
668
669
670
671
672
673
674
675
676

	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 = shpchp_enable_slot(p_slot);
		mutex_lock(&p_slot->lock);
		p_slot->state = STATIC_STATE;
		break;
	case POWERON_STATE:
677
678
		ctrl_info(ctrl, "Slot %s is already in powering on state\n",
			  slot_name(p_slot));
679
680
681
		break;
	case BLINKINGOFF_STATE:
	case POWEROFF_STATE:
682
683
		ctrl_info(ctrl, "Already enabled on slot %s\n",
			  slot_name(p_slot));
684
685
		break;
	default:
686
687
		ctrl_err(ctrl, "Not a valid state on slot %s\n",
			 slot_name(p_slot));
688
689
690
691
692
693
694
695
696
697
		break;
	}
	mutex_unlock(&p_slot->lock);

	return retval;
}

int shpchp_sysfs_disable_slot(struct slot *p_slot)
{
	int retval = -ENODEV;
698
	struct controller *ctrl = p_slot->ctrl;
699
700
701
702
703
704
705
706
707
708
709
710
711

	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 = shpchp_disable_slot(p_slot);
		mutex_lock(&p_slot->lock);
		p_slot->state = STATIC_STATE;
		break;
	case POWEROFF_STATE:
712
713
		ctrl_info(ctrl, "Slot %s is already in powering off state\n",
			  slot_name(p_slot));
714
715
716
		break;
	case BLINKINGON_STATE:
	case POWERON_STATE:
717
718
		ctrl_info(ctrl, "Already disabled on slot %s\n",
			  slot_name(p_slot));
719
720
		break;
	default:
721
722
		ctrl_err(ctrl, "Not a valid state on slot %s\n",
			 slot_name(p_slot));
723
724
725
726
727
728
		break;
	}
	mutex_unlock(&p_slot->lock);

	return retval;
}