|  | /* | 
|  | * PCI Express Hot Plug Controller Driver | 
|  | * | 
|  | * Copyright (C) 1995,2001 Compaq Computer Corporation | 
|  | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | 
|  | * Copyright (C) 2001 IBM Corp. | 
|  | * Copyright (C) 2003-2004 Intel Corporation | 
|  | * | 
|  | * All rights reserved. | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License as published by | 
|  | * the Free Software Foundation; either version 2 of the License, or (at | 
|  | * your option) any later version. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, but | 
|  | * WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | 
|  | * NON INFRINGEMENT.  See the GNU General Public License for more | 
|  | * details. | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License | 
|  | * along with this program; if not, write to the Free Software | 
|  | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | * | 
|  | * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include <linux/config.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/workqueue.h> | 
|  | #include <linux/proc_fs.h> | 
|  | #include <linux/pci.h> | 
|  | #include "../pci.h" | 
|  | #include "pciehp.h" | 
|  | #ifndef CONFIG_IA64 | 
|  | #include "../../../arch/i386/pci/pci.h"    /* horrible hack showing how processor dependant we are... */ | 
|  | #endif | 
|  |  | 
|  |  | 
|  | int pciehp_configure_device (struct controller* ctrl, struct pci_func* func) | 
|  | { | 
|  | unsigned char bus; | 
|  | struct pci_bus *child; | 
|  | int num; | 
|  |  | 
|  | if (func->pci_dev == NULL) | 
|  | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | 
|  |  | 
|  | /* Still NULL ? Well then scan for it ! */ | 
|  | if (func->pci_dev == NULL) { | 
|  | dbg("%s: pci_dev still null. do pci_scan_slot\n", __FUNCTION__); | 
|  |  | 
|  | num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function)); | 
|  |  | 
|  | if (num) | 
|  | pci_bus_add_devices(ctrl->pci_dev->subordinate); | 
|  |  | 
|  | func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); | 
|  | if (func->pci_dev == NULL) { | 
|  | dbg("ERROR: pci_dev still null\n"); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | 
|  | pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus); | 
|  | child = pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus); | 
|  | pci_do_scan_bus(child); | 
|  |  | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | int pciehp_unconfigure_device(struct pci_func* func) | 
|  | { | 
|  | int rc = 0; | 
|  | int j; | 
|  | struct pci_bus *pbus; | 
|  |  | 
|  | dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, | 
|  | func->device, func->function); | 
|  | pbus = func->pci_dev->bus; | 
|  |  | 
|  | for (j=0; j<8 ; j++) { | 
|  | struct pci_dev* temp = pci_find_slot(func->bus, | 
|  | (func->device << 3) | j); | 
|  | if (temp) { | 
|  | pci_remove_bus_device(temp); | 
|  | } | 
|  | } | 
|  | /* | 
|  | * Some PCI Express root ports require fixup after hot-plug operation. | 
|  | */ | 
|  | if (pcie_mch_quirk) | 
|  | pci_fixup_device(pci_fixup_final, pbus->self); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * pciehp_set_irq | 
|  | * | 
|  | * @bus_num: bus number of PCI device | 
|  | * @dev_num: device number of PCI device | 
|  | * @slot: pointer to u8 where slot number will be returned | 
|  | */ | 
|  | int pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) | 
|  | { | 
|  | #if defined(CONFIG_X86_32) && !defined(CONFIG_X86_IO_APIC) | 
|  | int rc; | 
|  | u16 temp_word; | 
|  | struct pci_dev fakedev; | 
|  | struct pci_bus fakebus; | 
|  |  | 
|  | fakedev.devfn = dev_num << 3; | 
|  | fakedev.bus = &fakebus; | 
|  | fakebus.number = bus_num; | 
|  | dbg("%s: dev %d, bus %d, pin %d, num %d\n", | 
|  | __FUNCTION__, dev_num, bus_num, int_pin, irq_num); | 
|  | rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num); | 
|  | dbg("%s: rc %d\n", __FUNCTION__, rc); | 
|  | if (!rc) | 
|  | return !rc; | 
|  |  | 
|  | /* set the Edge Level Control Register (ELCR) */ | 
|  | temp_word = inb(0x4d0); | 
|  | temp_word |= inb(0x4d1) << 8; | 
|  |  | 
|  | temp_word |= 0x01 << irq_num; | 
|  |  | 
|  | /* This should only be for x86 as it sets the Edge Level Control Register */ | 
|  | outb((u8) (temp_word & 0xFF), 0x4d0); | 
|  | outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); | 
|  | #endif | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* More PCI configuration routines; this time centered around hotplug controller */ | 
|  |  | 
|  |  | 
|  | /* | 
|  | * pciehp_save_config | 
|  | * | 
|  | * Reads configuration for all slots in a PCI bus and saves info. | 
|  | * | 
|  | * Note:  For non-hot plug busses, the slot # saved is the device # | 
|  | * | 
|  | * returns 0 if success | 
|  | */ | 
|  | int pciehp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num) | 
|  | { | 
|  | int rc; | 
|  | u8 class_code; | 
|  | u8 header_type; | 
|  | u32 ID; | 
|  | u8 secondary_bus; | 
|  | struct pci_func *new_slot; | 
|  | int sub_bus; | 
|  | int max_functions; | 
|  | int function; | 
|  | u8 DevError; | 
|  | int device = 0; | 
|  | int cloop = 0; | 
|  | int stop_it; | 
|  | int index; | 
|  | int is_hot_plug = num_ctlr_slots || first_device_num; | 
|  | struct pci_bus lpci_bus, *pci_bus; | 
|  | int FirstSupported, LastSupported; | 
|  |  | 
|  | dbg("%s: Enter\n", __FUNCTION__); | 
|  |  | 
|  | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | 
|  | pci_bus = &lpci_bus; | 
|  |  | 
|  | dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, | 
|  | num_ctlr_slots, first_device_num); | 
|  |  | 
|  | /*   Decide which slots are supported */ | 
|  | if (is_hot_plug) { | 
|  | /********************************* | 
|  | *  is_hot_plug is the slot mask | 
|  | *********************************/ | 
|  | FirstSupported = first_device_num; | 
|  | LastSupported = FirstSupported + num_ctlr_slots - 1; | 
|  | } else { | 
|  | FirstSupported = 0; | 
|  | LastSupported = 0x1F; | 
|  | } | 
|  |  | 
|  | dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported, | 
|  | LastSupported); | 
|  |  | 
|  | /*   Save PCI configuration space for all devices in supported slots */ | 
|  | dbg("%s: pci_bus->number = %x\n", __FUNCTION__, pci_bus->number); | 
|  | pci_bus->number = busnumber; | 
|  | dbg("%s: bus = %x, dev = %x\n", __FUNCTION__, busnumber, device); | 
|  | for (device = FirstSupported; device <= LastSupported; device++) { | 
|  | ID = 0xFFFFFFFF; | 
|  | rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), | 
|  | PCI_VENDOR_ID, &ID); | 
|  |  | 
|  | if (ID != 0xFFFFFFFF) {	  /*  device in slot */ | 
|  | dbg("%s: ID = %x\n", __FUNCTION__, ID); | 
|  | rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), | 
|  | 0x0B, &class_code); | 
|  | if (rc) | 
|  | return rc; | 
|  |  | 
|  | rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), | 
|  | PCI_HEADER_TYPE, &header_type); | 
|  | if (rc) | 
|  | return rc; | 
|  |  | 
|  | dbg("class_code = %x, header_type = %x\n", class_code, header_type); | 
|  |  | 
|  | /* If multi-function device, set max_functions to 8 */ | 
|  | if (header_type & 0x80) | 
|  | max_functions = 8; | 
|  | else | 
|  | max_functions = 1; | 
|  |  | 
|  | function = 0; | 
|  |  | 
|  | do { | 
|  | DevError = 0; | 
|  | dbg("%s: In do loop\n", __FUNCTION__); | 
|  |  | 
|  | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {   /* P-P Bridge */ | 
|  | /* Recurse the subordinate bus | 
|  | * get the subordinate bus number | 
|  | */ | 
|  | rc = pci_bus_read_config_byte(pci_bus, | 
|  | PCI_DEVFN(device, function), | 
|  | PCI_SECONDARY_BUS, &secondary_bus); | 
|  | if (rc) { | 
|  | return rc; | 
|  | } else { | 
|  | sub_bus = (int) secondary_bus; | 
|  |  | 
|  | /* Save secondary bus cfg spc with this recursive call. */ | 
|  | rc = pciehp_save_config(ctrl, sub_bus, 0, 0); | 
|  | if (rc) | 
|  | return rc; | 
|  | } | 
|  | } | 
|  |  | 
|  | index = 0; | 
|  | new_slot = pciehp_slot_find(busnumber, device, index++); | 
|  |  | 
|  | dbg("%s: new_slot = %p bus %x dev %x fun %x\n", | 
|  | __FUNCTION__, new_slot, busnumber, device, index-1); | 
|  |  | 
|  | while (new_slot && (new_slot->function != (u8) function)) { | 
|  | new_slot = pciehp_slot_find(busnumber, device, index++); | 
|  | dbg("%s: while loop, new_slot = %p bus %x dev %x fun %x\n", | 
|  | __FUNCTION__, new_slot, busnumber, device, index-1); | 
|  | } | 
|  | if (!new_slot) { | 
|  | /* Setup slot structure. */ | 
|  | new_slot = pciehp_slot_create(busnumber); | 
|  | dbg("%s: if, new_slot = %p bus %x dev %x fun %x\n", | 
|  | __FUNCTION__, new_slot, busnumber, device, function); | 
|  |  | 
|  | if (new_slot == NULL) | 
|  | return(1); | 
|  | } | 
|  |  | 
|  | new_slot->bus = (u8) busnumber; | 
|  | new_slot->device = (u8) device; | 
|  | new_slot->function = (u8) function; | 
|  | new_slot->is_a_board = 1; | 
|  | new_slot->switch_save = 0x10; | 
|  | /* In case of unsupported board */ | 
|  | new_slot->status = DevError; | 
|  | new_slot->pci_dev = pci_find_slot(new_slot->bus, | 
|  | (new_slot->device << 3) | new_slot->function); | 
|  | dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev); | 
|  |  | 
|  | for (cloop = 0; cloop < 0x20; cloop++) { | 
|  | rc = pci_bus_read_config_dword(pci_bus, | 
|  | PCI_DEVFN(device, function), | 
|  | cloop << 2, | 
|  | (u32 *) &(new_slot->config_space [cloop])); | 
|  | /* dbg("new_slot->config_space[%x] = %x\n", | 
|  | cloop, new_slot->config_space[cloop]); */ | 
|  | if (rc) | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | function++; | 
|  |  | 
|  | stop_it = 0; | 
|  |  | 
|  | /*  this loop skips to the next present function | 
|  | *  reading in Class Code and Header type. | 
|  | */ | 
|  |  | 
|  | while ((function < max_functions)&&(!stop_it)) { | 
|  | dbg("%s: In while loop \n", __FUNCTION__); | 
|  | rc = pci_bus_read_config_dword(pci_bus, | 
|  | PCI_DEVFN(device, function), | 
|  | PCI_VENDOR_ID, &ID); | 
|  |  | 
|  | if (ID == 0xFFFFFFFF) {  /* nothing there. */ | 
|  | function++; | 
|  | dbg("Nothing there\n"); | 
|  | } else {  /* Something there */ | 
|  | rc = pci_bus_read_config_byte(pci_bus, | 
|  | PCI_DEVFN(device, function), | 
|  | 0x0B, &class_code); | 
|  | if (rc) | 
|  | return rc; | 
|  |  | 
|  | rc = pci_bus_read_config_byte(pci_bus, | 
|  | PCI_DEVFN(device, function), | 
|  | PCI_HEADER_TYPE, &header_type); | 
|  | if (rc) | 
|  | return rc; | 
|  |  | 
|  | dbg("class_code = %x, header_type = %x\n", class_code, header_type); | 
|  | stop_it++; | 
|  | } | 
|  | } | 
|  |  | 
|  | } while (function < max_functions); | 
|  | /* End of IF (device in slot?) */ | 
|  | } else if (is_hot_plug) { | 
|  | /* Setup slot structure with entry for empty slot */ | 
|  | new_slot = pciehp_slot_create(busnumber); | 
|  |  | 
|  | if (new_slot == NULL) { | 
|  | return(1); | 
|  | } | 
|  | dbg("new_slot = %p, bus = %x, dev = %x, fun = %x\n", new_slot, | 
|  | new_slot->bus, new_slot->device, new_slot->function); | 
|  |  | 
|  | new_slot->bus = (u8) busnumber; | 
|  | new_slot->device = (u8) device; | 
|  | new_slot->function = 0; | 
|  | new_slot->is_a_board = 0; | 
|  | new_slot->presence_save = 0; | 
|  | new_slot->switch_save = 0; | 
|  | } | 
|  | } 			/* End of FOR loop */ | 
|  |  | 
|  | dbg("%s: Exit\n", __FUNCTION__); | 
|  | return(0); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * pciehp_save_slot_config | 
|  | * | 
|  | * Saves configuration info for all PCI devices in a given slot | 
|  | * including subordinate busses. | 
|  | * | 
|  | * returns 0 if success | 
|  | */ | 
|  | int pciehp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot) | 
|  | { | 
|  | int rc; | 
|  | u8 class_code; | 
|  | u8 header_type; | 
|  | u32 ID; | 
|  | u8 secondary_bus; | 
|  | int sub_bus; | 
|  | int max_functions; | 
|  | int function; | 
|  | int cloop = 0; | 
|  | int stop_it; | 
|  | struct pci_bus lpci_bus, *pci_bus; | 
|  | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | 
|  | pci_bus = &lpci_bus; | 
|  | pci_bus->number = new_slot->bus; | 
|  |  | 
|  | ID = 0xFFFFFFFF; | 
|  |  | 
|  | pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0), | 
|  | PCI_VENDOR_ID, &ID); | 
|  |  | 
|  | if (ID != 0xFFFFFFFF) {	  /*  device in slot */ | 
|  | pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), | 
|  | 0x0B, &class_code); | 
|  |  | 
|  | pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), | 
|  | PCI_HEADER_TYPE, &header_type); | 
|  |  | 
|  | if (header_type & 0x80)	/* Multi-function device */ | 
|  | max_functions = 8; | 
|  | else | 
|  | max_functions = 1; | 
|  |  | 
|  | function = 0; | 
|  |  | 
|  | do { | 
|  | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {	  /* PCI-PCI Bridge */ | 
|  | /*  Recurse the subordinate bus */ | 
|  | pci_bus_read_config_byte(pci_bus, | 
|  | PCI_DEVFN(new_slot->device, function), | 
|  | PCI_SECONDARY_BUS, &secondary_bus); | 
|  |  | 
|  | sub_bus = (int) secondary_bus; | 
|  |  | 
|  | /* Save the config headers for the secondary bus. */ | 
|  | rc = pciehp_save_config(ctrl, sub_bus, 0, 0); | 
|  |  | 
|  | if (rc) | 
|  | return rc; | 
|  |  | 
|  | }	/* End of IF */ | 
|  |  | 
|  | new_slot->status = 0; | 
|  |  | 
|  | for (cloop = 0; cloop < 0x20; cloop++) { | 
|  | pci_bus_read_config_dword(pci_bus, | 
|  | PCI_DEVFN(new_slot->device, function), | 
|  | cloop << 2, | 
|  | (u32 *) &(new_slot->config_space [cloop])); | 
|  | } | 
|  |  | 
|  | function++; | 
|  |  | 
|  | stop_it = 0; | 
|  |  | 
|  | /*  this loop skips to the next present function | 
|  | *  reading in the Class Code and the Header type. | 
|  | */ | 
|  |  | 
|  | while ((function < max_functions) && (!stop_it)) { | 
|  | pci_bus_read_config_dword(pci_bus, | 
|  | PCI_DEVFN(new_slot->device, function), | 
|  | PCI_VENDOR_ID, &ID); | 
|  |  | 
|  | if (ID == 0xFFFFFFFF) {	 /* nothing there. */ | 
|  | function++; | 
|  | } else {  /* Something there */ | 
|  | pci_bus_read_config_byte(pci_bus, | 
|  | PCI_DEVFN(new_slot->device, function), | 
|  | 0x0B, &class_code); | 
|  |  | 
|  | pci_bus_read_config_byte(pci_bus, | 
|  | PCI_DEVFN(new_slot->device, function), | 
|  | PCI_HEADER_TYPE, &header_type); | 
|  |  | 
|  | stop_it++; | 
|  | } | 
|  | } | 
|  |  | 
|  | } while (function < max_functions); | 
|  | }			/* End of IF (device in slot?) */ | 
|  | else { | 
|  | return 2; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | * pciehp_save_used_resources | 
|  | * | 
|  | * Stores used resource information for existing boards.  this is | 
|  | * for boards that were in the system when this driver was loaded. | 
|  | * this function is for hot plug ADD | 
|  | * | 
|  | * returns 0 if success | 
|  | * if disable  == 1(DISABLE_CARD), | 
|  | *  it loops for all functions of the slot and disables them. | 
|  | * else, it just get resources of the function and return. | 
|  | */ | 
|  | int pciehp_save_used_resources(struct controller *ctrl, struct pci_func *func, int disable) | 
|  | { | 
|  | u8 cloop; | 
|  | u8 header_type; | 
|  | u8 secondary_bus; | 
|  | u8 temp_byte; | 
|  | u16 command; | 
|  | u16 save_command; | 
|  | u16 w_base, w_length; | 
|  | u32 temp_register; | 
|  | u32 save_base; | 
|  | u32 base, length; | 
|  | u64 base64 = 0; | 
|  | int index = 0; | 
|  | unsigned int devfn; | 
|  | struct pci_resource *mem_node = NULL; | 
|  | struct pci_resource *p_mem_node = NULL; | 
|  | struct pci_resource *t_mem_node; | 
|  | struct pci_resource *io_node; | 
|  | struct pci_resource *bus_node; | 
|  | struct pci_bus lpci_bus, *pci_bus; | 
|  | memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus)); | 
|  | pci_bus = &lpci_bus; | 
|  |  | 
|  | if (disable) | 
|  | func = pciehp_slot_find(func->bus, func->device, index++); | 
|  |  | 
|  | while ((func != NULL) && func->is_a_board) { | 
|  | pci_bus->number = func->bus; | 
|  | devfn = PCI_DEVFN(func->device, func->function); | 
|  |  | 
|  | /* Save the command register */ | 
|  | pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command); | 
|  |  | 
|  | if (disable) { | 
|  | /* disable card */ | 
|  | command = 0x00; | 
|  | pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); | 
|  | } | 
|  |  | 
|  | /* Check for Bridge */ | 
|  | pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); | 
|  |  | 
|  | if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {     /* PCI-PCI Bridge */ | 
|  | dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n", | 
|  | func->bus, func->device, save_command); | 
|  | if (disable) { | 
|  | /* Clear Bridge Control Register */ | 
|  | command = 0x00; | 
|  | pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); | 
|  | } | 
|  |  | 
|  | pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); | 
|  | pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); | 
|  |  | 
|  | bus_node = kmalloc(sizeof(struct pci_resource), | 
|  | GFP_KERNEL); | 
|  | if (!bus_node) | 
|  | return -ENOMEM; | 
|  |  | 
|  | bus_node->base = (ulong)secondary_bus; | 
|  | bus_node->length = (ulong)(temp_byte - secondary_bus + 1); | 
|  |  | 
|  | bus_node->next = func->bus_head; | 
|  | func->bus_head = bus_node; | 
|  |  | 
|  | /* Save IO base and Limit registers */ | 
|  | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &temp_byte); | 
|  | base = temp_byte; | 
|  | pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &temp_byte); | 
|  | length = temp_byte; | 
|  |  | 
|  | if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) { | 
|  | io_node = kmalloc(sizeof(struct pci_resource), | 
|  | GFP_KERNEL); | 
|  | if (!io_node) | 
|  | return -ENOMEM; | 
|  |  | 
|  | io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8; | 
|  | io_node->length = (ulong)(length - base + 0x10) << 8; | 
|  |  | 
|  | io_node->next = func->io_head; | 
|  | func->io_head = io_node; | 
|  | } | 
|  |  | 
|  | /* Save memory base and Limit registers */ | 
|  | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base); | 
|  | pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); | 
|  |  | 
|  | if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { | 
|  | mem_node = kmalloc(sizeof(struct pci_resource), | 
|  | GFP_KERNEL); | 
|  | if (!mem_node) | 
|  | return -ENOMEM; | 
|  |  | 
|  | mem_node->base = (ulong)w_base << 16; | 
|  | mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; | 
|  |  | 
|  | mem_node->next = func->mem_head; | 
|  | func->mem_head = mem_node; | 
|  | } | 
|  | /* Save prefetchable memory base and Limit registers */ | 
|  | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); | 
|  | pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); | 
|  |  | 
|  | if ((w_base <= w_length) && (!disable || (save_command & PCI_COMMAND_MEMORY))) { | 
|  | p_mem_node = kmalloc(sizeof(struct pci_resource), | 
|  | GFP_KERNEL); | 
|  | if (!p_mem_node) | 
|  | return -ENOMEM; | 
|  |  | 
|  | p_mem_node->base = (ulong)w_base << 16; | 
|  | p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16; | 
|  |  | 
|  | p_mem_node->next = func->p_mem_head; | 
|  | func->p_mem_head = p_mem_node; | 
|  | } | 
|  | } else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { | 
|  | dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n", | 
|  | func->bus, func->device, save_command); | 
|  |  | 
|  | /* Figure out IO and memory base lengths */ | 
|  | for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) { | 
|  | pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base); | 
|  |  | 
|  | temp_register = 0xFFFFFFFF; | 
|  | pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); | 
|  | pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register); | 
|  |  | 
|  | if (!disable) | 
|  | pci_bus_write_config_dword(pci_bus, devfn, cloop, save_base); | 
|  |  | 
|  | if (!temp_register) | 
|  | continue; | 
|  |  | 
|  | base = temp_register; | 
|  |  | 
|  | if ((base & PCI_BASE_ADDRESS_SPACE_IO) && | 
|  | (!disable || (save_command & PCI_COMMAND_IO))) { | 
|  | /* IO base */ | 
|  | /* set temp_register = amount of IO space requested */ | 
|  | base = base & 0xFFFFFFFCL; | 
|  | base = (~base) + 1; | 
|  |  | 
|  | io_node = kmalloc(sizeof (struct pci_resource), | 
|  | GFP_KERNEL); | 
|  | if (!io_node) | 
|  | return -ENOMEM; | 
|  |  | 
|  | io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK; | 
|  | io_node->length = (ulong)base; | 
|  | dbg("sur adapter: IO bar=0x%x(length=0x%x)\n", | 
|  | io_node->base, io_node->length); | 
|  |  | 
|  | io_node->next = func->io_head; | 
|  | func->io_head = io_node; | 
|  | } else {  /* map Memory */ | 
|  | int prefetchable = 1; | 
|  | /* struct pci_resources **res_node; */ | 
|  | char *res_type_str = "PMEM"; | 
|  | u32 temp_register2; | 
|  |  | 
|  | t_mem_node = kmalloc(sizeof (struct pci_resource), | 
|  | GFP_KERNEL); | 
|  | if (!t_mem_node) | 
|  | return -ENOMEM; | 
|  |  | 
|  | if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) && | 
|  | (!disable || (save_command & PCI_COMMAND_MEMORY))) { | 
|  | prefetchable = 0; | 
|  | mem_node = t_mem_node; | 
|  | res_type_str++; | 
|  | } else | 
|  | p_mem_node = t_mem_node; | 
|  |  | 
|  | base = base & 0xFFFFFFF0L; | 
|  | base = (~base) + 1; | 
|  |  | 
|  | switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { | 
|  | case PCI_BASE_ADDRESS_MEM_TYPE_32: | 
|  | if (prefetchable) { | 
|  | p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; | 
|  | p_mem_node->length = (ulong)base; | 
|  | dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", | 
|  | res_type_str, | 
|  | p_mem_node->base, | 
|  | p_mem_node->length); | 
|  |  | 
|  | p_mem_node->next = func->p_mem_head; | 
|  | func->p_mem_head = p_mem_node; | 
|  | } else { | 
|  | mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK; | 
|  | mem_node->length = (ulong)base; | 
|  | dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", | 
|  | res_type_str, | 
|  | mem_node->base, | 
|  | mem_node->length); | 
|  |  | 
|  | mem_node->next = func->mem_head; | 
|  | func->mem_head = mem_node; | 
|  | } | 
|  | break; | 
|  | case PCI_BASE_ADDRESS_MEM_TYPE_64: | 
|  | pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2); | 
|  | base64 = temp_register2; | 
|  | base64 = (base64 << 32) | save_base; | 
|  |  | 
|  | if (temp_register2) { | 
|  | dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n", | 
|  | res_type_str, temp_register2, (u32)base64); | 
|  | base64 &= 0x00000000FFFFFFFFL; | 
|  | } | 
|  |  | 
|  | if (prefetchable) { | 
|  | p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; | 
|  | p_mem_node->length = base; | 
|  | dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", | 
|  | res_type_str, | 
|  | p_mem_node->base, | 
|  | p_mem_node->length); | 
|  |  | 
|  | p_mem_node->next = func->p_mem_head; | 
|  | func->p_mem_head = p_mem_node; | 
|  | } else { | 
|  | mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK; | 
|  | mem_node->length = base; | 
|  | dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", | 
|  | res_type_str, | 
|  | mem_node->base, | 
|  | mem_node->length); | 
|  |  | 
|  | mem_node->next = func->mem_head; | 
|  | func->mem_head = mem_node; | 
|  | } | 
|  | cloop += 4; | 
|  | break; | 
|  | default: | 
|  | dbg("asur: reserved BAR type=0x%x\n", | 
|  | temp_register); | 
|  | break; | 
|  | } | 
|  | } | 
|  | }	/* End of base register loop */ | 
|  | } else {	/* Some other unknown header type */ | 
|  | dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n", | 
|  | func->bus, func->device); | 
|  | } | 
|  |  | 
|  | /* find the next device in this slot */ | 
|  | if (!disable) | 
|  | break; | 
|  | func = pciehp_slot_find(func->bus, func->device, index++); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * kfree_resource_list: release memory of all list members | 
|  | * @res: resource list to free | 
|  | */ | 
|  | static inline void | 
|  | return_resource_list(struct pci_resource **func, struct pci_resource **res) | 
|  | { | 
|  | struct pci_resource *node; | 
|  | struct pci_resource *t_node; | 
|  |  | 
|  | node = *func; | 
|  | *func = NULL; | 
|  | while (node) { | 
|  | t_node = node->next; | 
|  | return_resource(res, node); | 
|  | node = t_node; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * pciehp_return_board_resources | 
|  | * | 
|  | * this routine returns all resources allocated to a board to | 
|  | * the available pool. | 
|  | * | 
|  | * returns 0 if success | 
|  | */ | 
|  | int pciehp_return_board_resources(struct pci_func * func, | 
|  | struct resource_lists * resources) | 
|  | { | 
|  | int rc; | 
|  |  | 
|  | dbg("%s\n", __FUNCTION__); | 
|  |  | 
|  | if (!func) | 
|  | return 1; | 
|  |  | 
|  | return_resource_list(&(func->io_head),&(resources->io_head)); | 
|  | return_resource_list(&(func->mem_head),&(resources->mem_head)); | 
|  | return_resource_list(&(func->p_mem_head),&(resources->p_mem_head)); | 
|  | return_resource_list(&(func->bus_head),&(resources->bus_head)); | 
|  |  | 
|  | rc = pciehp_resource_sort_and_combine(&(resources->mem_head)); | 
|  | rc |= pciehp_resource_sort_and_combine(&(resources->p_mem_head)); | 
|  | rc |= pciehp_resource_sort_and_combine(&(resources->io_head)); | 
|  | rc |= pciehp_resource_sort_and_combine(&(resources->bus_head)); | 
|  |  | 
|  | return rc; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * kfree_resource_list: release memory of all list members | 
|  | * @res: resource list to free | 
|  | */ | 
|  | static inline void | 
|  | kfree_resource_list(struct pci_resource **r) | 
|  | { | 
|  | struct pci_resource *res, *tres; | 
|  |  | 
|  | res = *r; | 
|  | *r = NULL; | 
|  |  | 
|  | while (res) { | 
|  | tres = res; | 
|  | res = res->next; | 
|  | kfree(tres); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * pciehp_destroy_resource_list: put node back in the resource list | 
|  | * @resources: list to put nodes back | 
|  | */ | 
|  | void pciehp_destroy_resource_list(struct resource_lists * resources) | 
|  | { | 
|  | kfree_resource_list(&(resources->io_head)); | 
|  | kfree_resource_list(&(resources->mem_head)); | 
|  | kfree_resource_list(&(resources->p_mem_head)); | 
|  | kfree_resource_list(&(resources->bus_head)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * pciehp_destroy_board_resources: put node back in the resource list | 
|  | * @resources: list to put nodes back | 
|  | */ | 
|  | void pciehp_destroy_board_resources(struct pci_func * func) | 
|  | { | 
|  | kfree_resource_list(&(func->io_head)); | 
|  | kfree_resource_list(&(func->mem_head)); | 
|  | kfree_resource_list(&(func->p_mem_head)); | 
|  | kfree_resource_list(&(func->bus_head)); | 
|  | } |