23dd61c4c66c82d9677466765e3534e658757272
[firefly-linux-kernel-4.4.55.git] / drivers / pci / hotplug / shpchp_ctrl.c
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/config.h>
31 #include <linux/module.h>
32 #include <linux/kernel.h>
33 #include <linux/types.h>
34 #include <linux/slab.h>
35 #include <linux/workqueue.h>
36 #include <linux/interrupt.h>
37 #include <linux/delay.h>
38 #include <linux/wait.h>
39 #include <linux/smp_lock.h>
40 #include <linux/pci.h>
41 #include "../pci.h"
42 #include "shpchp.h"
43
44 static void interrupt_event_handler(struct controller *ctrl);
45
46 static struct semaphore event_semaphore;        /* mutex for process loop (up if something to process) */
47 static struct semaphore event_exit;             /* guard ensure thread has exited before calling it quits */
48 static int event_finished;
49 static unsigned long pushbutton_pending;        /* = 0 */
50
51 u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id)
52 {
53         struct controller *ctrl = (struct controller *) inst_id;
54         struct slot *p_slot;
55         u8 rc = 0;
56         u8 getstatus;
57         struct pci_func *func;
58         struct event_info *taskInfo;
59
60         /* Attention Button Change */
61         dbg("shpchp:  Attention button interrupt received.\n");
62         
63         func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
64
65         /* This is the structure that tells the worker thread what to do */
66         taskInfo = &(ctrl->event_queue[ctrl->next_event]);
67         p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
68
69         p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
70         p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
71         
72         ctrl->next_event = (ctrl->next_event + 1) % 10;
73         taskInfo->hp_slot = hp_slot;
74
75         rc++;
76
77         /*
78          *  Button pressed - See if need to TAKE ACTION!!!
79          */
80         info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot);
81         taskInfo->event_type = INT_BUTTON_PRESS;
82
83         if ((p_slot->state == BLINKINGON_STATE)
84             || (p_slot->state == BLINKINGOFF_STATE)) {
85                 /* Cancel if we are still blinking; this means that we press the
86                  * attention again before the 5 sec. limit expires to cancel hot-add
87                  * or hot-remove
88                  */
89                 taskInfo->event_type = INT_BUTTON_CANCEL;
90                 info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot);
91         } else if ((p_slot->state == POWERON_STATE)
92                    || (p_slot->state == POWEROFF_STATE)) {
93                 /* Ignore if the slot is on power-on or power-off state; this 
94                  * means that the previous attention button action to hot-add or
95                  * hot-remove is undergoing
96                  */
97                 taskInfo->event_type = INT_BUTTON_IGNORE;
98                 info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot);
99         }
100
101         if (rc)
102                 up(&event_semaphore);   /* signal event thread that new event is posted */
103
104         return 0;
105
106 }
107
108 u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id)
109 {
110         struct controller *ctrl = (struct controller *) inst_id;
111         struct slot *p_slot;
112         u8 rc = 0;
113         u8 getstatus;
114         struct pci_func *func;
115         struct event_info *taskInfo;
116
117         /* Switch Change */
118         dbg("shpchp:  Switch interrupt received.\n");
119
120         func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
121
122         /* This is the structure that tells the worker thread
123          * what to do
124          */
125         taskInfo = &(ctrl->event_queue[ctrl->next_event]);
126         ctrl->next_event = (ctrl->next_event + 1) % 10;
127         taskInfo->hp_slot = hp_slot;
128
129         rc++;
130         p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
131         p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
132         p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
133         dbg("%s: Card present %x Power status %x\n", __FUNCTION__,
134                 func->presence_save, func->pwr_save);
135
136         if (getstatus) {
137                 /*
138                  * Switch opened
139                  */
140                 info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot);
141                 func->switch_save = 0;
142                 taskInfo->event_type = INT_SWITCH_OPEN;
143                 if (func->pwr_save && func->presence_save) {
144                         taskInfo->event_type = INT_POWER_FAULT;
145                         err("Surprise Removal of card\n");
146                 }
147         } else {
148                 /*
149                  *  Switch closed
150                  */
151                 info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot);
152                 func->switch_save = 0x10;
153                 taskInfo->event_type = INT_SWITCH_CLOSE;
154         }
155
156         if (rc)
157                 up(&event_semaphore);   /* signal event thread that new event is posted */
158
159         return rc;
160 }
161
162 u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id)
163 {
164         struct controller *ctrl = (struct controller *) inst_id;
165         struct slot *p_slot;
166         u8 rc = 0;
167         /*u8 temp_byte;*/
168         struct pci_func *func;
169         struct event_info *taskInfo;
170
171         /* Presence Change */
172         dbg("shpchp:  Presence/Notify input change.\n");
173
174         func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
175
176         /* This is the structure that tells the worker thread
177          * what to do
178          */
179         taskInfo = &(ctrl->event_queue[ctrl->next_event]);
180         ctrl->next_event = (ctrl->next_event + 1) % 10;
181         taskInfo->hp_slot = hp_slot;
182
183         rc++;
184         p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
185
186         /* 
187          * Save the presence state
188          */
189         p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
190         if (func->presence_save) {
191                 /*
192                  * Card Present
193                  */
194                 info("Card present on Slot(%d)\n", ctrl->first_slot + hp_slot);
195                 taskInfo->event_type = INT_PRESENCE_ON;
196         } else {
197                 /*
198                  * Not Present
199                  */
200                 info("Card not present on Slot(%d)\n", ctrl->first_slot + hp_slot);
201                 taskInfo->event_type = INT_PRESENCE_OFF;
202         }
203
204         if (rc)
205                 up(&event_semaphore);   /* signal event thread that new event is posted */
206
207         return rc;
208 }
209
210 u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id)
211 {
212         struct controller *ctrl = (struct controller *) inst_id;
213         struct slot *p_slot;
214         u8 rc = 0;
215         struct pci_func *func;
216         struct event_info *taskInfo;
217
218         /* Power fault */
219         dbg("shpchp:  Power fault interrupt received.\n");
220
221         func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
222
223         /* This is the structure that tells the worker thread
224          * what to do
225          */
226         taskInfo = &(ctrl->event_queue[ctrl->next_event]);
227         ctrl->next_event = (ctrl->next_event + 1) % 10;
228         taskInfo->hp_slot = hp_slot;
229
230         rc++;
231         p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
232
233         if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
234                 /*
235                  * Power fault Cleared
236                  */
237                 info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot);
238                 func->status = 0x00;
239                 taskInfo->event_type = INT_POWER_FAULT_CLEAR;
240         } else {
241                 /*
242                  *   Power fault
243                  */
244                 info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot);
245                 taskInfo->event_type = INT_POWER_FAULT;
246                 /* set power fault status for this board */
247                 func->status = 0xFF;
248                 info("power fault bit %x set\n", hp_slot);
249         }
250         if (rc)
251                 up(&event_semaphore);   /* signal event thread that new event is posted */
252
253         return rc;
254 }
255
256 /**
257  * shpchp_slot_create - Creates a node and adds it to the proper bus.
258  * @busnumber - bus where new node is to be located
259  *
260  * Returns pointer to the new node or NULL if unsuccessful
261  */
262 struct pci_func *shpchp_slot_create(u8 busnumber)
263 {
264         struct pci_func *new_slot;
265         struct pci_func *next;
266
267         new_slot = kmalloc(sizeof(*new_slot), GFP_KERNEL);
268
269         if (new_slot == NULL) {
270                 return(new_slot);
271         }
272
273         memset(new_slot, 0, sizeof(struct pci_func));
274
275         new_slot->next = NULL;
276         new_slot->configured = 1;
277
278         if (shpchp_slot_list[busnumber] == NULL) {
279                 shpchp_slot_list[busnumber] = new_slot;
280         } else {
281                 next = shpchp_slot_list[busnumber];
282                 while (next->next != NULL)
283                         next = next->next;
284                 next->next = new_slot;
285         }
286         return(new_slot);
287 }
288
289
290 /*
291  * slot_remove - Removes a node from the linked list of slots.
292  * @old_slot: slot to remove
293  *
294  * Returns 0 if successful, !0 otherwise.
295  */
296 static int slot_remove(struct pci_func * old_slot)
297 {
298         struct pci_func *next;
299
300         if (old_slot == NULL)
301                 return(1);
302
303         next = shpchp_slot_list[old_slot->bus];
304
305         if (next == NULL) {
306                 return(1);
307         }
308
309         if (next == old_slot) {
310                 shpchp_slot_list[old_slot->bus] = old_slot->next;
311                 kfree(old_slot);
312                 return(0);
313         }
314
315         while ((next->next != old_slot) && (next->next != NULL)) {
316                 next = next->next;
317         }
318
319         if (next->next == old_slot) {
320                 next->next = old_slot->next;
321                 kfree(old_slot);
322                 return(0);
323         } else
324                 return(2);
325 }
326
327
328 /**
329  * bridge_slot_remove - Removes a node from the linked list of slots.
330  * @bridge: bridge to remove
331  *
332  * Returns 0 if successful, !0 otherwise.
333  */
334 static int bridge_slot_remove(struct pci_func *bridge)
335 {
336         u8 subordinateBus, secondaryBus;
337         u8 tempBus;
338         struct pci_func *next;
339
340         if (bridge == NULL)
341                 return(1);
342
343         secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
344         subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
345
346         for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
347                 next = shpchp_slot_list[tempBus];
348
349                 while (!slot_remove(next)) {
350                         next = shpchp_slot_list[tempBus];
351                 }
352         }
353
354         next = shpchp_slot_list[bridge->bus];
355
356         if (next == NULL) {
357                 return(1);
358         }
359
360         if (next == bridge) {
361                 shpchp_slot_list[bridge->bus] = bridge->next;
362                 kfree(bridge);
363                 return(0);
364         }
365
366         while ((next->next != bridge) && (next->next != NULL)) {
367                 next = next->next;
368         }
369
370         if (next->next == bridge) {
371                 next->next = bridge->next;
372                 kfree(bridge);
373                 return(0);
374         } else
375                 return(2);
376 }
377
378
379 /**
380  * shpchp_slot_find - Looks for a node by bus, and device, multiple functions accessed
381  * @bus: bus to find
382  * @device: device to find
383  * @index: is 0 for first function found, 1 for the second...
384  *
385  * Returns pointer to the node if successful, %NULL otherwise.
386  */
387 struct pci_func *shpchp_slot_find(u8 bus, u8 device, u8 index)
388 {
389         int found = -1;
390         struct pci_func *func;
391
392         func = shpchp_slot_list[bus];
393
394         if ((func == NULL) || ((func->device == device) && (index == 0)))
395                 return(func);
396
397         if (func->device == device)
398                 found++;
399
400         while (func->next != NULL) {
401                 func = func->next;
402
403                 if (func->device == device)
404                         found++;
405
406                 if (found == index)
407                         return(func);
408         }
409
410         return(NULL);
411 }
412
413 static int is_bridge(struct pci_func * func)
414 {
415         /* Check the header type */
416         if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
417                 return 1;
418         else
419                 return 0;
420 }
421
422
423 /* The following routines constitute the bulk of the 
424    hotplug controller logic
425  */
426 static u32 change_bus_speed(struct controller *ctrl, struct slot *p_slot, enum pci_bus_speed speed)
427
428         u32 rc = 0;
429
430         dbg("%s: change to speed %d\n", __FUNCTION__, speed);
431         down(&ctrl->crit_sect);
432         if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) {
433                 err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
434                 up(&ctrl->crit_sect);
435                 return WRONG_BUS_FREQUENCY;
436         }
437         wait_for_ctrl_irq (ctrl);
438                 
439         if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) {
440                 err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
441                           __FUNCTION__);
442                 err("%s: Error code (%d)\n", __FUNCTION__, rc);
443                 up(&ctrl->crit_sect);
444                 return WRONG_BUS_FREQUENCY;
445         }
446         up(&ctrl->crit_sect);
447         return rc;
448 }
449
450 static u32 fix_bus_speed(struct controller *ctrl, struct slot *pslot, u8 flag, 
451 enum pci_bus_speed asp, enum pci_bus_speed bsp, enum pci_bus_speed msp)
452
453         u32 rc = 0;
454         
455         if (flag != 0) { /* Other slots on the same bus are occupied */
456                 if ( asp < bsp ) {
457                         err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bsp, asp);
458                         return WRONG_BUS_FREQUENCY;
459                 }
460         } else {
461                 /* Other slots on the same bus are empty */
462                 if (msp == bsp) {
463                 /* if adapter_speed >= bus_speed, do nothing */
464                         if (asp < bsp) {
465                                 /* 
466                                 * Try to lower bus speed to accommodate the adapter if other slots 
467                                 * on the same controller are empty
468                                 */
469                                 if ((rc = change_bus_speed(ctrl, pslot, asp)))
470                                         return rc;
471                         } 
472                 } else {
473                         if (asp < msp) {
474                                 if ((rc = change_bus_speed(ctrl, pslot, asp)))
475                                         return rc;
476                         } else {
477                                 if ((rc = change_bus_speed(ctrl, pslot, msp)))
478                                         return rc;
479                         }
480                 }
481         }
482         return rc;
483 }
484
485 /**
486  * board_added - Called after a board has been added to the system.
487  *
488  * Turns power on for the board
489  * Configures board
490  *
491  */
492 static u32 board_added(struct pci_func * func, struct controller * ctrl)
493 {
494         u8 hp_slot;
495         u8 slots_not_empty = 0;
496         u32 rc = 0;
497         struct slot *p_slot;
498         enum pci_bus_speed adapter_speed, bus_speed, max_bus_speed;
499         u8 pi, mode;
500
501         p_slot = shpchp_find_slot(ctrl, func->device);
502         hp_slot = func->device - ctrl->slot_device_offset;
503
504         dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot);
505
506         /* Wait for exclusive access to hardware */
507         down(&ctrl->crit_sect);
508
509         /* Power on slot without connecting to bus */
510         rc = p_slot->hpc_ops->power_on_slot(p_slot);
511         if (rc) {
512                 err("%s: Failed to power on slot\n", __FUNCTION__);
513                 /* Done with exclusive hardware access */
514                 up(&ctrl->crit_sect);
515                 return -1;
516         }
517                         
518         /* Wait for the command to complete */
519         wait_for_ctrl_irq (ctrl);
520         
521         rc = p_slot->hpc_ops->check_cmd_status(ctrl);
522         if (rc) {
523                 err("%s: Failed to power on slot, error code(%d)\n", __FUNCTION__, rc);
524                 /* Done with exclusive hardware access */
525                 up(&ctrl->crit_sect);
526                 return -1;
527         }
528
529         
530         if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) {
531                 if (slots_not_empty)
532                         return WRONG_BUS_FREQUENCY;
533                 
534                 if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) {
535                         err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
536                         up(&ctrl->crit_sect);
537                         return WRONG_BUS_FREQUENCY;
538                 }
539                 wait_for_ctrl_irq (ctrl);
540                 
541                 if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) {
542                         err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
543                                   __FUNCTION__);
544                         err("%s: Error code (%d)\n", __FUNCTION__, rc);
545                         up(&ctrl->crit_sect);
546                         return WRONG_BUS_FREQUENCY;
547                 }
548                 /* turn on board, blink green LED, turn off Amber LED */
549                 if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
550                         err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
551                         up(&ctrl->crit_sect);
552                         return rc;
553                 }
554                 wait_for_ctrl_irq (ctrl);
555
556                 if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) {
557                         err("%s: Failed to enable slot, error code(%d)\n", __FUNCTION__, rc);
558                         up(&ctrl->crit_sect);
559                         return rc;  
560                 }
561         }
562  
563         rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &adapter_speed);
564         /* 0 = PCI 33Mhz, 1 = PCI 66 Mhz, 2 = PCI-X 66 PA, 4 = PCI-X 66 ECC, */
565         /* 5 = PCI-X 133 PA, 7 = PCI-X 133 ECC,  0xa = PCI-X 133 Mhz 266, */
566         /* 0xd = PCI-X 133 Mhz 533 */
567         /* This encoding is different from the one used in cur_bus_speed & */
568         /* max_bus_speed */
569
570         if (rc  || adapter_speed == PCI_SPEED_UNKNOWN) {
571                 err("%s: Can't get adapter speed or bus mode mismatch\n", __FUNCTION__);
572                 /* Done with exclusive hardware access */
573                 up(&ctrl->crit_sect);
574                 return WRONG_BUS_FREQUENCY;
575         }
576
577         rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bus_speed);
578         if (rc || bus_speed == PCI_SPEED_UNKNOWN) {
579                 err("%s: Can't get bus operation speed\n", __FUNCTION__);
580                 /* Done with exclusive hardware access */
581                 up(&ctrl->crit_sect);
582                 return WRONG_BUS_FREQUENCY;
583         }
584
585         rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &max_bus_speed);
586         if (rc || max_bus_speed == PCI_SPEED_UNKNOWN) {
587                 err("%s: Can't get max bus operation speed\n", __FUNCTION__);
588                 max_bus_speed = bus_speed;
589         }
590
591         /* Done with exclusive hardware access */
592         up(&ctrl->crit_sect);
593
594         if ((rc  = p_slot->hpc_ops->get_prog_int(p_slot, &pi))) {
595                 err("%s: Can't get controller programming interface, set it to 1\n", __FUNCTION__);
596                 pi = 1;
597         }
598
599         /* Check if there are other slots or devices on the same bus */
600         if (!list_empty(&ctrl->pci_dev->subordinate->devices))
601                 slots_not_empty = 1;
602
603         dbg("%s: slots_not_empty %d, pi %d\n", __FUNCTION__, 
604                 slots_not_empty, pi);
605         dbg("adapter_speed %d, bus_speed %d, max_bus_speed %d\n", 
606                 adapter_speed, bus_speed, max_bus_speed);
607
608         if (pi == 2) {
609                 dbg("%s: In PI = %d\n", __FUNCTION__, pi);
610                 if ((rc = p_slot->hpc_ops->get_mode1_ECC_cap(p_slot, &mode))) {
611                         err("%s: Can't get Mode1_ECC, set mode to 0\n", __FUNCTION__);
612                         mode = 0;
613                 }
614
615                 switch (adapter_speed) {
616                 case PCI_SPEED_133MHz_PCIX_533:
617                 case PCI_SPEED_133MHz_PCIX_266:
618                         if ((bus_speed != adapter_speed) &&
619                            ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) 
620                                 return rc;
621                         break;  
622                 case PCI_SPEED_133MHz_PCIX_ECC:
623                 case PCI_SPEED_133MHz_PCIX:
624                         if (mode) { /* Bus - Mode 1 ECC */
625                                 if ((bus_speed != 0x7) &&
626                                    ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) 
627                                         return rc;
628                         } else {
629                                 if ((bus_speed != 0x4) &&
630                                    ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) 
631                                         return rc;
632                         }
633                         break;
634                 case PCI_SPEED_66MHz_PCIX_ECC:
635                 case PCI_SPEED_66MHz_PCIX:
636                         if (mode) { /* Bus - Mode 1 ECC */
637                                 if ((bus_speed != 0x5) &&
638                                    ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) 
639                                         return rc;
640                         } else {
641                                 if ((bus_speed != 0x2) &&
642                                    ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) 
643                                         return rc;
644                         }
645                         break;
646                 case PCI_SPEED_66MHz:
647                         if ((bus_speed != 0x1) &&
648                            ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed)))) 
649                                 return rc;
650                         break;  
651                 case PCI_SPEED_33MHz:
652                         if (bus_speed > 0x0) {
653                                 if (slots_not_empty == 0) {
654                                         if ((rc = change_bus_speed(ctrl, p_slot, adapter_speed)))
655                                                 return rc;
656                                 } else {
657                                         err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
658                                         return WRONG_BUS_FREQUENCY;
659                                 }
660                         }
661                         break;
662                 default:
663                         err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
664                         return WRONG_BUS_FREQUENCY;
665                 }
666         } else {
667                 /* If adpater_speed == bus_speed, nothing to do here */
668                 dbg("%s: In PI = %d\n", __FUNCTION__, pi);
669                 if ((adapter_speed != bus_speed) &&
670                    ((rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, adapter_speed, bus_speed, max_bus_speed))))
671                                 return rc;
672         }
673
674         down(&ctrl->crit_sect);
675         /* turn on board, blink green LED, turn off Amber LED */
676         if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) {
677                 err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
678                 up(&ctrl->crit_sect);
679                 return rc;
680         }
681         wait_for_ctrl_irq (ctrl);
682
683         if ((rc = p_slot->hpc_ops->check_cmd_status(ctrl))) {
684                 err("%s: Failed to enable slot, error code(%d)\n", __FUNCTION__, rc);
685                 up(&ctrl->crit_sect);
686                 return rc;  
687         }
688
689         up(&ctrl->crit_sect);
690
691         /* Wait for ~1 second */
692         dbg("%s: before long_delay\n", __FUNCTION__);
693         wait_for_ctrl_irq (ctrl);
694         dbg("%s: after long_delay\n", __FUNCTION__);
695
696         dbg("%s: func status = %x\n", __FUNCTION__, func->status);
697         /* Check for a power fault */
698         if (func->status == 0xFF) {
699                 /* power fault occurred, but it was benign */
700                 dbg("%s: power fault\n", __FUNCTION__);
701                 rc = POWER_FAILURE;
702                 func->status = 0;
703                 goto err_exit;
704         }
705
706         if (shpchp_configure_device(p_slot)) {
707                 err("Cannot add device at 0x%x:0x%x\n", p_slot->bus,
708                                 p_slot->device);
709                 goto err_exit;
710         }
711
712         shpchp_save_slot_config(ctrl, func);
713
714         func->status = 0;
715         func->switch_save = 0x10;
716         func->is_a_board = 0x01;
717         func->pwr_save = 1;
718
719         /* Wait for exclusive access to hardware */
720         down(&ctrl->crit_sect);
721
722         p_slot->hpc_ops->green_led_on(p_slot);
723
724         /* Wait for the command to complete */
725         wait_for_ctrl_irq (ctrl);
726
727         /* Done with exclusive hardware access */
728         up(&ctrl->crit_sect);
729
730         return 0;
731
732 err_exit:
733         /* Wait for exclusive access to hardware */
734         down(&ctrl->crit_sect);
735
736         /* turn off slot, turn on Amber LED, turn off Green LED */
737         rc = p_slot->hpc_ops->slot_disable(p_slot);
738         if (rc) {
739                 err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
740                 /* Done with exclusive hardware access */
741                 up(&ctrl->crit_sect);
742                 return rc;
743         }
744         /* Wait for the command to complete */
745         wait_for_ctrl_irq (ctrl);
746
747         rc = p_slot->hpc_ops->check_cmd_status(ctrl);
748         if (rc) {
749                 err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc);
750                 /* Done with exclusive hardware access */
751                 up(&ctrl->crit_sect);
752                 return rc;
753         }
754
755         /* Done with exclusive hardware access */
756         up(&ctrl->crit_sect);
757
758         return(rc);
759 }
760
761
762 /**
763  * remove_board - Turns off slot and LED's
764  *
765  */
766 static u32 remove_board(struct pci_func *func, struct controller *ctrl)
767 {
768         u8 device;
769         u8 hp_slot;
770         u32 rc;
771         struct slot *p_slot;
772
773         if (func == NULL)
774                 return(1);
775
776         if (shpchp_unconfigure_device(func))
777                 return(1);
778
779         device = func->device;
780
781         hp_slot = func->device - ctrl->slot_device_offset;
782         p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
783
784         dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
785
786         /* Change status to shutdown */
787         if (func->is_a_board)
788                 func->status = 0x01;
789         func->configured = 0;
790
791         /* Wait for exclusive access to hardware */
792         down(&ctrl->crit_sect);
793
794         /* turn off slot, turn on Amber LED, turn off Green LED */
795         rc = p_slot->hpc_ops->slot_disable(p_slot);
796         if (rc) {
797                 err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
798                 /* Done with exclusive hardware access */
799                 up(&ctrl->crit_sect);
800                 return rc;
801         }
802         /* Wait for the command to complete */
803         wait_for_ctrl_irq (ctrl);
804
805         rc = p_slot->hpc_ops->check_cmd_status(ctrl);
806         if (rc) {
807                 err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc);
808                 /* Done with exclusive hardware access */
809                 up(&ctrl->crit_sect);
810                 return rc;  
811         }
812         
813         rc = p_slot->hpc_ops->set_attention_status(p_slot, 0);
814         if (rc) {
815                 err("%s: Issue of Set Attention command failed\n", __FUNCTION__);
816                 /* Done with exclusive hardware access */
817                 up(&ctrl->crit_sect);
818                 return rc;
819         }
820         /* Wait for the command to complete */
821         wait_for_ctrl_irq (ctrl);
822
823         /* Done with exclusive hardware access */
824         up(&ctrl->crit_sect);
825
826         if (ctrl->add_support) {
827                 while (func) {
828                         if (is_bridge(func)) {
829                                 dbg("PCI Bridge Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, 
830                                         func->device, func->function);
831                                 bridge_slot_remove(func);
832                         } else
833                                 dbg("PCI Function Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, 
834                                         func->device, func->function);
835                                 slot_remove(func);
836
837                         func = shpchp_slot_find(ctrl->slot_bus, device, 0);
838                 }
839
840                 /* Setup slot structure with entry for empty slot */
841                 func = shpchp_slot_create(ctrl->slot_bus);
842
843                 if (func == NULL) {
844                         return(1);
845                 }
846
847                 func->bus = ctrl->slot_bus;
848                 func->device = device;
849                 func->function = 0;
850                 func->configured = 0;
851                 func->switch_save = 0x10;
852                 func->pwr_save = 0;
853                 func->is_a_board = 0;
854         }
855
856         return 0;
857 }
858
859
860 static void pushbutton_helper_thread (unsigned long data)
861 {
862         pushbutton_pending = data;
863
864         up(&event_semaphore);
865 }
866
867
868 /**
869  * shpchp_pushbutton_thread
870  *
871  * Scheduled procedure to handle blocking stuff for the pushbuttons
872  * Handles all pending events and exits.
873  *
874  */
875 static void shpchp_pushbutton_thread (unsigned long slot)
876 {
877         struct slot *p_slot = (struct slot *) slot;
878         u8 getstatus;
879         
880         pushbutton_pending = 0;
881
882         if (!p_slot) {
883                 dbg("%s: Error! slot NULL\n", __FUNCTION__);
884                 return;
885         }
886
887         p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
888         if (getstatus) {
889                 p_slot->state = POWEROFF_STATE;
890                 dbg("In power_down_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
891
892                 shpchp_disable_slot(p_slot);
893                 p_slot->state = STATIC_STATE;
894         } else {
895                 p_slot->state = POWERON_STATE;
896                 dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
897
898                 if (shpchp_enable_slot(p_slot)) {
899                         /* Wait for exclusive access to hardware */
900                         down(&p_slot->ctrl->crit_sect);
901
902                         p_slot->hpc_ops->green_led_off(p_slot);
903
904                         /* Wait for the command to complete */
905                         wait_for_ctrl_irq (p_slot->ctrl);
906
907                         /* Done with exclusive hardware access */
908                         up(&p_slot->ctrl->crit_sect);
909                 }
910                 p_slot->state = STATIC_STATE;
911         }
912
913         return;
914 }
915
916
917 /* this is the main worker thread */
918 static int event_thread(void* data)
919 {
920         struct controller *ctrl;
921         lock_kernel();
922         daemonize("shpchpd_event");
923         unlock_kernel();
924
925         while (1) {
926                 dbg("!!!!event_thread sleeping\n");
927                 down_interruptible (&event_semaphore);
928                 dbg("event_thread woken finished = %d\n", event_finished);
929                 if (event_finished || signal_pending(current))
930                         break;
931                 /* Do stuff here */
932                 if (pushbutton_pending)
933                         shpchp_pushbutton_thread(pushbutton_pending);
934                 else
935                         for (ctrl = shpchp_ctrl_list; ctrl; ctrl=ctrl->next)
936                                 interrupt_event_handler(ctrl);
937         }
938         dbg("event_thread signals exit\n");
939         up(&event_exit);
940         return 0;
941 }
942
943 int shpchp_event_start_thread (void)
944 {
945         int pid;
946
947         /* initialize our semaphores */
948         init_MUTEX_LOCKED(&event_exit);
949         event_finished=0;
950
951         init_MUTEX_LOCKED(&event_semaphore);
952         pid = kernel_thread(event_thread, NULL, 0);
953
954         if (pid < 0) {
955                 err ("Can't start up our event thread\n");
956                 return -1;
957         }
958         dbg("Our event thread pid = %d\n", pid);
959         return 0;
960 }
961
962
963 void shpchp_event_stop_thread (void)
964 {
965         event_finished = 1;
966         dbg("event_thread finish command given\n");
967         up(&event_semaphore);
968         dbg("wait for event_thread to exit\n");
969         down(&event_exit);
970 }
971
972
973 static int update_slot_info (struct slot *slot)
974 {
975         struct hotplug_slot_info *info;
976         int result;
977
978         info = kmalloc(sizeof(*info), GFP_KERNEL);
979         if (!info)
980                 return -ENOMEM;
981
982         slot->hpc_ops->get_power_status(slot, &(info->power_status));
983         slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
984         slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
985         slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
986
987         result = pci_hp_change_slot_info(slot->hotplug_slot, info);
988         kfree (info);
989         return result;
990 }
991
992 static void interrupt_event_handler(struct controller *ctrl)
993 {
994         int loop = 0;
995         int change = 1;
996         struct pci_func *func;
997         u8 hp_slot;
998         u8 getstatus;
999         struct slot *p_slot;
1000
1001         dbg("%s:\n", __FUNCTION__);
1002         while (change) {
1003                 change = 0;
1004
1005                 for (loop = 0; loop < 10; loop++) {
1006                         if (ctrl->event_queue[loop].event_type != 0) {
1007                                 dbg("%s:loop %x event_type %x\n", __FUNCTION__, loop, 
1008                                         ctrl->event_queue[loop].event_type);
1009                                 hp_slot = ctrl->event_queue[loop].hp_slot;
1010
1011                                 func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
1012
1013                                 p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
1014
1015                                 dbg("%s: hp_slot %d, func %p, p_slot %p\n", __FUNCTION__, hp_slot, func, p_slot);
1016
1017                                 if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) {
1018                                         dbg("%s: button cancel\n", __FUNCTION__);
1019                                         del_timer(&p_slot->task_event);
1020
1021                                         switch (p_slot->state) {
1022                                         case BLINKINGOFF_STATE:
1023                                                 /* Wait for exclusive access to hardware */
1024                                                 down(&ctrl->crit_sect);
1025
1026                                                 p_slot->hpc_ops->green_led_on(p_slot);
1027                                                 /* Wait for the command to complete */
1028                                                 wait_for_ctrl_irq (ctrl);
1029
1030                                                 p_slot->hpc_ops->set_attention_status(p_slot, 0);
1031
1032                                                 /* Wait for the command to complete */
1033                                                 wait_for_ctrl_irq (ctrl);
1034
1035                                                 /* Done with exclusive hardware access */
1036                                                 up(&ctrl->crit_sect);
1037                                                 break;
1038                                         case BLINKINGON_STATE:
1039                                                 /* Wait for exclusive access to hardware */
1040                                                 down(&ctrl->crit_sect);
1041
1042                                                 p_slot->hpc_ops->green_led_off(p_slot);
1043                                                 /* Wait for the command to complete */
1044                                                 wait_for_ctrl_irq (ctrl);
1045
1046                                                 p_slot->hpc_ops->set_attention_status(p_slot, 0);
1047                                                 /* Wait for the command to complete */
1048                                                 wait_for_ctrl_irq (ctrl);
1049
1050                                                 /* Done with exclusive hardware access */
1051                                                 up(&ctrl->crit_sect);
1052
1053                                                 break;
1054                                         default:
1055                                                 warn("Not a valid state\n");
1056                                                 return;
1057                                         }
1058                                         info(msg_button_cancel, p_slot->number);
1059                                         p_slot->state = STATIC_STATE;
1060                                 } else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
1061                                         /* Button Pressed (No action on 1st press...) */
1062                                         dbg("%s: Button pressed\n", __FUNCTION__);
1063
1064                                         p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
1065                                         if (getstatus) {
1066                                                 /* slot is on */
1067                                                 dbg("%s: slot is on\n", __FUNCTION__);
1068                                                 p_slot->state = BLINKINGOFF_STATE;
1069                                                 info(msg_button_off, p_slot->number);
1070                                         } else {
1071                                                 /* slot is off */
1072                                                 dbg("%s: slot is off\n", __FUNCTION__);
1073                                                 p_slot->state = BLINKINGON_STATE;
1074                                                 info(msg_button_on, p_slot->number);
1075                                         }
1076
1077                                         /* Wait for exclusive access to hardware */
1078                                         down(&ctrl->crit_sect);
1079
1080                                         /* blink green LED and turn off amber */
1081                                         p_slot->hpc_ops->green_led_blink(p_slot);
1082                                         /* Wait for the command to complete */
1083                                         wait_for_ctrl_irq (ctrl);
1084                                         
1085                                         p_slot->hpc_ops->set_attention_status(p_slot, 0);
1086
1087                                         /* Wait for the command to complete */
1088                                         wait_for_ctrl_irq (ctrl);
1089
1090                                         /* Done with exclusive hardware access */
1091                                         up(&ctrl->crit_sect);
1092
1093                                         init_timer(&p_slot->task_event);
1094                                         p_slot->task_event.expires = jiffies + 5 * HZ;   /* 5 second delay */
1095                                         p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread;
1096                                         p_slot->task_event.data = (unsigned long) p_slot;
1097
1098                                         dbg("%s: add_timer p_slot = %p\n", __FUNCTION__,(void *) p_slot);
1099                                         add_timer(&p_slot->task_event);
1100                                 } else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
1101                                         /***********POWER FAULT********************/
1102                                         dbg("%s: power fault\n", __FUNCTION__);
1103                                         /* Wait for exclusive access to hardware */
1104                                         down(&ctrl->crit_sect);
1105
1106                                         p_slot->hpc_ops->set_attention_status(p_slot, 1);
1107                                         /* Wait for the command to complete */
1108                                         wait_for_ctrl_irq (ctrl);
1109                                         
1110                                         p_slot->hpc_ops->green_led_off(p_slot);
1111                                         /* Wait for the command to complete */
1112                                         wait_for_ctrl_irq (ctrl);
1113
1114                                         /* Done with exclusive hardware access */
1115                                         up(&ctrl->crit_sect);
1116                                 } else {
1117                                         /* refresh notification */
1118                                         if (p_slot)
1119                                                 update_slot_info(p_slot);
1120                                 }
1121
1122                                 ctrl->event_queue[loop].event_type = 0;
1123
1124                                 change = 1;
1125                         }
1126                 }               /* End of FOR loop */
1127         }
1128
1129         return;
1130 }
1131
1132
1133 int shpchp_enable_slot (struct slot *p_slot)
1134 {
1135         u8 getstatus = 0;
1136         int rc;
1137         struct pci_func *func;
1138
1139         func = shpchp_slot_find(p_slot->bus, p_slot->device, 0);
1140         if (!func) {
1141                 dbg("%s: Error! slot NULL\n", __FUNCTION__);
1142                 return -ENODEV;
1143         }
1144
1145         /* Check to see if (latch closed, card present, power off) */
1146         down(&p_slot->ctrl->crit_sect);
1147         rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
1148         if (rc || !getstatus) {
1149                 info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
1150                 up(&p_slot->ctrl->crit_sect);
1151                 return -ENODEV;
1152         }
1153         rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
1154         if (rc || getstatus) {
1155                 info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
1156                 up(&p_slot->ctrl->crit_sect);
1157                 return -ENODEV;
1158         }
1159         rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
1160         if (rc || getstatus) {
1161                 info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number);
1162                 up(&p_slot->ctrl->crit_sect);
1163                 return -ENODEV;
1164         }
1165         up(&p_slot->ctrl->crit_sect);
1166
1167         slot_remove(func);
1168
1169         func = shpchp_slot_create(p_slot->bus);
1170         if (func == NULL)
1171                 return -ENOMEM;
1172
1173         func->bus = p_slot->bus;
1174         func->device = p_slot->device;
1175         func->function = 0;
1176         func->configured = 0;
1177         func->is_a_board = 1;
1178
1179         /* We have to save the presence info for these slots */
1180         p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
1181         p_slot->hpc_ops->get_power_status(p_slot, &(func->pwr_save));
1182         dbg("%s: func->pwr_save %x\n", __FUNCTION__, func->pwr_save);
1183         p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
1184         func->switch_save = !getstatus? 0x10:0;
1185
1186         rc = board_added(func, p_slot->ctrl);
1187         if (rc) {
1188                 if (is_bridge(func))
1189                         bridge_slot_remove(func);
1190                 else
1191                         slot_remove(func);
1192
1193                 /* Setup slot structure with entry for empty slot */
1194                 func = shpchp_slot_create(p_slot->bus);
1195                 if (func == NULL)
1196                         return -ENOMEM; /* Out of memory */
1197
1198                 func->bus = p_slot->bus;
1199                 func->device = p_slot->device;
1200                 func->function = 0;
1201                 func->configured = 0;
1202                 func->is_a_board = 1;
1203
1204                 /* We have to save the presence info for these slots */
1205                 p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
1206                 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
1207                 func->switch_save = !getstatus? 0x10:0;
1208         }
1209
1210         if (p_slot)
1211                 update_slot_info(p_slot);
1212
1213         return rc;
1214 }
1215
1216
1217 int shpchp_disable_slot (struct slot *p_slot)
1218 {
1219         u8 class_code, header_type, BCR;
1220         u8 index = 0;
1221         u8 getstatus = 0;
1222         u32 rc = 0;
1223         int ret = 0;
1224         unsigned int devfn;
1225         struct pci_bus *pci_bus;
1226         struct pci_func *func;
1227
1228         if (!p_slot->ctrl)
1229                 return -ENODEV;
1230
1231         pci_bus = p_slot->ctrl->pci_dev->subordinate;
1232
1233         /* Check to see if (latch closed, card present, power on) */
1234         down(&p_slot->ctrl->crit_sect);
1235
1236         ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
1237         if (ret || !getstatus) {
1238                 info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
1239                 up(&p_slot->ctrl->crit_sect);
1240                 return -ENODEV;
1241         }
1242         ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
1243         if (ret || getstatus) {
1244                 info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
1245                 up(&p_slot->ctrl->crit_sect);
1246                 return -ENODEV;
1247         }
1248         ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
1249         if (ret || !getstatus) {
1250                 info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number);
1251                 up(&p_slot->ctrl->crit_sect);
1252                 return -ENODEV;
1253         }
1254         up(&p_slot->ctrl->crit_sect);
1255
1256         func = shpchp_slot_find(p_slot->bus, p_slot->device, index++);
1257
1258         /* Make sure there are no video controllers here
1259          * for all func of p_slot
1260          */
1261         while (func && !rc) {
1262                 pci_bus->number = func->bus;
1263                 devfn = PCI_DEVFN(func->device, func->function);
1264
1265                 /* Check the Class Code */
1266                 rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
1267                 if (rc)
1268                         return -ENODEV;
1269
1270                 if (class_code == PCI_BASE_CLASS_DISPLAY) {
1271                         /* Display/Video adapter (not supported) */
1272                         rc = REMOVE_NOT_SUPPORTED;
1273                 } else {
1274                         /* See if it's a bridge */
1275                         rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
1276                         if (rc)
1277                                 return -ENODEV;
1278
1279                         /* If it's a bridge, check the VGA Enable bit */
1280                         if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
1281                                 rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR);
1282                                 if (rc)
1283                                         return -ENODEV;
1284
1285                                 /* If the VGA Enable bit is set, remove isn't supported */
1286                                 if (BCR & PCI_BRIDGE_CTL_VGA) {
1287                                         rc = REMOVE_NOT_SUPPORTED;
1288                                 }
1289                         }
1290                 }
1291
1292                 func = shpchp_slot_find(p_slot->bus, p_slot->device, index++);
1293         }
1294
1295         func = shpchp_slot_find(p_slot->bus, p_slot->device, 0);
1296         if ((func != NULL) && !rc) {
1297                 rc = remove_board(func, p_slot->ctrl);
1298         } else if (!rc)
1299                 rc = -ENODEV;
1300
1301         if (p_slot)
1302                 update_slot_info(p_slot);
1303
1304         return rc;
1305 }
1306