diff options
Diffstat (limited to 'drivers/pci/hotplug/shpchp_pci.c')
-rw-r--r-- | drivers/pci/hotplug/shpchp_pci.c | 482 |
1 files changed, 53 insertions, 429 deletions
diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index d867099114ec..89e404805777 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c | |||
@@ -37,46 +37,69 @@ | |||
37 | #include <linux/pci.h> | 37 | #include <linux/pci.h> |
38 | #include "../pci.h" | 38 | #include "../pci.h" |
39 | #include "shpchp.h" | 39 | #include "shpchp.h" |
40 | #ifndef CONFIG_IA64 | ||
41 | #include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */ | ||
42 | #endif | ||
43 | 40 | ||
44 | int shpchp_configure_device (struct controller* ctrl, struct pci_func* func) | 41 | int shpchp_configure_device(struct slot *p_slot) |
45 | { | 42 | { |
46 | unsigned char bus; | 43 | struct pci_dev *dev; |
47 | struct pci_bus *child; | 44 | struct pci_bus *parent = p_slot->ctrl->pci_dev->subordinate; |
48 | int num; | 45 | int num, fn; |
49 | 46 | ||
50 | if (func->pci_dev == NULL) | 47 | dev = pci_find_slot(p_slot->bus, PCI_DEVFN(p_slot->device, 0)); |
51 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | 48 | if (dev) { |
52 | 49 | err("Device %s already exists at %x:%x, cannot hot-add\n", | |
53 | /* Still NULL ? Well then scan for it ! */ | 50 | pci_name(dev), p_slot->bus, p_slot->device); |
54 | if (func->pci_dev == NULL) { | 51 | return -EINVAL; |
55 | num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function)); | ||
56 | if (num) { | ||
57 | dbg("%s: subordiante %p number %x\n", __FUNCTION__, ctrl->pci_dev->subordinate, | ||
58 | ctrl->pci_dev->subordinate->number); | ||
59 | pci_bus_add_devices(ctrl->pci_dev->subordinate); | ||
60 | } | ||
61 | |||
62 | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | ||
63 | if (func->pci_dev == NULL) { | ||
64 | dbg("ERROR: pci_dev still null\n"); | ||
65 | return 0; | ||
66 | } | ||
67 | } | 52 | } |
68 | 53 | ||
69 | if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | 54 | num = pci_scan_slot(parent, PCI_DEVFN(p_slot->device, 0)); |
70 | pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); | 55 | if (num == 0) { |
71 | child = pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); | 56 | err("No new device found\n"); |
72 | pci_do_scan_bus(child); | 57 | return -ENODEV; |
58 | } | ||
73 | 59 | ||
60 | for (fn = 0; fn < 8; fn++) { | ||
61 | if (!(dev = pci_find_slot(p_slot->bus, | ||
62 | PCI_DEVFN(p_slot->device, fn)))) | ||
63 | continue; | ||
64 | if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { | ||
65 | err("Cannot hot-add display device %s\n", | ||
66 | pci_name(dev)); | ||
67 | continue; | ||
68 | } | ||
69 | if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || | ||
70 | (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { | ||
71 | /* Find an unused bus number for the new bridge */ | ||
72 | struct pci_bus *child; | ||
73 | unsigned char busnr, start = parent->secondary; | ||
74 | unsigned char end = parent->subordinate; | ||
75 | for (busnr = start; busnr <= end; busnr++) { | ||
76 | if (!pci_find_bus(pci_domain_nr(parent), | ||
77 | busnr)) | ||
78 | break; | ||
79 | } | ||
80 | if (busnr >= end) { | ||
81 | err("No free bus for hot-added bridge\n"); | ||
82 | continue; | ||
83 | } | ||
84 | child = pci_add_new_bus(parent, dev, busnr); | ||
85 | if (!child) { | ||
86 | err("Cannot add new bus for %s\n", | ||
87 | pci_name(dev)); | ||
88 | continue; | ||
89 | } | ||
90 | child->subordinate = pci_do_scan_bus(child); | ||
91 | pci_bus_size_bridges(child); | ||
92 | } | ||
93 | /* TBD: program firmware provided _HPP values */ | ||
94 | /* program_fw_provided_values(dev); */ | ||
74 | } | 95 | } |
75 | 96 | ||
97 | pci_bus_assign_resources(parent); | ||
98 | pci_bus_add_devices(parent); | ||
99 | pci_enable_bridges(parent); | ||
76 | return 0; | 100 | return 0; |
77 | } | 101 | } |
78 | 102 | ||
79 | |||
80 | int shpchp_unconfigure_device(struct pci_func* func) | 103 | int shpchp_unconfigure_device(struct pci_func* func) |
81 | { | 104 | { |
82 | int rc = 0; | 105 | int rc = 0; |
@@ -95,44 +118,6 @@ int shpchp_unconfigure_device(struct pci_func* func) | |||
95 | return rc; | 118 | return rc; |
96 | } | 119 | } |
97 | 120 | ||
98 | /* | ||
99 | * shpchp_set_irq | ||
100 | * | ||
101 | * @bus_num: bus number of PCI device | ||
102 | * @dev_num: device number of PCI device | ||
103 | * @slot: pointer to u8 where slot number will be returned | ||
104 | */ | ||
105 | int shpchp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) | ||
106 | { | ||
107 | #if defined(CONFIG_X86) && !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64) | ||
108 | int rc; | ||
109 | u16 temp_word; | ||
110 | struct pci_dev fakedev; | ||
111 | struct pci_bus fakebus; | ||
112 | |||
113 | fakedev.devfn = dev_num << 3; | ||
114 | fakedev.bus = &fakebus; | ||
115 | fakebus.number = bus_num; | ||
116 | dbg("%s: dev %d, bus %d, pin %d, num %d\n", | ||
117 | __FUNCTION__, dev_num, bus_num, int_pin, irq_num); | ||
118 | rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); | ||
119 | dbg("%s: rc %d\n", __FUNCTION__, rc); | ||
120 | if (!rc) | ||
121 | return !rc; | ||
122 | |||
123 | /* set the Edge Level Control Register (ELCR) */ | ||
124 | temp_word = inb(0x4d0); | ||
125 | temp_word |= inb(0x4d1) << 8; | ||
126 | |||
127 | temp_word |= 0x01 << irq_num; | ||
128 | |||
129 | /* This should only be for x86 as it sets the Edge Level Control Register */ | ||
130 | outb((u8) (temp_word & 0xFF), 0x4d0); | ||
131 | outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); | ||
132 | #endif | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | /* More PCI configuration routines; this time centered around hotplug controller */ | 121 | /* More PCI configuration routines; this time centered around hotplug controller */ |
137 | 122 | ||
138 | 123 | ||
@@ -447,364 +432,3 @@ int shpchp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot) | |||
447 | return 0; | 432 | return 0; |
448 | } | 433 | } |
449 | 434 | ||
450 | |||
451 | /* | ||
452 | * shpchp_save_used_resources | ||
453 | * | ||
454 | * Stores used resource information for existing boards. this is | ||
455 | * for boards that were in the system when this driver was loaded. | ||
456 | * this function is for hot plug ADD | ||
457 | * | ||
458 | * returns 0 if success | ||
459 | * if disable == 1(DISABLE_CARD), | ||
460 | * it loops for all functions of the slot and disables them. | ||
461 | * else, it just get resources of the function and return. | ||
462 | */ | ||
463 | int shpchp_save_used_resources(struct controller *ctrl, struct pci_func *func, int disable) | ||
464 | { | ||
465 | u8 cloop; | ||
466 | u8 header_type; | ||
467 | u8 secondary_bus; | ||
468 | u8 temp_byte; | ||
469 | u16 command; | ||
470 | u16 save_command; | ||
471 | u16 w_base, w_length; | ||
472 | u32 temp_register; | ||
473 | u32 save_base; | ||
474 | u32 base, length; | ||
475 | u64 base64 = 0; | ||
476 | int index = 0; | ||
477 | unsigned int devfn; | ||
478 | struct pci_resource *mem_node = NULL; | ||
479 | struct pci_resource *p_mem_node = NULL; | ||
480 | struct pci_resource *t_mem_node; | ||
481 | struct pci_resource *io_node; | ||
482 | struct pci_resource *bus_node; | ||
483 | struct pci_bus lpci_bus, *pci_bus; | ||
484 | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | ||
485 | pci_bus = &lpci_bus; | ||
486 | |||
487 | if (disable) | ||
488 | func = shpchp_slot_find(func->bus, func->device, index++); | ||
489 | |||
490 | while ((func != NULL) && func->is_a_board) { | ||
491 | pci_bus->number = func->bus; | ||
492 | devfn = PCI_DEVFN(func->device, func->function); | ||
493 | |||
494 | /* Save the command register */ | ||
495 | pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command); | ||
496 | |||
497 | if (disable) { | ||
498 | /* disable card */ | ||
499 | command = 0x00; | ||
500 | pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | ||
501 | } | ||
502 | |||
503 | /* Check for Bridge */ | ||
504 | pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | ||
505 | |||
506 | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ | ||
507 | dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n", | ||
508 | func->bus, func->device, save_command); | ||
509 | if (disable) { | ||
510 | /* Clear Bridge Control Register */ | ||
511 | command = 0x00; | ||
512 | pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); | ||
513 | } | ||
514 | |||
515 | pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); | ||
516 | pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); | ||
517 | |||
518 | bus_node = kmalloc(sizeof(struct pci_resource), | ||
519 | GFP_KERNEL); | ||
520 | if (!bus_node) | ||
521 | return -ENOMEM; | ||
522 | |||
523 | bus_node->base = (ulong)secondary_bus; | ||
524 | bus_node->length = (ulong)(temp_byte - secondary_bus + 1); | ||
525 | |||
526 | bus_node->next = func->bus_head; | ||
527 | func->bus_head = bus_node; | ||
528 | |||
529 | /* Save IO base and Limit registers */ | ||
530 | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &temp_byte); | ||
531 | base = temp_byte; | ||
532 | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &temp_byte); | ||
533 | length = temp_byte; | ||
534 | |||
535 | if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) { | ||
536 | io_node = kmalloc(sizeof(struct pci_resource), | ||
537 | GFP_KERNEL); | ||
538 | if (!io_node) | ||
539 | return -ENOMEM; | ||
540 | |||
541 | io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8; | ||
542 | io_node->length = (ulong)(length - base + 0x10) << 8; | ||
543 | |||
544 | io_node->next = func->io_head; | ||
545 | func->io_head = io_node; | ||
546 | } | ||
547 | |||
548 | /* Save memory base and Limit registers */ | ||
549 | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base); | ||
550 | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); | ||
551 | |||
552 | if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
553 | mem_node = kmalloc(sizeof(struct pci_resource), | ||
554 | GFP_KERNEL); | ||
555 | if (!mem_node) | ||
556 | return -ENOMEM; | ||
557 | |||
558 | mem_node->base = (ulong)w_base << 16; | ||
559 | mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; | ||
560 | |||
561 | mem_node->next = func->mem_head; | ||
562 | func->mem_head = mem_node; | ||
563 | } | ||
564 | /* Save prefetchable memory base and Limit registers */ | ||
565 | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); | ||
566 | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); | ||
567 | |||
568 | if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
569 | p_mem_node = kmalloc(sizeof(struct pci_resource), | ||
570 | GFP_KERNEL); | ||
571 | if (!p_mem_node) | ||
572 | return -ENOMEM; | ||
573 | |||
574 | p_mem_node->base = (ulong)w_base << 16; | ||
575 | p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; | ||
576 | |||
577 | p_mem_node->next = func->p_mem_head; | ||
578 | func->p_mem_head = p_mem_node; | ||
579 | } | ||
580 | } else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { | ||
581 | dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n", | ||
582 | func->bus, func->device, save_command); | ||
583 | |||
584 | /* Figure out IO and memory base lengths */ | ||
585 | for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { | ||
586 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base); | ||
587 | |||
588 | temp_register = 0xFFFFFFFF; | ||
589 | pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); | ||
590 | pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register); | ||
591 | |||
592 | if (!disable) | ||
593 | pci_bus_write_config_dword(pci_bus, devfn, cloop, save_base); | ||
594 | |||
595 | if (!temp_register) | ||
596 | continue; | ||
597 | |||
598 | base = temp_register; | ||
599 | |||
600 | if ((base & PCI_BASE_ADDRESS_SPACE_IO) && | ||
601 | (!disable || (save_command & PCI_COMMAND_IO))) { | ||
602 | /* IO base */ | ||
603 | /* set temp_register = amount of IO space requested */ | ||
604 | base = base & 0xFFFFFFFCL; | ||
605 | base = (~base) + 1; | ||
606 | |||
607 | io_node = kmalloc(sizeof (struct pci_resource), | ||
608 | GFP_KERNEL); | ||
609 | if (!io_node) | ||
610 | return -ENOMEM; | ||
611 | |||
612 | io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK; | ||
613 | io_node->length = (ulong)base; | ||
614 | dbg("sur adapter: IO bar=0x%x(length=0x%x)\n", | ||
615 | io_node->base, io_node->length); | ||
616 | |||
617 | io_node->next = func->io_head; | ||
618 | func->io_head = io_node; | ||
619 | } else { /* map Memory */ | ||
620 | int prefetchable = 1; | ||
621 | /* struct pci_resources **res_node; */ | ||
622 | char *res_type_str = "PMEM"; | ||
623 | u32 temp_register2; | ||
624 | |||
625 | t_mem_node = kmalloc(sizeof (struct pci_resource), | ||
626 | GFP_KERNEL); | ||
627 | if (!t_mem_node) | ||
628 | return -ENOMEM; | ||
629 | |||
630 | if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) && | ||
631 | (!disable || (save_command & PCI_COMMAND_MEMORY))) { | ||
632 | prefetchable = 0; | ||
633 | mem_node = t_mem_node; | ||
634 | res_type_str++; | ||
635 | } else | ||
636 | p_mem_node = t_mem_node; | ||
637 | |||
638 | base = base & 0xFFFFFFF0L; | ||
639 | base = (~base) + 1; | ||
640 | |||
641 | switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { | ||
642 | case PCI_BASE_ADDRESS_MEM_TYPE_32: | ||
643 | if (prefetchable) { | ||
644 | p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; | ||
645 | p_mem_node->length = (ulong)base; | ||
646 | dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", | ||
647 | res_type_str, | ||
648 | p_mem_node->base, | ||
649 | p_mem_node->length); | ||
650 | |||
651 | p_mem_node->next = func->p_mem_head; | ||
652 | func->p_mem_head = p_mem_node; | ||
653 | } else { | ||
654 | mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; | ||
655 | mem_node->length = (ulong)base; | ||
656 | dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", | ||
657 | res_type_str, | ||
658 | mem_node->base, | ||
659 | mem_node->length); | ||
660 | |||
661 | mem_node->next = func->mem_head; | ||
662 | func->mem_head = mem_node; | ||
663 | } | ||
664 | break; | ||
665 | case PCI_BASE_ADDRESS_MEM_TYPE_64: | ||
666 | pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); | ||
667 | base64 = temp_register2; | ||
668 | base64 = (base64 << 32) | save_base; | ||
669 | |||
670 | if (temp_register2) { | ||
671 | dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n", | ||
672 | res_type_str, temp_register2, (u32)base64); | ||
673 | base64 &= 0x00000000FFFFFFFFL; | ||
674 | } | ||
675 | |||
676 | if (prefetchable) { | ||
677 | p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; | ||
678 | p_mem_node->length = base; | ||
679 | dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", | ||
680 | res_type_str, | ||
681 | p_mem_node->base, | ||
682 | p_mem_node->length); | ||
683 | |||
684 | p_mem_node->next = func->p_mem_head; | ||
685 | func->p_mem_head = p_mem_node; | ||
686 | } else { | ||
687 | mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; | ||
688 | mem_node->length = base; | ||
689 | dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", | ||
690 | res_type_str, | ||
691 | mem_node->base, | ||
692 | mem_node->length); | ||
693 | |||
694 | mem_node->next = func->mem_head; | ||
695 | func->mem_head = mem_node; | ||
696 | } | ||
697 | cloop += 4; | ||
698 | break; | ||
699 | default: | ||
700 | dbg("asur: reserved BAR type=0x%x\n", | ||
701 | temp_register); | ||
702 | break; | ||
703 | } | ||
704 | } | ||
705 | } /* End of base register loop */ | ||
706 | } else { /* Some other unknown header type */ | ||
707 | dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n", | ||
708 | func->bus, func->device); | ||
709 | } | ||
710 | |||
711 | /* find the next device in this slot */ | ||
712 | if (!disable) | ||
713 | break; | ||
714 | func = shpchp_slot_find(func->bus, func->device, index++); | ||
715 | } | ||
716 | |||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | /** | ||
721 | * kfree_resource_list: release memory of all list members | ||
722 | * @res: resource list to free | ||
723 | */ | ||
724 | static inline void | ||
725 | return_resource_list(struct pci_resource **func, struct pci_resource **res) | ||
726 | { | ||
727 | struct pci_resource *node; | ||
728 | struct pci_resource *t_node; | ||
729 | |||
730 | node = *func; | ||
731 | *func = NULL; | ||
732 | while (node) { | ||
733 | t_node = node->next; | ||
734 | return_resource(res, node); | ||
735 | node = t_node; | ||
736 | } | ||
737 | } | ||
738 | |||
739 | /* | ||
740 | * shpchp_return_board_resources | ||
741 | * | ||
742 | * this routine returns all resources allocated to a board to | ||
743 | * the available pool. | ||
744 | * | ||
745 | * returns 0 if success | ||
746 | */ | ||
747 | int shpchp_return_board_resources(struct pci_func * func, | ||
748 | struct resource_lists * resources) | ||
749 | { | ||
750 | int rc; | ||
751 | dbg("%s\n", __FUNCTION__); | ||
752 | |||
753 | if (!func) | ||
754 | return 1; | ||
755 | |||
756 | return_resource_list(&(func->io_head),&(resources->io_head)); | ||
757 | return_resource_list(&(func->mem_head),&(resources->mem_head)); | ||
758 | return_resource_list(&(func->p_mem_head),&(resources->p_mem_head)); | ||
759 | return_resource_list(&(func->bus_head),&(resources->bus_head)); | ||
760 | |||
761 | rc = shpchp_resource_sort_and_combine(&(resources->mem_head)); | ||
762 | rc |= shpchp_resource_sort_and_combine(&(resources->p_mem_head)); | ||
763 | rc |= shpchp_resource_sort_and_combine(&(resources->io_head)); | ||
764 | rc |= shpchp_resource_sort_and_combine(&(resources->bus_head)); | ||
765 | |||
766 | return rc; | ||
767 | } | ||
768 | |||
769 | /** | ||
770 | * kfree_resource_list: release memory of all list members | ||
771 | * @res: resource list to free | ||
772 | */ | ||
773 | static inline void | ||
774 | kfree_resource_list(struct pci_resource **r) | ||
775 | { | ||
776 | struct pci_resource *res, *tres; | ||
777 | |||
778 | res = *r; | ||
779 | *r = NULL; | ||
780 | |||
781 | while (res) { | ||
782 | tres = res; | ||
783 | res = res->next; | ||
784 | kfree(tres); | ||
785 | } | ||
786 | } | ||
787 | |||
788 | /** | ||
789 | * shpchp_destroy_resource_list: put node back in the resource list | ||
790 | * @resources: list to put nodes back | ||
791 | */ | ||
792 | void shpchp_destroy_resource_list(struct resource_lists *resources) | ||
793 | { | ||
794 | kfree_resource_list(&(resources->io_head)); | ||
795 | kfree_resource_list(&(resources->mem_head)); | ||
796 | kfree_resource_list(&(resources->p_mem_head)); | ||
797 | kfree_resource_list(&(resources->bus_head)); | ||
798 | } | ||
799 | |||
800 | /** | ||
801 | * shpchp_destroy_board_resources: put node back in the resource list | ||
802 | * @resources: list to put nodes back | ||
803 | */ | ||
804 | void shpchp_destroy_board_resources(struct pci_func * func) | ||
805 | { | ||
806 | kfree_resource_list(&(func->io_head)); | ||
807 | kfree_resource_list(&(func->mem_head)); | ||
808 | kfree_resource_list(&(func->p_mem_head)); | ||
809 | kfree_resource_list(&(func->bus_head)); | ||
810 | } | ||