18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Compaq Hot Plug Controller Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1995,2001 Compaq Computer Corporation 68c2ecf20Sopenharmony_ci * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) 78c2ecf20Sopenharmony_ci * Copyright (C) 2001 IBM Corp. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * All rights reserved. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Send feedback to <greg@kroah.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/types.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 208c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 218c2ecf20Sopenharmony_ci#include <linux/pci.h> 228c2ecf20Sopenharmony_ci#include <linux/pci_hotplug.h> 238c2ecf20Sopenharmony_ci#include "../pci.h" 248c2ecf20Sopenharmony_ci#include "cpqphp.h" 258c2ecf20Sopenharmony_ci#include "cpqphp_nvram.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciu8 cpqhp_nic_irq; 298c2ecf20Sopenharmony_ciu8 cpqhp_disk_irq; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic u16 unused_IRQ; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* 348c2ecf20Sopenharmony_ci * detect_HRT_floating_pointer 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * find the Hot Plug Resource Table in the specified region of memory. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_cistatic void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iomem *end) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci void __iomem *fp; 428c2ecf20Sopenharmony_ci void __iomem *endp; 438c2ecf20Sopenharmony_ci u8 temp1, temp2, temp3, temp4; 448c2ecf20Sopenharmony_ci int status = 0; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci endp = (end - sizeof(struct hrt) + 1); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci for (fp = begin; fp <= endp; fp += 16) { 498c2ecf20Sopenharmony_ci temp1 = readb(fp + SIG0); 508c2ecf20Sopenharmony_ci temp2 = readb(fp + SIG1); 518c2ecf20Sopenharmony_ci temp3 = readb(fp + SIG2); 528c2ecf20Sopenharmony_ci temp4 = readb(fp + SIG3); 538c2ecf20Sopenharmony_ci if (temp1 == '$' && 548c2ecf20Sopenharmony_ci temp2 == 'H' && 558c2ecf20Sopenharmony_ci temp3 == 'R' && 568c2ecf20Sopenharmony_ci temp4 == 'T') { 578c2ecf20Sopenharmony_ci status = 1; 588c2ecf20Sopenharmony_ci break; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (!status) 638c2ecf20Sopenharmony_ci fp = NULL; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci dbg("Discovered Hotplug Resource Table at %p\n", fp); 668c2ecf20Sopenharmony_ci return fp; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ciint cpqhp_configure_device(struct controller *ctrl, struct pci_func *func) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct pci_bus *child; 738c2ecf20Sopenharmony_ci int num; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci pci_lock_rescan_remove(); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (func->pci_dev == NULL) 788c2ecf20Sopenharmony_ci func->pci_dev = pci_get_domain_bus_and_slot(0, func->bus, 798c2ecf20Sopenharmony_ci PCI_DEVFN(func->device, 808c2ecf20Sopenharmony_ci func->function)); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* No pci device, we need to create it then */ 838c2ecf20Sopenharmony_ci if (func->pci_dev == NULL) { 848c2ecf20Sopenharmony_ci dbg("INFO: pci_dev still null\n"); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci num = pci_scan_slot(ctrl->pci_dev->bus, PCI_DEVFN(func->device, func->function)); 878c2ecf20Sopenharmony_ci if (num) 888c2ecf20Sopenharmony_ci pci_bus_add_devices(ctrl->pci_dev->bus); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci func->pci_dev = pci_get_domain_bus_and_slot(0, func->bus, 918c2ecf20Sopenharmony_ci PCI_DEVFN(func->device, 928c2ecf20Sopenharmony_ci func->function)); 938c2ecf20Sopenharmony_ci if (func->pci_dev == NULL) { 948c2ecf20Sopenharmony_ci dbg("ERROR: pci_dev still null\n"); 958c2ecf20Sopenharmony_ci goto out; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { 1008c2ecf20Sopenharmony_ci pci_hp_add_bridge(func->pci_dev); 1018c2ecf20Sopenharmony_ci child = func->pci_dev->subordinate; 1028c2ecf20Sopenharmony_ci if (child) 1038c2ecf20Sopenharmony_ci pci_bus_add_devices(child); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci pci_dev_put(func->pci_dev); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci out: 1098c2ecf20Sopenharmony_ci pci_unlock_rescan_remove(); 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ciint cpqhp_unconfigure_device(struct pci_func *func) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci int j; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci dbg("%s: bus/dev/func = %x/%x/%x\n", __func__, func->bus, func->device, func->function); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci pci_lock_rescan_remove(); 1218c2ecf20Sopenharmony_ci for (j = 0; j < 8 ; j++) { 1228c2ecf20Sopenharmony_ci struct pci_dev *temp = pci_get_domain_bus_and_slot(0, 1238c2ecf20Sopenharmony_ci func->bus, 1248c2ecf20Sopenharmony_ci PCI_DEVFN(func->device, 1258c2ecf20Sopenharmony_ci j)); 1268c2ecf20Sopenharmony_ci if (temp) { 1278c2ecf20Sopenharmony_ci pci_dev_put(temp); 1288c2ecf20Sopenharmony_ci pci_stop_and_remove_bus_device(temp); 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci pci_unlock_rescan_remove(); 1328c2ecf20Sopenharmony_ci return 0; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic int PCI_RefinedAccessConfig(struct pci_bus *bus, unsigned int devfn, u8 offset, u32 *value) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci u32 vendID = 0; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendID) == -1) 1408c2ecf20Sopenharmony_ci return -1; 1418c2ecf20Sopenharmony_ci if (vendID == 0xffffffff) 1428c2ecf20Sopenharmony_ci return -1; 1438c2ecf20Sopenharmony_ci return pci_bus_read_config_dword(bus, devfn, offset, value); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* 1488c2ecf20Sopenharmony_ci * cpqhp_set_irq 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * @bus_num: bus number of PCI device 1518c2ecf20Sopenharmony_ci * @dev_num: device number of PCI device 1528c2ecf20Sopenharmony_ci * @slot: pointer to u8 where slot number will be returned 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ciint cpqhp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci int rc = 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (cpqhp_legacy_mode) { 1598c2ecf20Sopenharmony_ci struct pci_dev *fakedev; 1608c2ecf20Sopenharmony_ci struct pci_bus *fakebus; 1618c2ecf20Sopenharmony_ci u16 temp_word; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci fakedev = kmalloc(sizeof(*fakedev), GFP_KERNEL); 1648c2ecf20Sopenharmony_ci fakebus = kmalloc(sizeof(*fakebus), GFP_KERNEL); 1658c2ecf20Sopenharmony_ci if (!fakedev || !fakebus) { 1668c2ecf20Sopenharmony_ci kfree(fakedev); 1678c2ecf20Sopenharmony_ci kfree(fakebus); 1688c2ecf20Sopenharmony_ci return -ENOMEM; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci fakedev->devfn = dev_num << 3; 1728c2ecf20Sopenharmony_ci fakedev->bus = fakebus; 1738c2ecf20Sopenharmony_ci fakebus->number = bus_num; 1748c2ecf20Sopenharmony_ci dbg("%s: dev %d, bus %d, pin %d, num %d\n", 1758c2ecf20Sopenharmony_ci __func__, dev_num, bus_num, int_pin, irq_num); 1768c2ecf20Sopenharmony_ci rc = pcibios_set_irq_routing(fakedev, int_pin - 1, irq_num); 1778c2ecf20Sopenharmony_ci kfree(fakedev); 1788c2ecf20Sopenharmony_ci kfree(fakebus); 1798c2ecf20Sopenharmony_ci dbg("%s: rc %d\n", __func__, rc); 1808c2ecf20Sopenharmony_ci if (!rc) 1818c2ecf20Sopenharmony_ci return !rc; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* set the Edge Level Control Register (ELCR) */ 1848c2ecf20Sopenharmony_ci temp_word = inb(0x4d0); 1858c2ecf20Sopenharmony_ci temp_word |= inb(0x4d1) << 8; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci temp_word |= 0x01 << irq_num; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* This should only be for x86 as it sets the Edge Level 1908c2ecf20Sopenharmony_ci * Control Register 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci outb((u8) (temp_word & 0xFF), 0x4d0); outb((u8) ((temp_word & 1938c2ecf20Sopenharmony_ci 0xFF00) >> 8), 0x4d1); rc = 0; } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return rc; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 *dev_num) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci u16 tdevice; 2028c2ecf20Sopenharmony_ci u32 work; 2038c2ecf20Sopenharmony_ci u8 tbus; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ctrl->pci_bus->number = bus_num; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci for (tdevice = 0; tdevice < 0xFF; tdevice++) { 2088c2ecf20Sopenharmony_ci /* Scan for access first */ 2098c2ecf20Sopenharmony_ci if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) 2108c2ecf20Sopenharmony_ci continue; 2118c2ecf20Sopenharmony_ci dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice); 2128c2ecf20Sopenharmony_ci /* Yep we got one. Not a bridge ? */ 2138c2ecf20Sopenharmony_ci if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) { 2148c2ecf20Sopenharmony_ci *dev_num = tdevice; 2158c2ecf20Sopenharmony_ci dbg("found it !\n"); 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci for (tdevice = 0; tdevice < 0xFF; tdevice++) { 2208c2ecf20Sopenharmony_ci /* Scan for access first */ 2218c2ecf20Sopenharmony_ci if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) 2228c2ecf20Sopenharmony_ci continue; 2238c2ecf20Sopenharmony_ci dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice); 2248c2ecf20Sopenharmony_ci /* Yep we got one. bridge ? */ 2258c2ecf20Sopenharmony_ci if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { 2268c2ecf20Sopenharmony_ci pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(tdevice, 0), PCI_SECONDARY_BUS, &tbus); 2278c2ecf20Sopenharmony_ci /* XXX: no recursion, wtf? */ 2288c2ecf20Sopenharmony_ci dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice); 2298c2ecf20Sopenharmony_ci return 0; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return -1; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci int loop, len; 2408c2ecf20Sopenharmony_ci u32 work; 2418c2ecf20Sopenharmony_ci u8 tbus, tdevice, tslot; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci len = cpqhp_routing_table_length(); 2448c2ecf20Sopenharmony_ci for (loop = 0; loop < len; ++loop) { 2458c2ecf20Sopenharmony_ci tbus = cpqhp_routing_table->slots[loop].bus; 2468c2ecf20Sopenharmony_ci tdevice = cpqhp_routing_table->slots[loop].devfn; 2478c2ecf20Sopenharmony_ci tslot = cpqhp_routing_table->slots[loop].slot; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (tslot == slot) { 2508c2ecf20Sopenharmony_ci *bus_num = tbus; 2518c2ecf20Sopenharmony_ci *dev_num = tdevice; 2528c2ecf20Sopenharmony_ci ctrl->pci_bus->number = tbus; 2538c2ecf20Sopenharmony_ci pci_bus_read_config_dword(ctrl->pci_bus, *dev_num, PCI_VENDOR_ID, &work); 2548c2ecf20Sopenharmony_ci if (!nobridge || (work == 0xffffffff)) 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci dbg("bus_num %d devfn %d\n", *bus_num, *dev_num); 2588c2ecf20Sopenharmony_ci pci_bus_read_config_dword(ctrl->pci_bus, *dev_num, PCI_CLASS_REVISION, &work); 2598c2ecf20Sopenharmony_ci dbg("work >> 8 (%x) = BRIDGE (%x)\n", work >> 8, PCI_TO_PCI_BRIDGE_CLASS); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { 2628c2ecf20Sopenharmony_ci pci_bus_read_config_byte(ctrl->pci_bus, *dev_num, PCI_SECONDARY_BUS, &tbus); 2638c2ecf20Sopenharmony_ci dbg("Scan bus for Non Bridge: bus %d\n", tbus); 2648c2ecf20Sopenharmony_ci if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) { 2658c2ecf20Sopenharmony_ci *bus_num = tbus; 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } else 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci return -1; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ciint cpqhp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci /* plain (bridges allowed) */ 2798c2ecf20Sopenharmony_ci return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* More PCI configuration routines; this time centered around hotplug 2848c2ecf20Sopenharmony_ci * controller 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci/* 2898c2ecf20Sopenharmony_ci * cpqhp_save_config 2908c2ecf20Sopenharmony_ci * 2918c2ecf20Sopenharmony_ci * Reads configuration for all slots in a PCI bus and saves info. 2928c2ecf20Sopenharmony_ci * 2938c2ecf20Sopenharmony_ci * Note: For non-hot plug buses, the slot # saved is the device # 2948c2ecf20Sopenharmony_ci * 2958c2ecf20Sopenharmony_ci * returns 0 if success 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ciint cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci long rc; 3008c2ecf20Sopenharmony_ci u8 class_code; 3018c2ecf20Sopenharmony_ci u8 header_type; 3028c2ecf20Sopenharmony_ci u32 ID; 3038c2ecf20Sopenharmony_ci u8 secondary_bus; 3048c2ecf20Sopenharmony_ci struct pci_func *new_slot; 3058c2ecf20Sopenharmony_ci int sub_bus; 3068c2ecf20Sopenharmony_ci int FirstSupported; 3078c2ecf20Sopenharmony_ci int LastSupported; 3088c2ecf20Sopenharmony_ci int max_functions; 3098c2ecf20Sopenharmony_ci int function; 3108c2ecf20Sopenharmony_ci u8 DevError; 3118c2ecf20Sopenharmony_ci int device = 0; 3128c2ecf20Sopenharmony_ci int cloop = 0; 3138c2ecf20Sopenharmony_ci int stop_it; 3148c2ecf20Sopenharmony_ci int index; 3158c2ecf20Sopenharmony_ci u16 devfn; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Decide which slots are supported */ 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (is_hot_plug) { 3208c2ecf20Sopenharmony_ci /* 3218c2ecf20Sopenharmony_ci * is_hot_plug is the slot mask 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci FirstSupported = is_hot_plug >> 4; 3248c2ecf20Sopenharmony_ci LastSupported = FirstSupported + (is_hot_plug & 0x0F) - 1; 3258c2ecf20Sopenharmony_ci } else { 3268c2ecf20Sopenharmony_ci FirstSupported = 0; 3278c2ecf20Sopenharmony_ci LastSupported = 0x1F; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Save PCI configuration space for all devices in supported slots */ 3318c2ecf20Sopenharmony_ci ctrl->pci_bus->number = busnumber; 3328c2ecf20Sopenharmony_ci for (device = FirstSupported; device <= LastSupported; device++) { 3338c2ecf20Sopenharmony_ci ID = 0xFFFFFFFF; 3348c2ecf20Sopenharmony_ci rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (ID == 0xFFFFFFFF) { 3378c2ecf20Sopenharmony_ci if (is_hot_plug) { 3388c2ecf20Sopenharmony_ci /* Setup slot structure with entry for empty 3398c2ecf20Sopenharmony_ci * slot 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci new_slot = cpqhp_slot_create(busnumber); 3428c2ecf20Sopenharmony_ci if (new_slot == NULL) 3438c2ecf20Sopenharmony_ci return 1; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci new_slot->bus = (u8) busnumber; 3468c2ecf20Sopenharmony_ci new_slot->device = (u8) device; 3478c2ecf20Sopenharmony_ci new_slot->function = 0; 3488c2ecf20Sopenharmony_ci new_slot->is_a_board = 0; 3498c2ecf20Sopenharmony_ci new_slot->presence_save = 0; 3508c2ecf20Sopenharmony_ci new_slot->switch_save = 0; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci continue; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code); 3568c2ecf20Sopenharmony_ci if (rc) 3578c2ecf20Sopenharmony_ci return rc; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type); 3608c2ecf20Sopenharmony_ci if (rc) 3618c2ecf20Sopenharmony_ci return rc; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* If multi-function device, set max_functions to 8 */ 3648c2ecf20Sopenharmony_ci if (header_type & 0x80) 3658c2ecf20Sopenharmony_ci max_functions = 8; 3668c2ecf20Sopenharmony_ci else 3678c2ecf20Sopenharmony_ci max_functions = 1; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci function = 0; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci do { 3728c2ecf20Sopenharmony_ci DevError = 0; 3738c2ecf20Sopenharmony_ci if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { 3748c2ecf20Sopenharmony_ci /* Recurse the subordinate bus 3758c2ecf20Sopenharmony_ci * get the subordinate bus number 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus); 3788c2ecf20Sopenharmony_ci if (rc) { 3798c2ecf20Sopenharmony_ci return rc; 3808c2ecf20Sopenharmony_ci } else { 3818c2ecf20Sopenharmony_ci sub_bus = (int) secondary_bus; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Save secondary bus cfg spc 3848c2ecf20Sopenharmony_ci * with this recursive call. 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_ci rc = cpqhp_save_config(ctrl, sub_bus, 0); 3878c2ecf20Sopenharmony_ci if (rc) 3888c2ecf20Sopenharmony_ci return rc; 3898c2ecf20Sopenharmony_ci ctrl->pci_bus->number = busnumber; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci index = 0; 3948c2ecf20Sopenharmony_ci new_slot = cpqhp_slot_find(busnumber, device, index++); 3958c2ecf20Sopenharmony_ci while (new_slot && 3968c2ecf20Sopenharmony_ci (new_slot->function != (u8) function)) 3978c2ecf20Sopenharmony_ci new_slot = cpqhp_slot_find(busnumber, device, index++); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (!new_slot) { 4008c2ecf20Sopenharmony_ci /* Setup slot structure. */ 4018c2ecf20Sopenharmony_ci new_slot = cpqhp_slot_create(busnumber); 4028c2ecf20Sopenharmony_ci if (new_slot == NULL) 4038c2ecf20Sopenharmony_ci return 1; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci new_slot->bus = (u8) busnumber; 4078c2ecf20Sopenharmony_ci new_slot->device = (u8) device; 4088c2ecf20Sopenharmony_ci new_slot->function = (u8) function; 4098c2ecf20Sopenharmony_ci new_slot->is_a_board = 1; 4108c2ecf20Sopenharmony_ci new_slot->switch_save = 0x10; 4118c2ecf20Sopenharmony_ci /* In case of unsupported board */ 4128c2ecf20Sopenharmony_ci new_slot->status = DevError; 4138c2ecf20Sopenharmony_ci devfn = (new_slot->device << 3) | new_slot->function; 4148c2ecf20Sopenharmony_ci new_slot->pci_dev = pci_get_domain_bus_and_slot(0, 4158c2ecf20Sopenharmony_ci new_slot->bus, devfn); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci for (cloop = 0; cloop < 0x20; cloop++) { 4188c2ecf20Sopenharmony_ci rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) &(new_slot->config_space[cloop])); 4198c2ecf20Sopenharmony_ci if (rc) 4208c2ecf20Sopenharmony_ci return rc; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci pci_dev_put(new_slot->pci_dev); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci function++; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci stop_it = 0; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* this loop skips to the next present function 4308c2ecf20Sopenharmony_ci * reading in Class Code and Header type. 4318c2ecf20Sopenharmony_ci */ 4328c2ecf20Sopenharmony_ci while ((function < max_functions) && (!stop_it)) { 4338c2ecf20Sopenharmony_ci rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID); 4348c2ecf20Sopenharmony_ci if (ID == 0xFFFFFFFF) { 4358c2ecf20Sopenharmony_ci function++; 4368c2ecf20Sopenharmony_ci continue; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code); 4398c2ecf20Sopenharmony_ci if (rc) 4408c2ecf20Sopenharmony_ci return rc; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type); 4438c2ecf20Sopenharmony_ci if (rc) 4448c2ecf20Sopenharmony_ci return rc; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci stop_it++; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci } while (function < max_functions); 4508c2ecf20Sopenharmony_ci } /* End of FOR loop */ 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return 0; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/* 4578c2ecf20Sopenharmony_ci * cpqhp_save_slot_config 4588c2ecf20Sopenharmony_ci * 4598c2ecf20Sopenharmony_ci * Saves configuration info for all PCI devices in a given slot 4608c2ecf20Sopenharmony_ci * including subordinate buses. 4618c2ecf20Sopenharmony_ci * 4628c2ecf20Sopenharmony_ci * returns 0 if success 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_ciint cpqhp_save_slot_config(struct controller *ctrl, struct pci_func *new_slot) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci long rc; 4678c2ecf20Sopenharmony_ci u8 class_code; 4688c2ecf20Sopenharmony_ci u8 header_type; 4698c2ecf20Sopenharmony_ci u32 ID; 4708c2ecf20Sopenharmony_ci u8 secondary_bus; 4718c2ecf20Sopenharmony_ci int sub_bus; 4728c2ecf20Sopenharmony_ci int max_functions; 4738c2ecf20Sopenharmony_ci int function = 0; 4748c2ecf20Sopenharmony_ci int cloop = 0; 4758c2ecf20Sopenharmony_ci int stop_it; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ID = 0xFFFFFFFF; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci ctrl->pci_bus->number = new_slot->bus; 4808c2ecf20Sopenharmony_ci pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (ID == 0xFFFFFFFF) 4838c2ecf20Sopenharmony_ci return 2; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code); 4868c2ecf20Sopenharmony_ci pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (header_type & 0x80) /* Multi-function device */ 4898c2ecf20Sopenharmony_ci max_functions = 8; 4908c2ecf20Sopenharmony_ci else 4918c2ecf20Sopenharmony_ci max_functions = 1; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci while (function < max_functions) { 4948c2ecf20Sopenharmony_ci if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { 4958c2ecf20Sopenharmony_ci /* Recurse the subordinate bus */ 4968c2ecf20Sopenharmony_ci pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci sub_bus = (int) secondary_bus; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* Save the config headers for the secondary 5018c2ecf20Sopenharmony_ci * bus. 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_ci rc = cpqhp_save_config(ctrl, sub_bus, 0); 5048c2ecf20Sopenharmony_ci if (rc) 5058c2ecf20Sopenharmony_ci return(rc); 5068c2ecf20Sopenharmony_ci ctrl->pci_bus->number = new_slot->bus; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci new_slot->status = 0; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci for (cloop = 0; cloop < 0x20; cloop++) 5138c2ecf20Sopenharmony_ci pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) &(new_slot->config_space[cloop])); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci function++; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci stop_it = 0; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* this loop skips to the next present function 5208c2ecf20Sopenharmony_ci * reading in the Class Code and the Header type. 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_ci while ((function < max_functions) && (!stop_it)) { 5238c2ecf20Sopenharmony_ci pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (ID == 0xFFFFFFFF) 5268c2ecf20Sopenharmony_ci function++; 5278c2ecf20Sopenharmony_ci else { 5288c2ecf20Sopenharmony_ci pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code); 5298c2ecf20Sopenharmony_ci pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type); 5308c2ecf20Sopenharmony_ci stop_it++; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return 0; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/* 5418c2ecf20Sopenharmony_ci * cpqhp_save_base_addr_length 5428c2ecf20Sopenharmony_ci * 5438c2ecf20Sopenharmony_ci * Saves the length of all base address registers for the 5448c2ecf20Sopenharmony_ci * specified slot. this is for hot plug REPLACE 5458c2ecf20Sopenharmony_ci * 5468c2ecf20Sopenharmony_ci * returns 0 if success 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ciint cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func *func) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci u8 cloop; 5518c2ecf20Sopenharmony_ci u8 header_type; 5528c2ecf20Sopenharmony_ci u8 secondary_bus; 5538c2ecf20Sopenharmony_ci u8 type; 5548c2ecf20Sopenharmony_ci int sub_bus; 5558c2ecf20Sopenharmony_ci u32 temp_register; 5568c2ecf20Sopenharmony_ci u32 base; 5578c2ecf20Sopenharmony_ci u32 rc; 5588c2ecf20Sopenharmony_ci struct pci_func *next; 5598c2ecf20Sopenharmony_ci int index = 0; 5608c2ecf20Sopenharmony_ci struct pci_bus *pci_bus = ctrl->pci_bus; 5618c2ecf20Sopenharmony_ci unsigned int devfn; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci func = cpqhp_slot_find(func->bus, func->device, index++); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci while (func != NULL) { 5668c2ecf20Sopenharmony_ci pci_bus->number = func->bus; 5678c2ecf20Sopenharmony_ci devfn = PCI_DEVFN(func->device, func->function); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Check for Bridge */ 5708c2ecf20Sopenharmony_ci pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { 5738c2ecf20Sopenharmony_ci pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci sub_bus = (int) secondary_bus; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci next = cpqhp_slot_list[sub_bus]; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci while (next != NULL) { 5808c2ecf20Sopenharmony_ci rc = cpqhp_save_base_addr_length(ctrl, next); 5818c2ecf20Sopenharmony_ci if (rc) 5828c2ecf20Sopenharmony_ci return rc; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci next = next->next; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci pci_bus->number = func->bus; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* FIXME: this loop is duplicated in the non-bridge 5898c2ecf20Sopenharmony_ci * case. The two could be rolled together Figure out 5908c2ecf20Sopenharmony_ci * IO and memory base lengths 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ci for (cloop = 0x10; cloop <= 0x14; cloop += 4) { 5938c2ecf20Sopenharmony_ci temp_register = 0xFFFFFFFF; 5948c2ecf20Sopenharmony_ci pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); 5958c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, cloop, &base); 5968c2ecf20Sopenharmony_ci /* If this register is implemented */ 5978c2ecf20Sopenharmony_ci if (base) { 5988c2ecf20Sopenharmony_ci if (base & 0x01L) { 5998c2ecf20Sopenharmony_ci /* IO base 6008c2ecf20Sopenharmony_ci * set base = amount of IO space 6018c2ecf20Sopenharmony_ci * requested 6028c2ecf20Sopenharmony_ci */ 6038c2ecf20Sopenharmony_ci base = base & 0xFFFFFFFE; 6048c2ecf20Sopenharmony_ci base = (~base) + 1; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci type = 1; 6078c2ecf20Sopenharmony_ci } else { 6088c2ecf20Sopenharmony_ci /* memory base */ 6098c2ecf20Sopenharmony_ci base = base & 0xFFFFFFF0; 6108c2ecf20Sopenharmony_ci base = (~base) + 1; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci type = 0; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci } else { 6158c2ecf20Sopenharmony_ci base = 0x0L; 6168c2ecf20Sopenharmony_ci type = 0; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci /* Save information in slot structure */ 6208c2ecf20Sopenharmony_ci func->base_length[(cloop - 0x10) >> 2] = 6218c2ecf20Sopenharmony_ci base; 6228c2ecf20Sopenharmony_ci func->base_type[(cloop - 0x10) >> 2] = type; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci } /* End of base register loop */ 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci } else if ((header_type & 0x7F) == 0x00) { 6278c2ecf20Sopenharmony_ci /* Figure out IO and memory base lengths */ 6288c2ecf20Sopenharmony_ci for (cloop = 0x10; cloop <= 0x24; cloop += 4) { 6298c2ecf20Sopenharmony_ci temp_register = 0xFFFFFFFF; 6308c2ecf20Sopenharmony_ci pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); 6318c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, cloop, &base); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* If this register is implemented */ 6348c2ecf20Sopenharmony_ci if (base) { 6358c2ecf20Sopenharmony_ci if (base & 0x01L) { 6368c2ecf20Sopenharmony_ci /* IO base 6378c2ecf20Sopenharmony_ci * base = amount of IO space 6388c2ecf20Sopenharmony_ci * requested 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci base = base & 0xFFFFFFFE; 6418c2ecf20Sopenharmony_ci base = (~base) + 1; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci type = 1; 6448c2ecf20Sopenharmony_ci } else { 6458c2ecf20Sopenharmony_ci /* memory base 6468c2ecf20Sopenharmony_ci * base = amount of memory 6478c2ecf20Sopenharmony_ci * space requested 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_ci base = base & 0xFFFFFFF0; 6508c2ecf20Sopenharmony_ci base = (~base) + 1; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci type = 0; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci } else { 6558c2ecf20Sopenharmony_ci base = 0x0L; 6568c2ecf20Sopenharmony_ci type = 0; 6578c2ecf20Sopenharmony_ci } 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* Save information in slot structure */ 6608c2ecf20Sopenharmony_ci func->base_length[(cloop - 0x10) >> 2] = base; 6618c2ecf20Sopenharmony_ci func->base_type[(cloop - 0x10) >> 2] = type; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci } /* End of base register loop */ 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci } else { /* Some other unknown header type */ 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* find the next device in this slot */ 6698c2ecf20Sopenharmony_ci func = cpqhp_slot_find(func->bus, func->device, index++); 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci return(0); 6738c2ecf20Sopenharmony_ci} 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/* 6778c2ecf20Sopenharmony_ci * cpqhp_save_used_resources 6788c2ecf20Sopenharmony_ci * 6798c2ecf20Sopenharmony_ci * Stores used resource information for existing boards. this is 6808c2ecf20Sopenharmony_ci * for boards that were in the system when this driver was loaded. 6818c2ecf20Sopenharmony_ci * this function is for hot plug ADD 6828c2ecf20Sopenharmony_ci * 6838c2ecf20Sopenharmony_ci * returns 0 if success 6848c2ecf20Sopenharmony_ci */ 6858c2ecf20Sopenharmony_ciint cpqhp_save_used_resources(struct controller *ctrl, struct pci_func *func) 6868c2ecf20Sopenharmony_ci{ 6878c2ecf20Sopenharmony_ci u8 cloop; 6888c2ecf20Sopenharmony_ci u8 header_type; 6898c2ecf20Sopenharmony_ci u8 secondary_bus; 6908c2ecf20Sopenharmony_ci u8 temp_byte; 6918c2ecf20Sopenharmony_ci u8 b_base; 6928c2ecf20Sopenharmony_ci u8 b_length; 6938c2ecf20Sopenharmony_ci u16 command; 6948c2ecf20Sopenharmony_ci u16 save_command; 6958c2ecf20Sopenharmony_ci u16 w_base; 6968c2ecf20Sopenharmony_ci u16 w_length; 6978c2ecf20Sopenharmony_ci u32 temp_register; 6988c2ecf20Sopenharmony_ci u32 save_base; 6998c2ecf20Sopenharmony_ci u32 base; 7008c2ecf20Sopenharmony_ci int index = 0; 7018c2ecf20Sopenharmony_ci struct pci_resource *mem_node; 7028c2ecf20Sopenharmony_ci struct pci_resource *p_mem_node; 7038c2ecf20Sopenharmony_ci struct pci_resource *io_node; 7048c2ecf20Sopenharmony_ci struct pci_resource *bus_node; 7058c2ecf20Sopenharmony_ci struct pci_bus *pci_bus = ctrl->pci_bus; 7068c2ecf20Sopenharmony_ci unsigned int devfn; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci func = cpqhp_slot_find(func->bus, func->device, index++); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci while ((func != NULL) && func->is_a_board) { 7118c2ecf20Sopenharmony_ci pci_bus->number = func->bus; 7128c2ecf20Sopenharmony_ci devfn = PCI_DEVFN(func->device, func->function); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* Save the command register */ 7158c2ecf20Sopenharmony_ci pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci /* disable card */ 7188c2ecf20Sopenharmony_ci command = 0x00; 7198c2ecf20Sopenharmony_ci pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* Check for Bridge */ 7228c2ecf20Sopenharmony_ci pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { 7258c2ecf20Sopenharmony_ci /* Clear Bridge Control Register */ 7268c2ecf20Sopenharmony_ci command = 0x00; 7278c2ecf20Sopenharmony_ci pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); 7288c2ecf20Sopenharmony_ci pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); 7298c2ecf20Sopenharmony_ci pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte); 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL); 7328c2ecf20Sopenharmony_ci if (!bus_node) 7338c2ecf20Sopenharmony_ci return -ENOMEM; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci bus_node->base = secondary_bus; 7368c2ecf20Sopenharmony_ci bus_node->length = temp_byte - secondary_bus + 1; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci bus_node->next = func->bus_head; 7398c2ecf20Sopenharmony_ci func->bus_head = bus_node; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* Save IO base and Limit registers */ 7428c2ecf20Sopenharmony_ci pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &b_base); 7438c2ecf20Sopenharmony_ci pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &b_length); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if ((b_base <= b_length) && (save_command & 0x01)) { 7468c2ecf20Sopenharmony_ci io_node = kmalloc(sizeof(*io_node), GFP_KERNEL); 7478c2ecf20Sopenharmony_ci if (!io_node) 7488c2ecf20Sopenharmony_ci return -ENOMEM; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci io_node->base = (b_base & 0xF0) << 8; 7518c2ecf20Sopenharmony_ci io_node->length = (b_length - b_base + 0x10) << 8; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci io_node->next = func->io_head; 7548c2ecf20Sopenharmony_ci func->io_head = io_node; 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci /* Save memory base and Limit registers */ 7588c2ecf20Sopenharmony_ci pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base); 7598c2ecf20Sopenharmony_ci pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if ((w_base <= w_length) && (save_command & 0x02)) { 7628c2ecf20Sopenharmony_ci mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL); 7638c2ecf20Sopenharmony_ci if (!mem_node) 7648c2ecf20Sopenharmony_ci return -ENOMEM; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci mem_node->base = w_base << 16; 7678c2ecf20Sopenharmony_ci mem_node->length = (w_length - w_base + 0x10) << 16; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci mem_node->next = func->mem_head; 7708c2ecf20Sopenharmony_ci func->mem_head = mem_node; 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci /* Save prefetchable memory base and Limit registers */ 7748c2ecf20Sopenharmony_ci pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); 7758c2ecf20Sopenharmony_ci pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if ((w_base <= w_length) && (save_command & 0x02)) { 7788c2ecf20Sopenharmony_ci p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL); 7798c2ecf20Sopenharmony_ci if (!p_mem_node) 7808c2ecf20Sopenharmony_ci return -ENOMEM; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci p_mem_node->base = w_base << 16; 7838c2ecf20Sopenharmony_ci p_mem_node->length = (w_length - w_base + 0x10) << 16; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci p_mem_node->next = func->p_mem_head; 7868c2ecf20Sopenharmony_ci func->p_mem_head = p_mem_node; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci /* Figure out IO and memory base lengths */ 7898c2ecf20Sopenharmony_ci for (cloop = 0x10; cloop <= 0x14; cloop += 4) { 7908c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci temp_register = 0xFFFFFFFF; 7938c2ecf20Sopenharmony_ci pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); 7948c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, cloop, &base); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci temp_register = base; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* If this register is implemented */ 7998c2ecf20Sopenharmony_ci if (base) { 8008c2ecf20Sopenharmony_ci if (((base & 0x03L) == 0x01) 8018c2ecf20Sopenharmony_ci && (save_command & 0x01)) { 8028c2ecf20Sopenharmony_ci /* IO base 8038c2ecf20Sopenharmony_ci * set temp_register = amount 8048c2ecf20Sopenharmony_ci * of IO space requested 8058c2ecf20Sopenharmony_ci */ 8068c2ecf20Sopenharmony_ci temp_register = base & 0xFFFFFFFE; 8078c2ecf20Sopenharmony_ci temp_register = (~temp_register) + 1; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci io_node = kmalloc(sizeof(*io_node), 8108c2ecf20Sopenharmony_ci GFP_KERNEL); 8118c2ecf20Sopenharmony_ci if (!io_node) 8128c2ecf20Sopenharmony_ci return -ENOMEM; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci io_node->base = 8158c2ecf20Sopenharmony_ci save_base & (~0x03L); 8168c2ecf20Sopenharmony_ci io_node->length = temp_register; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci io_node->next = func->io_head; 8198c2ecf20Sopenharmony_ci func->io_head = io_node; 8208c2ecf20Sopenharmony_ci } else 8218c2ecf20Sopenharmony_ci if (((base & 0x0BL) == 0x08) 8228c2ecf20Sopenharmony_ci && (save_command & 0x02)) { 8238c2ecf20Sopenharmony_ci /* prefetchable memory base */ 8248c2ecf20Sopenharmony_ci temp_register = base & 0xFFFFFFF0; 8258c2ecf20Sopenharmony_ci temp_register = (~temp_register) + 1; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci p_mem_node = kmalloc(sizeof(*p_mem_node), 8288c2ecf20Sopenharmony_ci GFP_KERNEL); 8298c2ecf20Sopenharmony_ci if (!p_mem_node) 8308c2ecf20Sopenharmony_ci return -ENOMEM; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci p_mem_node->base = save_base & (~0x0FL); 8338c2ecf20Sopenharmony_ci p_mem_node->length = temp_register; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci p_mem_node->next = func->p_mem_head; 8368c2ecf20Sopenharmony_ci func->p_mem_head = p_mem_node; 8378c2ecf20Sopenharmony_ci } else 8388c2ecf20Sopenharmony_ci if (((base & 0x0BL) == 0x00) 8398c2ecf20Sopenharmony_ci && (save_command & 0x02)) { 8408c2ecf20Sopenharmony_ci /* prefetchable memory base */ 8418c2ecf20Sopenharmony_ci temp_register = base & 0xFFFFFFF0; 8428c2ecf20Sopenharmony_ci temp_register = (~temp_register) + 1; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci mem_node = kmalloc(sizeof(*mem_node), 8458c2ecf20Sopenharmony_ci GFP_KERNEL); 8468c2ecf20Sopenharmony_ci if (!mem_node) 8478c2ecf20Sopenharmony_ci return -ENOMEM; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci mem_node->base = save_base & (~0x0FL); 8508c2ecf20Sopenharmony_ci mem_node->length = temp_register; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci mem_node->next = func->mem_head; 8538c2ecf20Sopenharmony_ci func->mem_head = mem_node; 8548c2ecf20Sopenharmony_ci } else 8558c2ecf20Sopenharmony_ci return(1); 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci } /* End of base register loop */ 8588c2ecf20Sopenharmony_ci /* Standard header */ 8598c2ecf20Sopenharmony_ci } else if ((header_type & 0x7F) == 0x00) { 8608c2ecf20Sopenharmony_ci /* Figure out IO and memory base lengths */ 8618c2ecf20Sopenharmony_ci for (cloop = 0x10; cloop <= 0x24; cloop += 4) { 8628c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base); 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci temp_register = 0xFFFFFFFF; 8658c2ecf20Sopenharmony_ci pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); 8668c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, cloop, &base); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci temp_register = base; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci /* If this register is implemented */ 8718c2ecf20Sopenharmony_ci if (base) { 8728c2ecf20Sopenharmony_ci if (((base & 0x03L) == 0x01) 8738c2ecf20Sopenharmony_ci && (save_command & 0x01)) { 8748c2ecf20Sopenharmony_ci /* IO base 8758c2ecf20Sopenharmony_ci * set temp_register = amount 8768c2ecf20Sopenharmony_ci * of IO space requested 8778c2ecf20Sopenharmony_ci */ 8788c2ecf20Sopenharmony_ci temp_register = base & 0xFFFFFFFE; 8798c2ecf20Sopenharmony_ci temp_register = (~temp_register) + 1; 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci io_node = kmalloc(sizeof(*io_node), 8828c2ecf20Sopenharmony_ci GFP_KERNEL); 8838c2ecf20Sopenharmony_ci if (!io_node) 8848c2ecf20Sopenharmony_ci return -ENOMEM; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci io_node->base = save_base & (~0x01L); 8878c2ecf20Sopenharmony_ci io_node->length = temp_register; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci io_node->next = func->io_head; 8908c2ecf20Sopenharmony_ci func->io_head = io_node; 8918c2ecf20Sopenharmony_ci } else 8928c2ecf20Sopenharmony_ci if (((base & 0x0BL) == 0x08) 8938c2ecf20Sopenharmony_ci && (save_command & 0x02)) { 8948c2ecf20Sopenharmony_ci /* prefetchable memory base */ 8958c2ecf20Sopenharmony_ci temp_register = base & 0xFFFFFFF0; 8968c2ecf20Sopenharmony_ci temp_register = (~temp_register) + 1; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci p_mem_node = kmalloc(sizeof(*p_mem_node), 8998c2ecf20Sopenharmony_ci GFP_KERNEL); 9008c2ecf20Sopenharmony_ci if (!p_mem_node) 9018c2ecf20Sopenharmony_ci return -ENOMEM; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci p_mem_node->base = save_base & (~0x0FL); 9048c2ecf20Sopenharmony_ci p_mem_node->length = temp_register; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci p_mem_node->next = func->p_mem_head; 9078c2ecf20Sopenharmony_ci func->p_mem_head = p_mem_node; 9088c2ecf20Sopenharmony_ci } else 9098c2ecf20Sopenharmony_ci if (((base & 0x0BL) == 0x00) 9108c2ecf20Sopenharmony_ci && (save_command & 0x02)) { 9118c2ecf20Sopenharmony_ci /* prefetchable memory base */ 9128c2ecf20Sopenharmony_ci temp_register = base & 0xFFFFFFF0; 9138c2ecf20Sopenharmony_ci temp_register = (~temp_register) + 1; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci mem_node = kmalloc(sizeof(*mem_node), 9168c2ecf20Sopenharmony_ci GFP_KERNEL); 9178c2ecf20Sopenharmony_ci if (!mem_node) 9188c2ecf20Sopenharmony_ci return -ENOMEM; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci mem_node->base = save_base & (~0x0FL); 9218c2ecf20Sopenharmony_ci mem_node->length = temp_register; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci mem_node->next = func->mem_head; 9248c2ecf20Sopenharmony_ci func->mem_head = mem_node; 9258c2ecf20Sopenharmony_ci } else 9268c2ecf20Sopenharmony_ci return(1); 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci } /* End of base register loop */ 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* find the next device in this slot */ 9328c2ecf20Sopenharmony_ci func = cpqhp_slot_find(func->bus, func->device, index++); 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci return 0; 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci/* 9408c2ecf20Sopenharmony_ci * cpqhp_configure_board 9418c2ecf20Sopenharmony_ci * 9428c2ecf20Sopenharmony_ci * Copies saved configuration information to one slot. 9438c2ecf20Sopenharmony_ci * this is called recursively for bridge devices. 9448c2ecf20Sopenharmony_ci * this is for hot plug REPLACE! 9458c2ecf20Sopenharmony_ci * 9468c2ecf20Sopenharmony_ci * returns 0 if success 9478c2ecf20Sopenharmony_ci */ 9488c2ecf20Sopenharmony_ciint cpqhp_configure_board(struct controller *ctrl, struct pci_func *func) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci int cloop; 9518c2ecf20Sopenharmony_ci u8 header_type; 9528c2ecf20Sopenharmony_ci u8 secondary_bus; 9538c2ecf20Sopenharmony_ci int sub_bus; 9548c2ecf20Sopenharmony_ci struct pci_func *next; 9558c2ecf20Sopenharmony_ci u32 temp; 9568c2ecf20Sopenharmony_ci u32 rc; 9578c2ecf20Sopenharmony_ci int index = 0; 9588c2ecf20Sopenharmony_ci struct pci_bus *pci_bus = ctrl->pci_bus; 9598c2ecf20Sopenharmony_ci unsigned int devfn; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci func = cpqhp_slot_find(func->bus, func->device, index++); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci while (func != NULL) { 9648c2ecf20Sopenharmony_ci pci_bus->number = func->bus; 9658c2ecf20Sopenharmony_ci devfn = PCI_DEVFN(func->device, func->function); 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* Start at the top of config space so that the control 9688c2ecf20Sopenharmony_ci * registers are programmed last 9698c2ecf20Sopenharmony_ci */ 9708c2ecf20Sopenharmony_ci for (cloop = 0x3C; cloop > 0; cloop -= 4) 9718c2ecf20Sopenharmony_ci pci_bus_write_config_dword(pci_bus, devfn, cloop, func->config_space[cloop >> 2]); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci /* If this is a bridge device, restore subordinate devices */ 9768c2ecf20Sopenharmony_ci if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { 9778c2ecf20Sopenharmony_ci pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci sub_bus = (int) secondary_bus; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci next = cpqhp_slot_list[sub_bus]; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci while (next != NULL) { 9848c2ecf20Sopenharmony_ci rc = cpqhp_configure_board(ctrl, next); 9858c2ecf20Sopenharmony_ci if (rc) 9868c2ecf20Sopenharmony_ci return rc; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci next = next->next; 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci } else { 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci /* Check all the base Address Registers to make sure 9938c2ecf20Sopenharmony_ci * they are the same. If not, the board is different. 9948c2ecf20Sopenharmony_ci */ 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci for (cloop = 16; cloop < 40; cloop += 4) { 9978c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (temp != func->config_space[cloop >> 2]) { 10008c2ecf20Sopenharmony_ci dbg("Config space compare failure!!! offset = %x\n", cloop); 10018c2ecf20Sopenharmony_ci dbg("bus = %x, device = %x, function = %x\n", func->bus, func->device, func->function); 10028c2ecf20Sopenharmony_ci dbg("temp = %x, config space = %x\n\n", temp, func->config_space[cloop >> 2]); 10038c2ecf20Sopenharmony_ci return 1; 10048c2ecf20Sopenharmony_ci } 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci func->configured = 1; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci func = cpqhp_slot_find(func->bus, func->device, index++); 10118c2ecf20Sopenharmony_ci } 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci return 0; 10148c2ecf20Sopenharmony_ci} 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci/* 10188c2ecf20Sopenharmony_ci * cpqhp_valid_replace 10198c2ecf20Sopenharmony_ci * 10208c2ecf20Sopenharmony_ci * this function checks to see if a board is the same as the 10218c2ecf20Sopenharmony_ci * one it is replacing. this check will detect if the device's 10228c2ecf20Sopenharmony_ci * vendor or device id's are the same 10238c2ecf20Sopenharmony_ci * 10248c2ecf20Sopenharmony_ci * returns 0 if the board is the same nonzero otherwise 10258c2ecf20Sopenharmony_ci */ 10268c2ecf20Sopenharmony_ciint cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci u8 cloop; 10298c2ecf20Sopenharmony_ci u8 header_type; 10308c2ecf20Sopenharmony_ci u8 secondary_bus; 10318c2ecf20Sopenharmony_ci u8 type; 10328c2ecf20Sopenharmony_ci u32 temp_register = 0; 10338c2ecf20Sopenharmony_ci u32 base; 10348c2ecf20Sopenharmony_ci u32 rc; 10358c2ecf20Sopenharmony_ci struct pci_func *next; 10368c2ecf20Sopenharmony_ci int index = 0; 10378c2ecf20Sopenharmony_ci struct pci_bus *pci_bus = ctrl->pci_bus; 10388c2ecf20Sopenharmony_ci unsigned int devfn; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (!func->is_a_board) 10418c2ecf20Sopenharmony_ci return(ADD_NOT_SUPPORTED); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci func = cpqhp_slot_find(func->bus, func->device, index++); 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci while (func != NULL) { 10468c2ecf20Sopenharmony_ci pci_bus->number = func->bus; 10478c2ecf20Sopenharmony_ci devfn = PCI_DEVFN(func->device, func->function); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, PCI_VENDOR_ID, &temp_register); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* No adapter present */ 10528c2ecf20Sopenharmony_ci if (temp_register == 0xFFFFFFFF) 10538c2ecf20Sopenharmony_ci return(NO_ADAPTER_PRESENT); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci if (temp_register != func->config_space[0]) 10568c2ecf20Sopenharmony_ci return(ADAPTER_NOT_SAME); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci /* Check for same revision number and class code */ 10598c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, PCI_CLASS_REVISION, &temp_register); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci /* Adapter not the same */ 10628c2ecf20Sopenharmony_ci if (temp_register != func->config_space[0x08 >> 2]) 10638c2ecf20Sopenharmony_ci return(ADAPTER_NOT_SAME); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci /* Check for Bridge */ 10668c2ecf20Sopenharmony_ci pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { 10698c2ecf20Sopenharmony_ci /* In order to continue checking, we must program the 10708c2ecf20Sopenharmony_ci * bus registers in the bridge to respond to accesses 10718c2ecf20Sopenharmony_ci * for its subordinate bus(es) 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci temp_register = func->config_space[0x18 >> 2]; 10758c2ecf20Sopenharmony_ci pci_bus_write_config_dword(pci_bus, devfn, PCI_PRIMARY_BUS, temp_register); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci secondary_bus = (temp_register >> 8) & 0xFF; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci next = cpqhp_slot_list[secondary_bus]; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci while (next != NULL) { 10828c2ecf20Sopenharmony_ci rc = cpqhp_valid_replace(ctrl, next); 10838c2ecf20Sopenharmony_ci if (rc) 10848c2ecf20Sopenharmony_ci return rc; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci next = next->next; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci /* Check to see if it is a standard config header */ 10918c2ecf20Sopenharmony_ci else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { 10928c2ecf20Sopenharmony_ci /* Check subsystem vendor and ID */ 10938c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (temp_register != func->config_space[0x2C >> 2]) { 10968c2ecf20Sopenharmony_ci /* If it's a SMART-2 and the register isn't 10978c2ecf20Sopenharmony_ci * filled in, ignore the difference because 10988c2ecf20Sopenharmony_ci * they just have an old rev of the firmware 10998c2ecf20Sopenharmony_ci */ 11008c2ecf20Sopenharmony_ci if (!((func->config_space[0] == 0xAE100E11) 11018c2ecf20Sopenharmony_ci && (temp_register == 0x00L))) 11028c2ecf20Sopenharmony_ci return(ADAPTER_NOT_SAME); 11038c2ecf20Sopenharmony_ci } 11048c2ecf20Sopenharmony_ci /* Figure out IO and memory base lengths */ 11058c2ecf20Sopenharmony_ci for (cloop = 0x10; cloop <= 0x24; cloop += 4) { 11068c2ecf20Sopenharmony_ci temp_register = 0xFFFFFFFF; 11078c2ecf20Sopenharmony_ci pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); 11088c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, devfn, cloop, &base); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci /* If this register is implemented */ 11118c2ecf20Sopenharmony_ci if (base) { 11128c2ecf20Sopenharmony_ci if (base & 0x01L) { 11138c2ecf20Sopenharmony_ci /* IO base 11148c2ecf20Sopenharmony_ci * set base = amount of IO 11158c2ecf20Sopenharmony_ci * space requested 11168c2ecf20Sopenharmony_ci */ 11178c2ecf20Sopenharmony_ci base = base & 0xFFFFFFFE; 11188c2ecf20Sopenharmony_ci base = (~base) + 1; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci type = 1; 11218c2ecf20Sopenharmony_ci } else { 11228c2ecf20Sopenharmony_ci /* memory base */ 11238c2ecf20Sopenharmony_ci base = base & 0xFFFFFFF0; 11248c2ecf20Sopenharmony_ci base = (~base) + 1; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci type = 0; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci } else { 11298c2ecf20Sopenharmony_ci base = 0x0L; 11308c2ecf20Sopenharmony_ci type = 0; 11318c2ecf20Sopenharmony_ci } 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci /* Check information in slot structure */ 11348c2ecf20Sopenharmony_ci if (func->base_length[(cloop - 0x10) >> 2] != base) 11358c2ecf20Sopenharmony_ci return(ADAPTER_NOT_SAME); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (func->base_type[(cloop - 0x10) >> 2] != type) 11388c2ecf20Sopenharmony_ci return(ADAPTER_NOT_SAME); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci } /* End of base register loop */ 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci } /* End of (type 0 config space) else */ 11438c2ecf20Sopenharmony_ci else { 11448c2ecf20Sopenharmony_ci /* this is not a type 0 or 1 config space header so 11458c2ecf20Sopenharmony_ci * we don't know how to do it 11468c2ecf20Sopenharmony_ci */ 11478c2ecf20Sopenharmony_ci return(DEVICE_TYPE_NOT_SUPPORTED); 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci /* Get the next function */ 11518c2ecf20Sopenharmony_ci func = cpqhp_slot_find(func->bus, func->device, index++); 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci return 0; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci/* 11608c2ecf20Sopenharmony_ci * cpqhp_find_available_resources 11618c2ecf20Sopenharmony_ci * 11628c2ecf20Sopenharmony_ci * Finds available memory, IO, and IRQ resources for programming 11638c2ecf20Sopenharmony_ci * devices which may be added to the system 11648c2ecf20Sopenharmony_ci * this function is for hot plug ADD! 11658c2ecf20Sopenharmony_ci * 11668c2ecf20Sopenharmony_ci * returns 0 if success 11678c2ecf20Sopenharmony_ci */ 11688c2ecf20Sopenharmony_ciint cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_start) 11698c2ecf20Sopenharmony_ci{ 11708c2ecf20Sopenharmony_ci u8 temp; 11718c2ecf20Sopenharmony_ci u8 populated_slot; 11728c2ecf20Sopenharmony_ci u8 bridged_slot; 11738c2ecf20Sopenharmony_ci void __iomem *one_slot; 11748c2ecf20Sopenharmony_ci void __iomem *rom_resource_table; 11758c2ecf20Sopenharmony_ci struct pci_func *func = NULL; 11768c2ecf20Sopenharmony_ci int i = 10, index; 11778c2ecf20Sopenharmony_ci u32 temp_dword, rc; 11788c2ecf20Sopenharmony_ci struct pci_resource *mem_node; 11798c2ecf20Sopenharmony_ci struct pci_resource *p_mem_node; 11808c2ecf20Sopenharmony_ci struct pci_resource *io_node; 11818c2ecf20Sopenharmony_ci struct pci_resource *bus_node; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci rom_resource_table = detect_HRT_floating_pointer(rom_start, rom_start+0xffff); 11848c2ecf20Sopenharmony_ci dbg("rom_resource_table = %p\n", rom_resource_table); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci if (rom_resource_table == NULL) 11878c2ecf20Sopenharmony_ci return -ENODEV; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci /* Sum all resources and setup resource maps */ 11908c2ecf20Sopenharmony_ci unused_IRQ = readl(rom_resource_table + UNUSED_IRQ); 11918c2ecf20Sopenharmony_ci dbg("unused_IRQ = %x\n", unused_IRQ); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci temp = 0; 11948c2ecf20Sopenharmony_ci while (unused_IRQ) { 11958c2ecf20Sopenharmony_ci if (unused_IRQ & 1) { 11968c2ecf20Sopenharmony_ci cpqhp_disk_irq = temp; 11978c2ecf20Sopenharmony_ci break; 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci unused_IRQ = unused_IRQ >> 1; 12008c2ecf20Sopenharmony_ci temp++; 12018c2ecf20Sopenharmony_ci } 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci dbg("cpqhp_disk_irq= %d\n", cpqhp_disk_irq); 12048c2ecf20Sopenharmony_ci unused_IRQ = unused_IRQ >> 1; 12058c2ecf20Sopenharmony_ci temp++; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci while (unused_IRQ) { 12088c2ecf20Sopenharmony_ci if (unused_IRQ & 1) { 12098c2ecf20Sopenharmony_ci cpqhp_nic_irq = temp; 12108c2ecf20Sopenharmony_ci break; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci unused_IRQ = unused_IRQ >> 1; 12138c2ecf20Sopenharmony_ci temp++; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci dbg("cpqhp_nic_irq= %d\n", cpqhp_nic_irq); 12178c2ecf20Sopenharmony_ci unused_IRQ = readl(rom_resource_table + PCIIRQ); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci temp = 0; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci if (!cpqhp_nic_irq) 12228c2ecf20Sopenharmony_ci cpqhp_nic_irq = ctrl->cfgspc_irq; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (!cpqhp_disk_irq) 12258c2ecf20Sopenharmony_ci cpqhp_disk_irq = ctrl->cfgspc_irq; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci dbg("cpqhp_disk_irq, cpqhp_nic_irq= %d, %d\n", cpqhp_disk_irq, cpqhp_nic_irq); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci rc = compaq_nvram_load(rom_start, ctrl); 12308c2ecf20Sopenharmony_ci if (rc) 12318c2ecf20Sopenharmony_ci return rc; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci one_slot = rom_resource_table + sizeof(struct hrt); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci i = readb(rom_resource_table + NUMBER_OF_ENTRIES); 12368c2ecf20Sopenharmony_ci dbg("number_of_entries = %d\n", i); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci if (!readb(one_slot + SECONDARY_BUS)) 12398c2ecf20Sopenharmony_ci return 1; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci dbg("dev|IO base|length|Mem base|length|Pre base|length|PB SB MB\n"); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci while (i && readb(one_slot + SECONDARY_BUS)) { 12448c2ecf20Sopenharmony_ci u8 dev_func = readb(one_slot + DEV_FUNC); 12458c2ecf20Sopenharmony_ci u8 primary_bus = readb(one_slot + PRIMARY_BUS); 12468c2ecf20Sopenharmony_ci u8 secondary_bus = readb(one_slot + SECONDARY_BUS); 12478c2ecf20Sopenharmony_ci u8 max_bus = readb(one_slot + MAX_BUS); 12488c2ecf20Sopenharmony_ci u16 io_base = readw(one_slot + IO_BASE); 12498c2ecf20Sopenharmony_ci u16 io_length = readw(one_slot + IO_LENGTH); 12508c2ecf20Sopenharmony_ci u16 mem_base = readw(one_slot + MEM_BASE); 12518c2ecf20Sopenharmony_ci u16 mem_length = readw(one_slot + MEM_LENGTH); 12528c2ecf20Sopenharmony_ci u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE); 12538c2ecf20Sopenharmony_ci u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci dbg("%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n", 12568c2ecf20Sopenharmony_ci dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length, 12578c2ecf20Sopenharmony_ci primary_bus, secondary_bus, max_bus); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci /* If this entry isn't for our controller's bus, ignore it */ 12608c2ecf20Sopenharmony_ci if (primary_bus != ctrl->bus) { 12618c2ecf20Sopenharmony_ci i--; 12628c2ecf20Sopenharmony_ci one_slot += sizeof(struct slot_rt); 12638c2ecf20Sopenharmony_ci continue; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci /* find out if this entry is for an occupied slot */ 12668c2ecf20Sopenharmony_ci ctrl->pci_bus->number = primary_bus; 12678c2ecf20Sopenharmony_ci pci_bus_read_config_dword(ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword); 12688c2ecf20Sopenharmony_ci dbg("temp_D_word = %x\n", temp_dword); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci if (temp_dword != 0xFFFFFFFF) { 12718c2ecf20Sopenharmony_ci index = 0; 12728c2ecf20Sopenharmony_ci func = cpqhp_slot_find(primary_bus, dev_func >> 3, 0); 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci while (func && (func->function != (dev_func & 0x07))) { 12758c2ecf20Sopenharmony_ci dbg("func = %p (bus, dev, fun) = (%d, %d, %d)\n", func, primary_bus, dev_func >> 3, index); 12768c2ecf20Sopenharmony_ci func = cpqhp_slot_find(primary_bus, dev_func >> 3, index++); 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci /* If we can't find a match, skip this table entry */ 12808c2ecf20Sopenharmony_ci if (!func) { 12818c2ecf20Sopenharmony_ci i--; 12828c2ecf20Sopenharmony_ci one_slot += sizeof(struct slot_rt); 12838c2ecf20Sopenharmony_ci continue; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci /* this may not work and shouldn't be used */ 12868c2ecf20Sopenharmony_ci if (secondary_bus != primary_bus) 12878c2ecf20Sopenharmony_ci bridged_slot = 1; 12888c2ecf20Sopenharmony_ci else 12898c2ecf20Sopenharmony_ci bridged_slot = 0; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci populated_slot = 1; 12928c2ecf20Sopenharmony_ci } else { 12938c2ecf20Sopenharmony_ci populated_slot = 0; 12948c2ecf20Sopenharmony_ci bridged_slot = 0; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci /* If we've got a valid IO base, use it */ 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci temp_dword = io_base + io_length; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci if ((io_base) && (temp_dword < 0x10000)) { 13038c2ecf20Sopenharmony_ci io_node = kmalloc(sizeof(*io_node), GFP_KERNEL); 13048c2ecf20Sopenharmony_ci if (!io_node) 13058c2ecf20Sopenharmony_ci return -ENOMEM; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci io_node->base = io_base; 13088c2ecf20Sopenharmony_ci io_node->length = io_length; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci dbg("found io_node(base, length) = %x, %x\n", 13118c2ecf20Sopenharmony_ci io_node->base, io_node->length); 13128c2ecf20Sopenharmony_ci dbg("populated slot =%d \n", populated_slot); 13138c2ecf20Sopenharmony_ci if (!populated_slot) { 13148c2ecf20Sopenharmony_ci io_node->next = ctrl->io_head; 13158c2ecf20Sopenharmony_ci ctrl->io_head = io_node; 13168c2ecf20Sopenharmony_ci } else { 13178c2ecf20Sopenharmony_ci io_node->next = func->io_head; 13188c2ecf20Sopenharmony_ci func->io_head = io_node; 13198c2ecf20Sopenharmony_ci } 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* If we've got a valid memory base, use it */ 13238c2ecf20Sopenharmony_ci temp_dword = mem_base + mem_length; 13248c2ecf20Sopenharmony_ci if ((mem_base) && (temp_dword < 0x10000)) { 13258c2ecf20Sopenharmony_ci mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL); 13268c2ecf20Sopenharmony_ci if (!mem_node) 13278c2ecf20Sopenharmony_ci return -ENOMEM; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci mem_node->base = mem_base << 16; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci mem_node->length = mem_length << 16; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci dbg("found mem_node(base, length) = %x, %x\n", 13348c2ecf20Sopenharmony_ci mem_node->base, mem_node->length); 13358c2ecf20Sopenharmony_ci dbg("populated slot =%d \n", populated_slot); 13368c2ecf20Sopenharmony_ci if (!populated_slot) { 13378c2ecf20Sopenharmony_ci mem_node->next = ctrl->mem_head; 13388c2ecf20Sopenharmony_ci ctrl->mem_head = mem_node; 13398c2ecf20Sopenharmony_ci } else { 13408c2ecf20Sopenharmony_ci mem_node->next = func->mem_head; 13418c2ecf20Sopenharmony_ci func->mem_head = mem_node; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci /* If we've got a valid prefetchable memory base, and 13468c2ecf20Sopenharmony_ci * the base + length isn't greater than 0xFFFF 13478c2ecf20Sopenharmony_ci */ 13488c2ecf20Sopenharmony_ci temp_dword = pre_mem_base + pre_mem_length; 13498c2ecf20Sopenharmony_ci if ((pre_mem_base) && (temp_dword < 0x10000)) { 13508c2ecf20Sopenharmony_ci p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL); 13518c2ecf20Sopenharmony_ci if (!p_mem_node) 13528c2ecf20Sopenharmony_ci return -ENOMEM; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci p_mem_node->base = pre_mem_base << 16; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci p_mem_node->length = pre_mem_length << 16; 13578c2ecf20Sopenharmony_ci dbg("found p_mem_node(base, length) = %x, %x\n", 13588c2ecf20Sopenharmony_ci p_mem_node->base, p_mem_node->length); 13598c2ecf20Sopenharmony_ci dbg("populated slot =%d \n", populated_slot); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (!populated_slot) { 13628c2ecf20Sopenharmony_ci p_mem_node->next = ctrl->p_mem_head; 13638c2ecf20Sopenharmony_ci ctrl->p_mem_head = p_mem_node; 13648c2ecf20Sopenharmony_ci } else { 13658c2ecf20Sopenharmony_ci p_mem_node->next = func->p_mem_head; 13668c2ecf20Sopenharmony_ci func->p_mem_head = p_mem_node; 13678c2ecf20Sopenharmony_ci } 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci /* If we've got a valid bus number, use it 13718c2ecf20Sopenharmony_ci * The second condition is to ignore bus numbers on 13728c2ecf20Sopenharmony_ci * populated slots that don't have PCI-PCI bridges 13738c2ecf20Sopenharmony_ci */ 13748c2ecf20Sopenharmony_ci if (secondary_bus && (secondary_bus != primary_bus)) { 13758c2ecf20Sopenharmony_ci bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL); 13768c2ecf20Sopenharmony_ci if (!bus_node) 13778c2ecf20Sopenharmony_ci return -ENOMEM; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci bus_node->base = secondary_bus; 13808c2ecf20Sopenharmony_ci bus_node->length = max_bus - secondary_bus + 1; 13818c2ecf20Sopenharmony_ci dbg("found bus_node(base, length) = %x, %x\n", 13828c2ecf20Sopenharmony_ci bus_node->base, bus_node->length); 13838c2ecf20Sopenharmony_ci dbg("populated slot =%d \n", populated_slot); 13848c2ecf20Sopenharmony_ci if (!populated_slot) { 13858c2ecf20Sopenharmony_ci bus_node->next = ctrl->bus_head; 13868c2ecf20Sopenharmony_ci ctrl->bus_head = bus_node; 13878c2ecf20Sopenharmony_ci } else { 13888c2ecf20Sopenharmony_ci bus_node->next = func->bus_head; 13898c2ecf20Sopenharmony_ci func->bus_head = bus_node; 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci i--; 13948c2ecf20Sopenharmony_ci one_slot += sizeof(struct slot_rt); 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci /* If all of the following fail, we don't have any resources for 13988c2ecf20Sopenharmony_ci * hot plug add 13998c2ecf20Sopenharmony_ci */ 14008c2ecf20Sopenharmony_ci rc = 1; 14018c2ecf20Sopenharmony_ci rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); 14028c2ecf20Sopenharmony_ci rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); 14038c2ecf20Sopenharmony_ci rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head)); 14048c2ecf20Sopenharmony_ci rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci return rc; 14078c2ecf20Sopenharmony_ci} 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci/* 14118c2ecf20Sopenharmony_ci * cpqhp_return_board_resources 14128c2ecf20Sopenharmony_ci * 14138c2ecf20Sopenharmony_ci * this routine returns all resources allocated to a board to 14148c2ecf20Sopenharmony_ci * the available pool. 14158c2ecf20Sopenharmony_ci * 14168c2ecf20Sopenharmony_ci * returns 0 if success 14178c2ecf20Sopenharmony_ci */ 14188c2ecf20Sopenharmony_ciint cpqhp_return_board_resources(struct pci_func *func, struct resource_lists *resources) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci int rc = 0; 14218c2ecf20Sopenharmony_ci struct pci_resource *node; 14228c2ecf20Sopenharmony_ci struct pci_resource *t_node; 14238c2ecf20Sopenharmony_ci dbg("%s\n", __func__); 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (!func) 14268c2ecf20Sopenharmony_ci return 1; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci node = func->io_head; 14298c2ecf20Sopenharmony_ci func->io_head = NULL; 14308c2ecf20Sopenharmony_ci while (node) { 14318c2ecf20Sopenharmony_ci t_node = node->next; 14328c2ecf20Sopenharmony_ci return_resource(&(resources->io_head), node); 14338c2ecf20Sopenharmony_ci node = t_node; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci node = func->mem_head; 14378c2ecf20Sopenharmony_ci func->mem_head = NULL; 14388c2ecf20Sopenharmony_ci while (node) { 14398c2ecf20Sopenharmony_ci t_node = node->next; 14408c2ecf20Sopenharmony_ci return_resource(&(resources->mem_head), node); 14418c2ecf20Sopenharmony_ci node = t_node; 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci node = func->p_mem_head; 14458c2ecf20Sopenharmony_ci func->p_mem_head = NULL; 14468c2ecf20Sopenharmony_ci while (node) { 14478c2ecf20Sopenharmony_ci t_node = node->next; 14488c2ecf20Sopenharmony_ci return_resource(&(resources->p_mem_head), node); 14498c2ecf20Sopenharmony_ci node = t_node; 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci node = func->bus_head; 14538c2ecf20Sopenharmony_ci func->bus_head = NULL; 14548c2ecf20Sopenharmony_ci while (node) { 14558c2ecf20Sopenharmony_ci t_node = node->next; 14568c2ecf20Sopenharmony_ci return_resource(&(resources->bus_head), node); 14578c2ecf20Sopenharmony_ci node = t_node; 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci rc |= cpqhp_resource_sort_and_combine(&(resources->mem_head)); 14618c2ecf20Sopenharmony_ci rc |= cpqhp_resource_sort_and_combine(&(resources->p_mem_head)); 14628c2ecf20Sopenharmony_ci rc |= cpqhp_resource_sort_and_combine(&(resources->io_head)); 14638c2ecf20Sopenharmony_ci rc |= cpqhp_resource_sort_and_combine(&(resources->bus_head)); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci return rc; 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci/* 14708c2ecf20Sopenharmony_ci * cpqhp_destroy_resource_list 14718c2ecf20Sopenharmony_ci * 14728c2ecf20Sopenharmony_ci * Puts node back in the resource list pointed to by head 14738c2ecf20Sopenharmony_ci */ 14748c2ecf20Sopenharmony_civoid cpqhp_destroy_resource_list(struct resource_lists *resources) 14758c2ecf20Sopenharmony_ci{ 14768c2ecf20Sopenharmony_ci struct pci_resource *res, *tres; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci res = resources->io_head; 14798c2ecf20Sopenharmony_ci resources->io_head = NULL; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci while (res) { 14828c2ecf20Sopenharmony_ci tres = res; 14838c2ecf20Sopenharmony_ci res = res->next; 14848c2ecf20Sopenharmony_ci kfree(tres); 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci res = resources->mem_head; 14888c2ecf20Sopenharmony_ci resources->mem_head = NULL; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci while (res) { 14918c2ecf20Sopenharmony_ci tres = res; 14928c2ecf20Sopenharmony_ci res = res->next; 14938c2ecf20Sopenharmony_ci kfree(tres); 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci res = resources->p_mem_head; 14978c2ecf20Sopenharmony_ci resources->p_mem_head = NULL; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci while (res) { 15008c2ecf20Sopenharmony_ci tres = res; 15018c2ecf20Sopenharmony_ci res = res->next; 15028c2ecf20Sopenharmony_ci kfree(tres); 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci res = resources->bus_head; 15068c2ecf20Sopenharmony_ci resources->bus_head = NULL; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci while (res) { 15098c2ecf20Sopenharmony_ci tres = res; 15108c2ecf20Sopenharmony_ci res = res->next; 15118c2ecf20Sopenharmony_ci kfree(tres); 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci} 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci/* 15178c2ecf20Sopenharmony_ci * cpqhp_destroy_board_resources 15188c2ecf20Sopenharmony_ci * 15198c2ecf20Sopenharmony_ci * Puts node back in the resource list pointed to by head 15208c2ecf20Sopenharmony_ci */ 15218c2ecf20Sopenharmony_civoid cpqhp_destroy_board_resources(struct pci_func *func) 15228c2ecf20Sopenharmony_ci{ 15238c2ecf20Sopenharmony_ci struct pci_resource *res, *tres; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci res = func->io_head; 15268c2ecf20Sopenharmony_ci func->io_head = NULL; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci while (res) { 15298c2ecf20Sopenharmony_ci tres = res; 15308c2ecf20Sopenharmony_ci res = res->next; 15318c2ecf20Sopenharmony_ci kfree(tres); 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci res = func->mem_head; 15358c2ecf20Sopenharmony_ci func->mem_head = NULL; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci while (res) { 15388c2ecf20Sopenharmony_ci tres = res; 15398c2ecf20Sopenharmony_ci res = res->next; 15408c2ecf20Sopenharmony_ci kfree(tres); 15418c2ecf20Sopenharmony_ci } 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci res = func->p_mem_head; 15448c2ecf20Sopenharmony_ci func->p_mem_head = NULL; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci while (res) { 15478c2ecf20Sopenharmony_ci tres = res; 15488c2ecf20Sopenharmony_ci res = res->next; 15498c2ecf20Sopenharmony_ci kfree(tres); 15508c2ecf20Sopenharmony_ci } 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci res = func->bus_head; 15538c2ecf20Sopenharmony_ci func->bus_head = NULL; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci while (res) { 15568c2ecf20Sopenharmony_ci tres = res; 15578c2ecf20Sopenharmony_ci res = res->next; 15588c2ecf20Sopenharmony_ci kfree(tres); 15598c2ecf20Sopenharmony_ci } 15608c2ecf20Sopenharmony_ci} 1561