Newer
Older
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
26
27
28
29
30
31
32
33
34
35
36
37
/*
* Compaq 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.
*
* 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.
*
* Send feedback to <greg@kroah.com>
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <linux/kthread.h>
#include "cpqphp.h"
static u32 configure_new_device(struct controller* ctrl, struct pci_func *func,
u8 behind_bridge, struct resource_lists *resources);
static int configure_new_function(struct controller* ctrl, struct pci_func *func,
u8 behind_bridge, struct resource_lists *resources);
static void interrupt_event_handler(struct controller *ctrl);
static struct task_struct *cpqhp_event_thread;
static unsigned long pushbutton_pending; /* = 0 */
/* delay is in jiffies to wait for */
static void long_delay(int delay)
{
/*
* XXX(hch): if someone is bored please convert all callers
* to call msleep_interruptible directly. They really want
* to specify timeouts in natural units and spend a lot of
* effort converting them to jiffies..
*/
msleep_interruptible(jiffies_to_msecs(delay));
}
/* FIXME: The following line needs to be somewhere else... */
#define WRONG_BUS_FREQUENCY 0x07
static u8 handle_switch_change(u8 change, struct controller * ctrl)
{
int hp_slot;
u8 rc = 0;
u16 temp_word;
struct pci_func *func;
struct event_info *taskInfo;
if (!change)
return 0;
/* Switch Change */
dbg("cpqsbd: Switch interrupt received.\n");
for (hp_slot = 0; hp_slot < 6; hp_slot++) {
if (change & (0x1L << hp_slot)) {
func = cpqhp_slot_find(ctrl->bus,
(hp_slot + ctrl->slot_device_offset), 0);
/* this is the structure that tells the worker thread
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
temp_word = ctrl->ctrl_int_comp >> 16;
func->presence_save = (temp_word >> hp_slot) & 0x01;
func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
func->switch_save = 0;
taskInfo->event_type = INT_SWITCH_OPEN;
} else {
func->switch_save = 0x10;
taskInfo->event_type = INT_SWITCH_CLOSE;
}
}
}
return rc;
}
/**
* cpqhp_find_slot - find the struct slot of given device
* @ctrl: scan lots of this controller
* @device: the device id to find
*/
static struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device)
{
struct slot *slot = ctrl->slot;
while (slot && (slot->device != device))
slot = slot->next;
return slot;
}
static u8 handle_presence_change(u16 change, struct controller * ctrl)
{
int hp_slot;
u8 rc = 0;
u8 temp_byte;
u16 temp_word;
struct pci_func *func;
struct event_info *taskInfo;
struct slot *p_slot;
if (!change)
return 0;
dbg("cpqsbd: Presence/Notify input change.\n");
dbg(" Changed bits are 0x%4.4x\n", change );
for (hp_slot = 0; hp_slot < 6; hp_slot++) {
if (change & (0x0101 << hp_slot)) {
func = cpqhp_slot_find(ctrl->bus,
(hp_slot + ctrl->slot_device_offset), 0);
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
p_slot = cpqhp_find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4));
if (!p_slot)
return 0;
/* If the switch closed, must be a button
* If not in button mode, nevermind
*/
if (func->switch_save && (ctrl->push_button == 1)) {
temp_word = ctrl->ctrl_int_comp >> 16;
temp_byte = (temp_word >> hp_slot) & 0x01;
temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02;
if (temp_byte != func->presence_save) {
dbg("hp_slot %d button pressed\n", hp_slot);
taskInfo->event_type = INT_BUTTON_PRESS;
} else {
dbg("hp_slot %d button released\n", hp_slot);
taskInfo->event_type = INT_BUTTON_RELEASE;
/* Cancel if we are still blinking */
if ((p_slot->state == BLINKINGON_STATE)
|| (p_slot->state == BLINKINGOFF_STATE)) {
taskInfo->event_type = INT_BUTTON_CANCEL;
dbg("hp_slot %d button cancel\n", hp_slot);
} else if ((p_slot->state == POWERON_STATE)
|| (p_slot->state == POWEROFF_STATE)) {
/* info(msg_button_ignore, p_slot->number); */
taskInfo->event_type = INT_BUTTON_IGNORE;
dbg("hp_slot %d button ignore\n", hp_slot);
}
}
} else {
/* Switch is open, assume a presence change
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
temp_word = ctrl->ctrl_int_comp >> 16;
func->presence_save = (temp_word >> hp_slot) & 0x01;
func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
if ((!(ctrl->ctrl_int_comp & (0x010000 << hp_slot))) ||
(!(ctrl->ctrl_int_comp & (0x01000000 << hp_slot)))) {
/* Present */
taskInfo->event_type = INT_PRESENCE_ON;
} else {
/* Not Present */
taskInfo->event_type = INT_PRESENCE_OFF;
}
}
}
}
return rc;
}
static u8 handle_power_fault(u8 change, struct controller * ctrl)
{
int hp_slot;
u8 rc = 0;
struct pci_func *func;
struct event_info *taskInfo;
if (!change)
return 0;
info("power fault interrupt\n");
for (hp_slot = 0; hp_slot < 6; hp_slot++) {
if (change & (0x01 << hp_slot)) {
func = cpqhp_slot_find(ctrl->bus,
(hp_slot + ctrl->slot_device_offset), 0);
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) {
func->status = 0x00;
taskInfo->event_type = INT_POWER_FAULT_CLEAR;
} else {
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
taskInfo->event_type = INT_POWER_FAULT;
if (ctrl->rev < 4) {
amber_LED_on (ctrl, hp_slot);
green_LED_off (ctrl, hp_slot);
set_SOGO (ctrl);
/* this is a fatal condition, we want
* to crash the machine to protect from
* data corruption. simulated_NMI
* shouldn't ever return */
/* FIXME
simulated_NMI(hp_slot, ctrl); */
/* The following code causes a software
* crash just in case simulated_NMI did
* return */
/*FIXME
panic(msg_power_fault); */
} else {
/* set power fault status for this board */
func->status = 0xFF;
info("power fault bit %x set\n", hp_slot);
}
}
}
}
return rc;
}
/**
* sort_by_size - sort nodes on the list by their length, smallest first.
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
* @head: list to sort
*/
static int sort_by_size(struct pci_resource **head)
{
struct pci_resource *current_res;
struct pci_resource *next_res;
int out_of_order = 1;
if (!(*head))
return 1;
if (!((*head)->next))
return 0;
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->length > (*head)->next->length)) {
out_of_order++;
current_res = *head;
*head = (*head)->next;
current_res->next = (*head)->next;
(*head)->next = current_res;
}
current_res = *head;
while (current_res->next && current_res->next->next) {
if (current_res->next->length > current_res->next->next->length) {
out_of_order++;
next_res = current_res->next;
current_res->next = current_res->next->next;
current_res = current_res->next;
next_res->next = current_res->next;
current_res->next = next_res;
} else
current_res = current_res->next;
}
} /* End of out_of_order loop */
return 0;
}
/**
* sort_by_max_size - sort nodes on the list by their length, largest first.
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
* @head: list to sort
*/
static int sort_by_max_size(struct pci_resource **head)
{
struct pci_resource *current_res;
struct pci_resource *next_res;
int out_of_order = 1;
if (!(*head))
return 1;
if (!((*head)->next))
return 0;
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->length < (*head)->next->length)) {
out_of_order++;
current_res = *head;
*head = (*head)->next;
current_res->next = (*head)->next;
(*head)->next = current_res;
}
current_res = *head;
while (current_res->next && current_res->next->next) {
if (current_res->next->length < current_res->next->next->length) {
out_of_order++;
next_res = current_res->next;
current_res->next = current_res->next->next;
current_res = current_res->next;
next_res->next = current_res->next;
current_res->next = next_res;
} else
current_res = current_res->next;
}
} /* End of out_of_order loop */
return 0;
}
/**
* do_pre_bridge_resource_split - find node of resources that are unused
* @head: new list head
* @orig_head: original list head
* @alignment: max node size (?)
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
*/
static struct pci_resource *do_pre_bridge_resource_split(struct pci_resource **head,
struct pci_resource **orig_head, u32 alignment)
{
struct pci_resource *prevnode = NULL;
struct pci_resource *node;
struct pci_resource *split_node;
u32 rc;
u32 temp_dword;
dbg("do_pre_bridge_resource_split\n");
if (!(*head) || !(*orig_head))
return NULL;
rc = cpqhp_resource_sort_and_combine(head);
if (rc)
return NULL;
if ((*head)->base != (*orig_head)->base)
return NULL;
if ((*head)->length == (*orig_head)->length)
return NULL;
/* If we got here, there the bridge requires some of the resource, but
* we may be able to split some off of the front
*/
node = *head;
if (node->length & (alignment -1)) {
/* this one isn't an aligned length, so we'll make a new entry
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
return NULL;
temp_dword = (node->length | (alignment-1)) + 1 - alignment;
split_node->base = node->base;
split_node->length = temp_dword;
node->length -= temp_dword;
node->base += split_node->length;
/* Put it in the list */
*head = split_node;
split_node->next = node;
}
if (node->length < alignment)
return NULL;
/* Now unlink it */
if (*head == node) {
*head = node->next;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
}
node->next = NULL;
return node;
}
/**
* do_bridge_resource_split - find one node of resources that aren't in use
* @head: list head
* @alignment: max node size (?)
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
*/
static struct pci_resource *do_bridge_resource_split(struct pci_resource **head, u32 alignment)
{
struct pci_resource *prevnode = NULL;
struct pci_resource *node;
u32 rc;
u32 temp_dword;
rc = cpqhp_resource_sort_and_combine(head);
if (rc)
return NULL;
node = *head;
while (node->next) {
prevnode = node;
node = node->next;
kfree(prevnode);
}
if (node->length < alignment)
goto error;
if (node->base & (alignment - 1)) {
/* Short circuit if adjusted size is too small */
temp_dword = (node->base | (alignment-1)) + 1;
if ((node->length - (temp_dword - node->base)) < alignment)
goto error;
node->length -= (temp_dword - node->base);
node->base = temp_dword;
}
if (node->length & (alignment - 1))
/* There's stuff in use after this node */
goto error;
return node;
error:
kfree(node);
return NULL;
}
/**
* get_io_resource - find first node of given size not in ISA aliasing window.
* @head: list to search
* @size: size of node to find, must be a power of two.
*
* Description: This function sorts the resource list by size and then returns
* returns the first node of "size" length that is not in the ISA aliasing
* window. If it finds a node larger than "size" it will split it up.
*/
static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size)
{
struct pci_resource *prevnode;
struct pci_resource *node;
struct pci_resource *split_node;
u32 temp_dword;
if (!(*head))
return NULL;
if (cpqhp_resource_sort_and_combine(head))
return NULL;
for (node = *head; node; node = node->next) {
if (node->length < size)
continue;
if (node->base & (size - 1)) {
/* this one isn't base aligned properly
* so we'll make a new entry and split it up
*/
temp_dword = (node->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
if ((node->length - (temp_dword - node->base)) < size)
continue;
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
return NULL;
split_node->base = node->base;
split_node->length = temp_dword - node->base;
node->base = temp_dword;
node->length -= split_node->length;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of non-aligned base */
/* Don't need to check if too small since we already did */
if (node->length > size) {
/* this one is longer than we need
* so we'll make a new entry and split it up
*/
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
return NULL;
split_node->base = node->base + size;
split_node->length = node->length - size;
node->length = size;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of too big on top end */
/* For IO make sure it's not in the ISA aliasing space */
if (node->base & 0x300L)
continue;
/* If we got here, then it is the right size
* Now take it out of the list and break
*/
if (*head == node) {
*head = node->next;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
}
node->next = NULL;
break;
}
return node;
}
/**
* get_max_resource - get largest node which has at least the given size.
* @head: the list to search the node in
* @size: the minimum size of the node to find
*
* Description: Gets the largest node that is at least "size" big from the
* list pointed to by head. It aligns the node on top and bottom
* to "size" alignment before returning it.
*/
static struct pci_resource *get_max_resource(struct pci_resource **head, u32 size)
{
struct pci_resource *max;
struct pci_resource *temp;
struct pci_resource *split_node;
u32 temp_dword;
if (cpqhp_resource_sort_and_combine(head))
return NULL;
if (sort_by_max_size(head))
return NULL;
for (max = *head; max; max = max->next) {
/* If not big enough we could probably just bail,
* instead we'll continue to the next.
*/
if (max->length < size)
continue;
if (max->base & (size - 1)) {
/* this one isn't base aligned properly
* so we'll make a new entry and split it up
*/
temp_dword = (max->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
if ((max->length - (temp_dword - max->base)) < size)
continue;
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
return NULL;
split_node->base = max->base;
split_node->length = temp_dword - max->base;
max->base = temp_dword;
max->length -= split_node->length;
split_node->next = max->next;
max->next = split_node;
}
if ((max->base + max->length) & (size - 1)) {
/* this one isn't end aligned properly at the top
* so we'll make a new entry and split it up
*/
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
return NULL;
temp_dword = ((max->base + max->length) & ~(size - 1));
split_node->base = temp_dword;
split_node->length = max->length + max->base
- split_node->base;
max->length -= split_node->length;
split_node->next = max->next;
max->next = split_node;
}
/* Make sure it didn't shrink too much when we aligned it */
if (max->length < size)
continue;
/* Now take it out of the list */
temp = *head;
if (temp == max) {
*head = max->next;
} else {
while (temp && temp->next != max) {
temp = temp->next;
}
temp->next = max->next;
}
max->next = NULL;
break;
}
return max;
}
/**
* get_resource - find resource of given size and split up larger ones.
* @head: the list to search for resources
* @size: the size limit to use
*
* Description: This function sorts the resource list by size and then
* returns the first node of "size" length. If it finds a node
* larger than "size" it will split it up.
*
* size must be a power of two.
*/
static struct pci_resource *get_resource(struct pci_resource **head, u32 size)
{
struct pci_resource *prevnode;
struct pci_resource *node;
struct pci_resource *split_node;
u32 temp_dword;
if (cpqhp_resource_sort_and_combine(head))
return NULL;
if (sort_by_size(head))
return NULL;
for (node = *head; node; node = node->next) {
dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",
__func__, size, node, node->base, node->length);
if (node->length < size)
continue;
if (node->base & (size - 1)) {
dbg("%s: not aligned\n", __func__);
* so we'll make a new entry and split it up
*/
temp_dword = (node->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
if ((node->length - (temp_dword - node->base)) < size)
continue;
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
return NULL;
split_node->base = node->base;
split_node->length = temp_dword - node->base;
node->base = temp_dword;
node->length -= split_node->length;
split_node->next = node->next;
node->next = split_node;
} /* End of non-aligned base */
/* Don't need to check if too small since we already did */
if (node->length > size) {
dbg("%s: too big\n", __func__);
* so we'll make a new entry and split it up
*/
split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
if (!split_node)
return NULL;
split_node->base = node->base + size;
split_node->length = node->length - size;
node->length = size;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of too big on top end */
dbg("%s: got one!!!\n", __func__);
/* If we got here, then it is the right size
* Now take it out of the list */
if (*head == node) {
*head = node->next;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
}
node->next = NULL;
break;
}
return node;
}
/**
* cpqhp_resource_sort_and_combine - sort nodes by base addresses and clean up
* @head: the list to sort and clean up
*
* Description: Sorts all of the nodes in the list in ascending order by
* their base addresses. Also does garbage collection by
* combining adjacent nodes.
*
*/
int cpqhp_resource_sort_and_combine(struct pci_resource **head)
{
struct pci_resource *node1;
struct pci_resource *node2;
int out_of_order = 1;
dbg("%s: head = %p, *head = %p\n", __func__, head, *head);
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
if (!(*head))
return 1;
dbg("*head->next = %p\n",(*head)->next);
if (!(*head)->next)
return 0; /* only one item on the list, already sorted! */
dbg("*head->base = 0x%x\n",(*head)->base);
dbg("*head->next->base = 0x%x\n",(*head)->next->base);
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->base > (*head)->next->base)) {
node1 = *head;
(*head) = (*head)->next;
node1->next = (*head)->next;
(*head)->next = node1;
out_of_order++;
}
node1 = (*head);
while (node1->next && node1->next->next) {
if (node1->next->base > node1->next->next->base) {
out_of_order++;
node2 = node1->next;
node1->next = node1->next->next;
node1 = node1->next;
node2->next = node1->next;
node1->next = node2;
} else
node1 = node1->next;
}
} /* End of out_of_order loop */
node1 = *head;
while (node1 && node1->next) {
if ((node1->base + node1->length) == node1->next->base) {
/* Combine */
dbg("8..\n");
node1->length += node1->next->length;
node2 = node1->next;
node1->next = node1->next->next;
kfree(node2);
} else
node1 = node1->next;
}
return 0;
}
irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data)
{
struct controller *ctrl = data;
u8 schedule_flag = 0;
u8 reset;
u16 misc;
u32 Diff;
u32 temp_dword;
if (!(misc & 0x000C)) {
return IRQ_NONE;
}
if (misc & 0x0004) {
/* Clear the interrupt */
misc |= 0x0004;
writew(misc, ctrl->hpc_reg + MISC);
/* Read to clear posted writes */
misc = readw(ctrl->hpc_reg + MISC);
dbg ("%s - waking up\n", __func__);
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
wake_up_interruptible(&ctrl->queue);
}
if (misc & 0x0008) {
/* General-interrupt-input interrupt Pending */
Diff = readl(ctrl->hpc_reg + INT_INPUT_CLEAR) ^ ctrl->ctrl_int_comp;
ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
/* Clear the interrupt */
writel(Diff, ctrl->hpc_reg + INT_INPUT_CLEAR);
/* Read it back to clear any posted writes */
temp_dword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
if (!Diff)
/* Clear all interrupts */
writel(0xFFFFFFFF, ctrl->hpc_reg + INT_INPUT_CLEAR);
schedule_flag += handle_switch_change((u8)(Diff & 0xFFL), ctrl);
schedule_flag += handle_presence_change((u16)((Diff & 0xFFFF0000L) >> 16), ctrl);
schedule_flag += handle_power_fault((u8)((Diff & 0xFF00L) >> 8), ctrl);
}
reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE);
if (reset & 0x40) {
/* Bus reset has completed */
reset &= 0xCF;
writeb(reset, ctrl->hpc_reg + RESET_FREQ_MODE);
reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE);
wake_up_interruptible(&ctrl->queue);
}
if (schedule_flag) {
wake_up_process(cpqhp_event_thread);
dbg("Waking even thread");
}
return IRQ_HANDLED;
}
/**
* cpqhp_slot_create - Creates a node and adds it to the proper bus.
* @busnumber: bus where new node is to be located
* Returns pointer to the new node or %NULL if unsuccessful.
*/
struct pci_func *cpqhp_slot_create(u8 busnumber)
{
struct pci_func *new_slot;
struct pci_func *next;
new_slot = kzalloc(sizeof(*new_slot), GFP_KERNEL);
return new_slot;
new_slot->next = NULL;
new_slot->configured = 1;
if (cpqhp_slot_list[busnumber] == NULL) {
cpqhp_slot_list[busnumber] = new_slot;
} else {
next = cpqhp_slot_list[busnumber];
while (next->next != NULL)
next = next->next;
next->next = new_slot;
}
return new_slot;
}
/**
* slot_remove - Removes a node from the linked list of slots.
* @old_slot: slot to remove
*
*/
static int slot_remove(struct pci_func * old_slot)
{