]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | * | |
8cf4c195 | 26 | * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> |
1da177e4 LT |
27 | * |
28 | */ | |
29 | ||
1da177e4 LT |
30 | #include <linux/module.h> |
31 | #include <linux/kernel.h> | |
32 | #include <linux/types.h> | |
1da177e4 LT |
33 | #include <linux/smp_lock.h> |
34 | #include <linux/pci.h> | |
dbd7a788 | 35 | #include "../pci.h" |
1da177e4 | 36 | #include "shpchp.h" |
1da177e4 | 37 | |
1da177e4 LT |
38 | static void interrupt_event_handler(struct controller *ctrl); |
39 | ||
40 | static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */ | |
41 | static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */ | |
42 | static int event_finished; | |
43 | static unsigned long pushbutton_pending; /* = 0 */ | |
44 | ||
1da177e4 LT |
45 | u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id) |
46 | { | |
47 | struct controller *ctrl = (struct controller *) inst_id; | |
48 | struct slot *p_slot; | |
49 | u8 rc = 0; | |
50 | u8 getstatus; | |
1da177e4 LT |
51 | struct event_info *taskInfo; |
52 | ||
53 | /* Attention Button Change */ | |
54 | dbg("shpchp: Attention button interrupt received.\n"); | |
55 | ||
1da177e4 LT |
56 | /* This is the structure that tells the worker thread what to do */ |
57 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
58 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
59 | ||
2178bfad | 60 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); |
1da177e4 LT |
61 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
62 | ||
63 | ctrl->next_event = (ctrl->next_event + 1) % 10; | |
64 | taskInfo->hp_slot = hp_slot; | |
65 | ||
66 | rc++; | |
67 | ||
68 | /* | |
69 | * Button pressed - See if need to TAKE ACTION!!! | |
70 | */ | |
71 | info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
72 | taskInfo->event_type = INT_BUTTON_PRESS; | |
73 | ||
74 | if ((p_slot->state == BLINKINGON_STATE) | |
75 | || (p_slot->state == BLINKINGOFF_STATE)) { | |
76 | /* Cancel if we are still blinking; this means that we press the | |
77 | * attention again before the 5 sec. limit expires to cancel hot-add | |
78 | * or hot-remove | |
79 | */ | |
80 | taskInfo->event_type = INT_BUTTON_CANCEL; | |
81 | info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
82 | } else if ((p_slot->state == POWERON_STATE) | |
83 | || (p_slot->state == POWEROFF_STATE)) { | |
84 | /* Ignore if the slot is on power-on or power-off state; this | |
85 | * means that the previous attention button action to hot-add or | |
86 | * hot-remove is undergoing | |
87 | */ | |
88 | taskInfo->event_type = INT_BUTTON_IGNORE; | |
89 | info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
90 | } | |
91 | ||
92 | if (rc) | |
93 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
94 | ||
95 | return 0; | |
96 | ||
97 | } | |
98 | ||
99 | u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id) | |
100 | { | |
101 | struct controller *ctrl = (struct controller *) inst_id; | |
102 | struct slot *p_slot; | |
103 | u8 rc = 0; | |
104 | u8 getstatus; | |
1da177e4 LT |
105 | struct event_info *taskInfo; |
106 | ||
107 | /* Switch Change */ | |
108 | dbg("shpchp: Switch interrupt received.\n"); | |
109 | ||
1da177e4 LT |
110 | /* This is the structure that tells the worker thread |
111 | * what to do | |
112 | */ | |
113 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
114 | ctrl->next_event = (ctrl->next_event + 1) % 10; | |
115 | taskInfo->hp_slot = hp_slot; | |
116 | ||
117 | rc++; | |
118 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
2178bfad | 119 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); |
1da177e4 LT |
120 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
121 | dbg("%s: Card present %x Power status %x\n", __FUNCTION__, | |
2178bfad | 122 | p_slot->presence_save, p_slot->pwr_save); |
1da177e4 LT |
123 | |
124 | if (getstatus) { | |
125 | /* | |
126 | * Switch opened | |
127 | */ | |
128 | info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
1da177e4 | 129 | taskInfo->event_type = INT_SWITCH_OPEN; |
2178bfad | 130 | if (p_slot->pwr_save && p_slot->presence_save) { |
1da177e4 LT |
131 | taskInfo->event_type = INT_POWER_FAULT; |
132 | err("Surprise Removal of card\n"); | |
133 | } | |
134 | } else { | |
135 | /* | |
136 | * Switch closed | |
137 | */ | |
138 | info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
1da177e4 LT |
139 | taskInfo->event_type = INT_SWITCH_CLOSE; |
140 | } | |
141 | ||
142 | if (rc) | |
143 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
144 | ||
145 | return rc; | |
146 | } | |
147 | ||
148 | u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id) | |
149 | { | |
150 | struct controller *ctrl = (struct controller *) inst_id; | |
151 | struct slot *p_slot; | |
152 | u8 rc = 0; | |
153 | /*u8 temp_byte;*/ | |
1da177e4 LT |
154 | struct event_info *taskInfo; |
155 | ||
156 | /* Presence Change */ | |
157 | dbg("shpchp: Presence/Notify input change.\n"); | |
158 | ||
1da177e4 LT |
159 | /* This is the structure that tells the worker thread |
160 | * what to do | |
161 | */ | |
162 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
163 | ctrl->next_event = (ctrl->next_event + 1) % 10; | |
164 | taskInfo->hp_slot = hp_slot; | |
165 | ||
166 | rc++; | |
167 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
168 | ||
169 | /* | |
170 | * Save the presence state | |
171 | */ | |
2178bfad RS |
172 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); |
173 | if (p_slot->presence_save) { | |
1da177e4 LT |
174 | /* |
175 | * Card Present | |
176 | */ | |
177 | info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
178 | taskInfo->event_type = INT_PRESENCE_ON; | |
179 | } else { | |
180 | /* | |
181 | * Not Present | |
182 | */ | |
183 | info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
184 | taskInfo->event_type = INT_PRESENCE_OFF; | |
185 | } | |
186 | ||
187 | if (rc) | |
188 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
189 | ||
190 | return rc; | |
191 | } | |
192 | ||
193 | u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id) | |
194 | { | |
195 | struct controller *ctrl = (struct controller *) inst_id; | |
196 | struct slot *p_slot; | |
197 | u8 rc = 0; | |
1da177e4 LT |
198 | struct event_info *taskInfo; |
199 | ||
200 | /* Power fault */ | |
201 | dbg("shpchp: Power fault interrupt received.\n"); | |
202 | ||
1da177e4 LT |
203 | /* This is the structure that tells the worker thread |
204 | * what to do | |
205 | */ | |
206 | taskInfo = &(ctrl->event_queue[ctrl->next_event]); | |
207 | ctrl->next_event = (ctrl->next_event + 1) % 10; | |
208 | taskInfo->hp_slot = hp_slot; | |
209 | ||
210 | rc++; | |
211 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | |
212 | ||
213 | if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { | |
214 | /* | |
215 | * Power fault Cleared | |
216 | */ | |
217 | info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
2178bfad | 218 | p_slot->status = 0x00; |
1da177e4 LT |
219 | taskInfo->event_type = INT_POWER_FAULT_CLEAR; |
220 | } else { | |
221 | /* | |
222 | * Power fault | |
223 | */ | |
224 | info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot); | |
225 | taskInfo->event_type = INT_POWER_FAULT; | |
226 | /* set power fault status for this board */ | |
2178bfad | 227 | p_slot->status = 0xFF; |
1da177e4 LT |
228 | info("power fault bit %x set\n", hp_slot); |
229 | } | |
230 | if (rc) | |
231 | up(&event_semaphore); /* signal event thread that new event is posted */ | |
232 | ||
233 | return rc; | |
234 | } | |
235 | ||
1da177e4 LT |
236 | /* The following routines constitute the bulk of the |
237 | hotplug controller logic | |
238 | */ | |
ee138334 RS |
239 | static int change_bus_speed(struct controller *ctrl, struct slot *p_slot, |
240 | enum pci_bus_speed speed) | |
1da177e4 | 241 | { |
ee138334 | 242 | int rc = 0; |
1da177e4 LT |
243 | |
244 | dbg("%s: change to speed %d\n", __FUNCTION__, speed); | |
6aa4cdd0 | 245 | mutex_lock(&ctrl->crit_sect); |
1da177e4 LT |
246 | if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) { |
247 | err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); | |
6aa4cdd0 | 248 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
249 | return WRONG_BUS_FREQUENCY; |
250 | } | |
1da177e4 LT |
251 | |
252 | if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) { | |
253 | err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", | |
254 | __FUNCTION__); | |
255 | err("%s: Error code (%d)\n", __FUNCTION__, rc); | |
6aa4cdd0 | 256 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
257 | return WRONG_BUS_FREQUENCY; |
258 | } | |
6aa4cdd0 | 259 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
260 | return rc; |
261 | } | |
262 | ||
ee138334 RS |
263 | static int fix_bus_speed(struct controller *ctrl, struct slot *pslot, |
264 | u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp, | |
265 | enum pci_bus_speed msp) | |
1da177e4 | 266 | { |
ee138334 | 267 | int rc = 0; |
1da177e4 LT |
268 | |
269 | if (flag != 0) { /* Other slots on the same bus are occupied */ | |
270 | if ( asp < bsp ) { | |
271 | err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bsp, asp); | |
272 | return WRONG_BUS_FREQUENCY; | |
273 | } | |
274 | } else { | |
275 | /* Other slots on the same bus are empty */ | |
276 | if (msp == bsp) { | |
277 | /* if adapter_speed >= bus_speed, do nothing */ | |
278 | if (asp < bsp) { | |
279 | /* | |
280 | * Try to lower bus speed to accommodate the adapter if other slots | |
281 | * on the same controller are empty | |
282 | */ | |
283 | if ((rc = change_bus_speed(ctrl, pslot, asp))) | |
284 | return rc; | |
285 | } | |
286 | } else { | |
287 | if (asp < msp) { | |
288 | if ((rc = change_bus_speed(ctrl, pslot, asp))) | |
289 | return rc; | |
290 | } else { | |
291 | if ((rc = change_bus_speed(ctrl, pslot, msp))) | |
292 | return rc; | |
293 | } | |
294 | } | |
295 | } | |
296 | return rc; | |
297 | } | |
298 | ||
299 | /** | |
300 | * board_added - Called after a board has been added to the system. | |
301 | * | |
302 | * Turns power on for the board | |
303 | * Configures board | |
304 | * | |
305 | */ | |
ee138334 | 306 | static int board_added(struct slot *p_slot) |
1da177e4 LT |
307 | { |
308 | u8 hp_slot; | |
309 | u8 slots_not_empty = 0; | |
ee138334 | 310 | int rc = 0; |
1da177e4 LT |
311 | enum pci_bus_speed adapter_speed, bus_speed, max_bus_speed; |
312 | u8 pi, mode; | |
2178bfad | 313 | struct controller *ctrl = p_slot->ctrl; |
1da177e4 | 314 | |
2178bfad | 315 | hp_slot = p_slot->device - ctrl->slot_device_offset; |
1da177e4 | 316 | |
2178bfad RS |
317 | dbg("%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n", |
318 | __FUNCTION__, p_slot->device, | |
319 | ctrl->slot_device_offset, hp_slot); | |
1da177e4 LT |
320 | |
321 | /* Wait for exclusive access to hardware */ | |
6aa4cdd0 | 322 | mutex_lock(&ctrl->crit_sect); |
1da177e4 LT |
323 | |
324 | /* Power on slot without connecting to bus */ | |
325 | rc = p_slot->hpc_ops->power_on_slot(p_slot); | |
326 | if (rc) { | |
327 | err("%s: Failed to power on slot\n", __FUNCTION__); | |
328 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 329 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
330 | return -1; |
331 | } | |
1da177e4 LT |
332 | |
333 | rc = p_slot->hpc_ops->check_cmd_status(ctrl); | |
334 | if (rc) { | |
335 | err("%s: Failed to power on slot, error code(%d)\n", __FUNCTION__, rc); | |
336 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 337 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
338 | return -1; |
339 | } | |
340 | ||
341 | ||
342 | if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) { | |
343 | if (slots_not_empty) | |
344 | return WRONG_BUS_FREQUENCY; | |
345 | ||
346 | if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) { | |
347 | err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__); | |
6aa4cdd0 | 348 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
349 | return WRONG_BUS_FREQUENCY; |
350 | } | |
1da177e4 LT |
351 | |
352 | if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) { | |
353 | err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n", | |
354 | __FUNCTION__); | |
355 | err("%s: Error code (%d)\n", __FUNCTION__, rc); | |
6aa4cdd0 | 356 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
357 | return WRONG_BUS_FREQUENCY; |
358 | } | |
359 | /* turn on board, blink green LED, turn off Amber LED */ | |
360 | if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { | |
361 | err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); | |
6aa4cdd0 | 362 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
363 | return rc; |
364 | } | |
1da177e4 LT |
365 | |
366 | if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) { | |
367 | err("%s: Failed to enable slot, error code(%d)\n", __FUNCTION__, rc); | |
6aa4cdd0 | 368 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
369 | return rc; |
370 | } | |
371 | } | |
372 | ||
373 | rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &adapter_speed); | |
374 | /* 0 = PCI 33Mhz, 1 = PCI 66 Mhz, 2 = PCI-X 66 PA, 4 = PCI-X 66 ECC, */ | |
375 | /* 5 = PCI-X 133 PA, 7 = PCI-X 133 ECC, 0xa = PCI-X 133 Mhz 266, */ | |
376 | /* 0xd = PCI-X 133 Mhz 533 */ | |
377 | /* This encoding is different from the one used in cur_bus_speed & */ | |
378 | /* max_bus_speed */ | |
379 | ||
380 | if (rc || adapter_speed == PCI_SPEED_UNKNOWN) { | |
381 | err("%s: Can't get adapter speed or bus mode mismatch\n", __FUNCTION__); | |
382 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 383 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
384 | return WRONG_BUS_FREQUENCY; |
385 | } | |
386 | ||
387 | rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bus_speed); | |
388 | if (rc || bus_speed == PCI_SPEED_UNKNOWN) { | |
389 | err("%s: Can't get bus operation speed\n", __FUNCTION__); | |
390 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 391 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
392 | return WRONG_BUS_FREQUENCY; |
393 | } | |
394 | ||
395 | rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &max_bus_speed); | |
396 | if (rc || max_bus_speed == PCI_SPEED_UNKNOWN) { | |
397 | err("%s: Can't get max bus operation speed\n", __FUNCTION__); | |
398 | max_bus_speed = bus_speed; | |
399 | } | |
400 | ||
401 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 402 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
403 | |
404 | if ((rc = p_slot->hpc_ops->get_prog_int(p_slot, &pi))) { | |
405 | err("%s: Can't get controller programming interface, set it to 1\n", __FUNCTION__); | |
406 | pi = 1; | |
407 | } | |
408 | ||
409 | /* Check if there are other slots or devices on the same bus */ | |
410 | if (!list_empty(&ctrl->pci_dev->subordinate->devices)) | |
411 | slots_not_empty = 1; | |
412 | ||
413 | dbg("%s: slots_not_empty %d, pi %d\n", __FUNCTION__, | |
414 | slots_not_empty, pi); | |
415 | dbg("adapter_speed %d, bus_speed %d, max_bus_speed %d\n", | |
416 | adapter_speed, bus_speed, max_bus_speed); | |
417 | ||
418 | if (pi == 2) { | |
419 | dbg("%s: In PI = %d\n", __FUNCTION__, pi); | |
420 | if ((rc = p_slot->hpc_ops->get_mode1_ECC_cap(p_slot, &mode))) { | |
421 | err("%s: Can't get Mode1_ECC, set mode to 0\n", __FUNCTION__); | |
422 | mode = 0; | |
423 | } | |
424 | ||
425 | switch (adapter_speed) { | |
426 | case PCI_SPEED_133MHz_PCIX_533: | |
427 | case PCI_SPEED_133MHz_PCIX_266: | |
428 | if ((bus_speed != adapter_speed) && | |
429 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | |
430 | return rc; | |
431 | break; | |
432 | case PCI_SPEED_133MHz_PCIX_ECC: | |
433 | case PCI_SPEED_133MHz_PCIX: | |
434 | if (mode) { /* Bus - Mode 1 ECC */ | |
435 | if ((bus_speed != 0x7) && | |
436 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | |
437 | return rc; | |
438 | } else { | |
439 | if ((bus_speed != 0x4) && | |
440 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | |
441 | return rc; | |
442 | } | |
443 | break; | |
444 | case PCI_SPEED_66MHz_PCIX_ECC: | |
445 | case PCI_SPEED_66MHz_PCIX: | |
446 | if (mode) { /* Bus - Mode 1 ECC */ | |
447 | if ((bus_speed != 0x5) && | |
448 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | |
449 | return rc; | |
450 | } else { | |
451 | if ((bus_speed != 0x2) && | |
452 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | |
453 | return rc; | |
454 | } | |
455 | break; | |
456 | case PCI_SPEED_66MHz: | |
457 | if ((bus_speed != 0x1) && | |
458 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | |
459 | return rc; | |
460 | break; | |
461 | case PCI_SPEED_33MHz: | |
462 | if (bus_speed > 0x0) { | |
463 | if (slots_not_empty == 0) { | |
464 | if ((rc = change_bus_speed(ctrl, p_slot, adapter_speed))) | |
465 | return rc; | |
466 | } else { | |
467 | err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); | |
468 | return WRONG_BUS_FREQUENCY; | |
469 | } | |
470 | } | |
471 | break; | |
472 | default: | |
473 | err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed); | |
474 | return WRONG_BUS_FREQUENCY; | |
475 | } | |
476 | } else { | |
477 | /* If adpater_speed == bus_speed, nothing to do here */ | |
478 | dbg("%s: In PI = %d\n", __FUNCTION__, pi); | |
479 | if ((adapter_speed != bus_speed) && | |
480 | ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) | |
481 | return rc; | |
482 | } | |
483 | ||
6aa4cdd0 | 484 | mutex_lock(&ctrl->crit_sect); |
1da177e4 LT |
485 | /* turn on board, blink green LED, turn off Amber LED */ |
486 | if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { | |
487 | err("%s: Issue of Slot Enable command failed\n", __FUNCTION__); | |
6aa4cdd0 | 488 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
489 | return rc; |
490 | } | |
1da177e4 LT |
491 | |
492 | if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) { | |
493 | err("%s: Failed to enable slot, error code(%d)\n", __FUNCTION__, rc); | |
6aa4cdd0 | 494 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
495 | return rc; |
496 | } | |
497 | ||
6aa4cdd0 | 498 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
499 | |
500 | /* Wait for ~1 second */ | |
1da177e4 | 501 | wait_for_ctrl_irq (ctrl); |
1da177e4 | 502 | |
2178bfad | 503 | dbg("%s: slot status = %x\n", __FUNCTION__, p_slot->status); |
1da177e4 | 504 | /* Check for a power fault */ |
2178bfad | 505 | if (p_slot->status == 0xFF) { |
1da177e4 | 506 | /* power fault occurred, but it was benign */ |
dbd7a788 | 507 | dbg("%s: power fault\n", __FUNCTION__); |
1da177e4 | 508 | rc = POWER_FAILURE; |
2178bfad | 509 | p_slot->status = 0; |
dbd7a788 | 510 | goto err_exit; |
1da177e4 LT |
511 | } |
512 | ||
dbd7a788 RS |
513 | if (shpchp_configure_device(p_slot)) { |
514 | err("Cannot add device at 0x%x:0x%x\n", p_slot->bus, | |
515 | p_slot->device); | |
516 | goto err_exit; | |
517 | } | |
1da177e4 | 518 | |
2178bfad RS |
519 | p_slot->status = 0; |
520 | p_slot->is_a_board = 0x01; | |
521 | p_slot->pwr_save = 1; | |
1da177e4 | 522 | |
dbd7a788 | 523 | /* Wait for exclusive access to hardware */ |
6aa4cdd0 | 524 | mutex_lock(&ctrl->crit_sect); |
1da177e4 | 525 | |
dbd7a788 | 526 | p_slot->hpc_ops->green_led_on(p_slot); |
1da177e4 | 527 | |
dbd7a788 | 528 | /* Done with exclusive hardware access */ |
6aa4cdd0 | 529 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 | 530 | |
dbd7a788 | 531 | return 0; |
1da177e4 | 532 | |
dbd7a788 RS |
533 | err_exit: |
534 | /* Wait for exclusive access to hardware */ | |
6aa4cdd0 | 535 | mutex_lock(&ctrl->crit_sect); |
1da177e4 | 536 | |
dbd7a788 RS |
537 | /* turn off slot, turn on Amber LED, turn off Green LED */ |
538 | rc = p_slot->hpc_ops->slot_disable(p_slot); | |
539 | if (rc) { | |
540 | err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); | |
1da177e4 | 541 | /* Done with exclusive hardware access */ |
6aa4cdd0 | 542 | mutex_unlock(&ctrl->crit_sect); |
dbd7a788 RS |
543 | return rc; |
544 | } | |
1da177e4 | 545 | |
dbd7a788 RS |
546 | rc = p_slot->hpc_ops->check_cmd_status(ctrl); |
547 | if (rc) { | |
548 | err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc); | |
1da177e4 | 549 | /* Done with exclusive hardware access */ |
6aa4cdd0 | 550 | mutex_unlock(&ctrl->crit_sect); |
dbd7a788 | 551 | return rc; |
1da177e4 | 552 | } |
dbd7a788 RS |
553 | |
554 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 555 | mutex_unlock(&ctrl->crit_sect); |
dbd7a788 RS |
556 | |
557 | return(rc); | |
1da177e4 LT |
558 | } |
559 | ||
560 | ||
561 | /** | |
562 | * remove_board - Turns off slot and LED's | |
563 | * | |
564 | */ | |
ee138334 | 565 | static int remove_board(struct slot *p_slot) |
1da177e4 | 566 | { |
2178bfad | 567 | struct controller *ctrl = p_slot->ctrl; |
1da177e4 | 568 | u8 hp_slot; |
ee138334 | 569 | int rc; |
70b60919 | 570 | |
2178bfad | 571 | if (shpchp_unconfigure_device(p_slot)) |
1da177e4 LT |
572 | return(1); |
573 | ||
2178bfad | 574 | hp_slot = p_slot->device - ctrl->slot_device_offset; |
1da177e4 LT |
575 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
576 | ||
577 | dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot); | |
578 | ||
1da177e4 | 579 | /* Change status to shutdown */ |
2178bfad RS |
580 | if (p_slot->is_a_board) |
581 | p_slot->status = 0x01; | |
1da177e4 LT |
582 | |
583 | /* Wait for exclusive access to hardware */ | |
6aa4cdd0 | 584 | mutex_lock(&ctrl->crit_sect); |
1da177e4 LT |
585 | |
586 | /* turn off slot, turn on Amber LED, turn off Green LED */ | |
587 | rc = p_slot->hpc_ops->slot_disable(p_slot); | |
588 | if (rc) { | |
589 | err("%s: Issue of Slot Disable command failed\n", __FUNCTION__); | |
590 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 591 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
592 | return rc; |
593 | } | |
1da177e4 LT |
594 | |
595 | rc = p_slot->hpc_ops->check_cmd_status(ctrl); | |
596 | if (rc) { | |
597 | err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc); | |
598 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 599 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
600 | return rc; |
601 | } | |
602 | ||
603 | rc = p_slot->hpc_ops->set_attention_status(p_slot, 0); | |
604 | if (rc) { | |
605 | err("%s: Issue of Set Attention command failed\n", __FUNCTION__); | |
606 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 607 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
608 | return rc; |
609 | } | |
1da177e4 LT |
610 | |
611 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 612 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 | 613 | |
2178bfad RS |
614 | p_slot->pwr_save = 0; |
615 | p_slot->is_a_board = 0; | |
1da177e4 LT |
616 | |
617 | return 0; | |
618 | } | |
619 | ||
620 | ||
621 | static void pushbutton_helper_thread (unsigned long data) | |
622 | { | |
623 | pushbutton_pending = data; | |
624 | ||
625 | up(&event_semaphore); | |
626 | } | |
627 | ||
628 | ||
629 | /** | |
630 | * shpchp_pushbutton_thread | |
631 | * | |
632 | * Scheduled procedure to handle blocking stuff for the pushbuttons | |
633 | * Handles all pending events and exits. | |
634 | * | |
635 | */ | |
636 | static void shpchp_pushbutton_thread (unsigned long slot) | |
637 | { | |
638 | struct slot *p_slot = (struct slot *) slot; | |
639 | u8 getstatus; | |
640 | ||
641 | pushbutton_pending = 0; | |
642 | ||
643 | if (!p_slot) { | |
644 | dbg("%s: Error! slot NULL\n", __FUNCTION__); | |
645 | return; | |
646 | } | |
647 | ||
648 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
649 | if (getstatus) { | |
650 | p_slot->state = POWEROFF_STATE; | |
1da177e4 LT |
651 | |
652 | shpchp_disable_slot(p_slot); | |
653 | p_slot->state = STATIC_STATE; | |
654 | } else { | |
655 | p_slot->state = POWERON_STATE; | |
1da177e4 LT |
656 | |
657 | if (shpchp_enable_slot(p_slot)) { | |
658 | /* Wait for exclusive access to hardware */ | |
6aa4cdd0 | 659 | mutex_lock(&p_slot->ctrl->crit_sect); |
1da177e4 LT |
660 | |
661 | p_slot->hpc_ops->green_led_off(p_slot); | |
662 | ||
1da177e4 | 663 | /* Done with exclusive hardware access */ |
6aa4cdd0 | 664 | mutex_unlock(&p_slot->ctrl->crit_sect); |
1da177e4 LT |
665 | } |
666 | p_slot->state = STATIC_STATE; | |
667 | } | |
668 | ||
669 | return; | |
670 | } | |
671 | ||
672 | ||
673 | /* this is the main worker thread */ | |
674 | static int event_thread(void* data) | |
675 | { | |
676 | struct controller *ctrl; | |
677 | lock_kernel(); | |
678 | daemonize("shpchpd_event"); | |
679 | unlock_kernel(); | |
680 | ||
681 | while (1) { | |
682 | dbg("!!!!event_thread sleeping\n"); | |
683 | down_interruptible (&event_semaphore); | |
684 | dbg("event_thread woken finished = %d\n", event_finished); | |
685 | if (event_finished || signal_pending(current)) | |
686 | break; | |
687 | /* Do stuff here */ | |
688 | if (pushbutton_pending) | |
689 | shpchp_pushbutton_thread(pushbutton_pending); | |
690 | else | |
a4534560 | 691 | list_for_each_entry(ctrl, &shpchp_ctrl_list, ctrl_list) |
1da177e4 LT |
692 | interrupt_event_handler(ctrl); |
693 | } | |
694 | dbg("event_thread signals exit\n"); | |
695 | up(&event_exit); | |
696 | return 0; | |
697 | } | |
698 | ||
699 | int shpchp_event_start_thread (void) | |
700 | { | |
701 | int pid; | |
702 | ||
703 | /* initialize our semaphores */ | |
704 | init_MUTEX_LOCKED(&event_exit); | |
705 | event_finished=0; | |
706 | ||
707 | init_MUTEX_LOCKED(&event_semaphore); | |
708 | pid = kernel_thread(event_thread, NULL, 0); | |
709 | ||
710 | if (pid < 0) { | |
711 | err ("Can't start up our event thread\n"); | |
712 | return -1; | |
713 | } | |
1da177e4 LT |
714 | return 0; |
715 | } | |
716 | ||
717 | ||
718 | void shpchp_event_stop_thread (void) | |
719 | { | |
720 | event_finished = 1; | |
1da177e4 | 721 | up(&event_semaphore); |
1da177e4 LT |
722 | down(&event_exit); |
723 | } | |
724 | ||
725 | ||
726 | static int update_slot_info (struct slot *slot) | |
727 | { | |
728 | struct hotplug_slot_info *info; | |
729 | int result; | |
730 | ||
731 | info = kmalloc(sizeof(*info), GFP_KERNEL); | |
732 | if (!info) | |
733 | return -ENOMEM; | |
734 | ||
735 | slot->hpc_ops->get_power_status(slot, &(info->power_status)); | |
736 | slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); | |
737 | slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); | |
738 | slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); | |
739 | ||
740 | result = pci_hp_change_slot_info(slot->hotplug_slot, info); | |
741 | kfree (info); | |
742 | return result; | |
743 | } | |
744 | ||
745 | static void interrupt_event_handler(struct controller *ctrl) | |
746 | { | |
747 | int loop = 0; | |
748 | int change = 1; | |
1da177e4 LT |
749 | u8 hp_slot; |
750 | u8 getstatus; | |
751 | struct slot *p_slot; | |
752 | ||
1da177e4 LT |
753 | while (change) { |
754 | change = 0; | |
755 | ||
756 | for (loop = 0; loop < 10; loop++) { | |
757 | if (ctrl->event_queue[loop].event_type != 0) { | |
758 | dbg("%s:loop %x event_type %x\n", __FUNCTION__, loop, | |
759 | ctrl->event_queue[loop].event_type); | |
760 | hp_slot = ctrl->event_queue[loop].hp_slot; | |
761 | ||
1da177e4 LT |
762 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
763 | ||
1da177e4 LT |
764 | if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) { |
765 | dbg("%s: button cancel\n", __FUNCTION__); | |
766 | del_timer(&p_slot->task_event); | |
767 | ||
768 | switch (p_slot->state) { | |
769 | case BLINKINGOFF_STATE: | |
770 | /* Wait for exclusive access to hardware */ | |
6aa4cdd0 | 771 | mutex_lock(&ctrl->crit_sect); |
1da177e4 LT |
772 | |
773 | p_slot->hpc_ops->green_led_on(p_slot); | |
1da177e4 LT |
774 | |
775 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | |
776 | ||
1da177e4 | 777 | /* Done with exclusive hardware access */ |
6aa4cdd0 | 778 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
779 | break; |
780 | case BLINKINGON_STATE: | |
781 | /* Wait for exclusive access to hardware */ | |
6aa4cdd0 | 782 | mutex_lock(&ctrl->crit_sect); |
1da177e4 LT |
783 | |
784 | p_slot->hpc_ops->green_led_off(p_slot); | |
1da177e4 LT |
785 | |
786 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | |
1da177e4 LT |
787 | |
788 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 789 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
790 | |
791 | break; | |
792 | default: | |
793 | warn("Not a valid state\n"); | |
794 | return; | |
795 | } | |
796 | info(msg_button_cancel, p_slot->number); | |
797 | p_slot->state = STATIC_STATE; | |
798 | } else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { | |
799 | /* Button Pressed (No action on 1st press...) */ | |
800 | dbg("%s: Button pressed\n", __FUNCTION__); | |
801 | ||
802 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
803 | if (getstatus) { | |
804 | /* slot is on */ | |
805 | dbg("%s: slot is on\n", __FUNCTION__); | |
806 | p_slot->state = BLINKINGOFF_STATE; | |
807 | info(msg_button_off, p_slot->number); | |
808 | } else { | |
809 | /* slot is off */ | |
810 | dbg("%s: slot is off\n", __FUNCTION__); | |
811 | p_slot->state = BLINKINGON_STATE; | |
812 | info(msg_button_on, p_slot->number); | |
813 | } | |
814 | ||
815 | /* Wait for exclusive access to hardware */ | |
6aa4cdd0 | 816 | mutex_lock(&ctrl->crit_sect); |
1da177e4 LT |
817 | |
818 | /* blink green LED and turn off amber */ | |
819 | p_slot->hpc_ops->green_led_blink(p_slot); | |
1da177e4 LT |
820 | |
821 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | |
822 | ||
1da177e4 | 823 | /* Done with exclusive hardware access */ |
6aa4cdd0 | 824 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
825 | |
826 | init_timer(&p_slot->task_event); | |
827 | p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ | |
828 | p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread; | |
829 | p_slot->task_event.data = (unsigned long) p_slot; | |
830 | ||
831 | dbg("%s: add_timer p_slot = %p\n", __FUNCTION__,(void *) p_slot); | |
832 | add_timer(&p_slot->task_event); | |
833 | } else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { | |
834 | /***********POWER FAULT********************/ | |
835 | dbg("%s: power fault\n", __FUNCTION__); | |
836 | /* Wait for exclusive access to hardware */ | |
6aa4cdd0 | 837 | mutex_lock(&ctrl->crit_sect); |
1da177e4 LT |
838 | |
839 | p_slot->hpc_ops->set_attention_status(p_slot, 1); | |
1da177e4 LT |
840 | |
841 | p_slot->hpc_ops->green_led_off(p_slot); | |
1da177e4 LT |
842 | |
843 | /* Done with exclusive hardware access */ | |
6aa4cdd0 | 844 | mutex_unlock(&ctrl->crit_sect); |
1da177e4 LT |
845 | } else { |
846 | /* refresh notification */ | |
847 | if (p_slot) | |
848 | update_slot_info(p_slot); | |
849 | } | |
850 | ||
851 | ctrl->event_queue[loop].event_type = 0; | |
852 | ||
853 | change = 1; | |
854 | } | |
855 | } /* End of FOR loop */ | |
856 | } | |
857 | ||
858 | return; | |
859 | } | |
860 | ||
861 | ||
862 | int shpchp_enable_slot (struct slot *p_slot) | |
863 | { | |
864 | u8 getstatus = 0; | |
865 | int rc; | |
1da177e4 LT |
866 | |
867 | /* Check to see if (latch closed, card present, power off) */ | |
6aa4cdd0 | 868 | mutex_lock(&p_slot->ctrl->crit_sect); |
1da177e4 LT |
869 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); |
870 | if (rc || !getstatus) { | |
871 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); | |
6aa4cdd0 | 872 | mutex_unlock(&p_slot->ctrl->crit_sect); |
ee17fd93 | 873 | return -ENODEV; |
1da177e4 LT |
874 | } |
875 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | |
876 | if (rc || getstatus) { | |
877 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); | |
6aa4cdd0 | 878 | mutex_unlock(&p_slot->ctrl->crit_sect); |
ee17fd93 | 879 | return -ENODEV; |
1da177e4 LT |
880 | } |
881 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
882 | if (rc || getstatus) { | |
883 | info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number); | |
6aa4cdd0 | 884 | mutex_unlock(&p_slot->ctrl->crit_sect); |
ee17fd93 | 885 | return -ENODEV; |
1da177e4 | 886 | } |
6aa4cdd0 | 887 | mutex_unlock(&p_slot->ctrl->crit_sect); |
1da177e4 | 888 | |
2178bfad | 889 | p_slot->is_a_board = 1; |
1da177e4 LT |
890 | |
891 | /* We have to save the presence info for these slots */ | |
2178bfad RS |
892 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); |
893 | p_slot->hpc_ops->get_power_status(p_slot, &(p_slot->pwr_save)); | |
894 | dbg("%s: p_slot->pwr_save %x\n", __FUNCTION__, p_slot->pwr_save); | |
1da177e4 | 895 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
1da177e4 | 896 | |
53044f35 KD |
897 | if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) || |
898 | (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458)) | |
899 | && p_slot->ctrl->num_slots == 1) { | |
900 | /* handle amd pogo errata; this must be done before enable */ | |
901 | amd_pogo_errata_save_misc_reg(p_slot); | |
902 | rc = board_added(p_slot); | |
903 | /* handle amd pogo errata; this must be done after enable */ | |
904 | amd_pogo_errata_restore_misc_reg(p_slot); | |
905 | } else | |
906 | rc = board_added(p_slot); | |
907 | ||
1da177e4 | 908 | if (rc) { |
2178bfad RS |
909 | p_slot->hpc_ops->get_adapter_status(p_slot, |
910 | &(p_slot->presence_save)); | |
1da177e4 | 911 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
1da177e4 LT |
912 | } |
913 | ||
2178bfad | 914 | update_slot_info(p_slot); |
1da177e4 LT |
915 | return rc; |
916 | } | |
917 | ||
918 | ||
919 | int shpchp_disable_slot (struct slot *p_slot) | |
920 | { | |
1da177e4 | 921 | u8 getstatus = 0; |
1da177e4 | 922 | int ret = 0; |
1da177e4 LT |
923 | |
924 | if (!p_slot->ctrl) | |
ee17fd93 | 925 | return -ENODEV; |
1da177e4 | 926 | |
1da177e4 | 927 | /* Check to see if (latch closed, card present, power on) */ |
6aa4cdd0 | 928 | mutex_lock(&p_slot->ctrl->crit_sect); |
1da177e4 LT |
929 | |
930 | ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | |
931 | if (ret || !getstatus) { | |
932 | info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number); | |
6aa4cdd0 | 933 | mutex_unlock(&p_slot->ctrl->crit_sect); |
ee17fd93 | 934 | return -ENODEV; |
1da177e4 LT |
935 | } |
936 | ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | |
937 | if (ret || getstatus) { | |
938 | info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number); | |
6aa4cdd0 | 939 | mutex_unlock(&p_slot->ctrl->crit_sect); |
ee17fd93 | 940 | return -ENODEV; |
1da177e4 LT |
941 | } |
942 | ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | |
943 | if (ret || !getstatus) { | |
944 | info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number); | |
6aa4cdd0 | 945 | mutex_unlock(&p_slot->ctrl->crit_sect); |
ee17fd93 | 946 | return -ENODEV; |
1da177e4 | 947 | } |
6aa4cdd0 | 948 | mutex_unlock(&p_slot->ctrl->crit_sect); |
1da177e4 | 949 | |
ee138334 | 950 | ret = remove_board(p_slot); |
2178bfad | 951 | update_slot_info(p_slot); |
ee138334 | 952 | return ret; |
1da177e4 LT |
953 | } |
954 |