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/interrupt.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/wait.h> 238c2ecf20Sopenharmony_ci#include <linux/pci.h> 248c2ecf20Sopenharmony_ci#include <linux/pci_hotplug.h> 258c2ecf20Sopenharmony_ci#include <linux/kthread.h> 268c2ecf20Sopenharmony_ci#include "cpqphp.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic u32 configure_new_device(struct controller *ctrl, struct pci_func *func, 298c2ecf20Sopenharmony_ci u8 behind_bridge, struct resource_lists *resources); 308c2ecf20Sopenharmony_cistatic int configure_new_function(struct controller *ctrl, struct pci_func *func, 318c2ecf20Sopenharmony_ci u8 behind_bridge, struct resource_lists *resources); 328c2ecf20Sopenharmony_cistatic void interrupt_event_handler(struct controller *ctrl); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct task_struct *cpqhp_event_thread; 368c2ecf20Sopenharmony_cistatic struct timer_list *pushbutton_pending; /* = NULL */ 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* delay is in jiffies to wait for */ 398c2ecf20Sopenharmony_cistatic void long_delay(int delay) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci /* 428c2ecf20Sopenharmony_ci * XXX(hch): if someone is bored please convert all callers 438c2ecf20Sopenharmony_ci * to call msleep_interruptible directly. They really want 448c2ecf20Sopenharmony_ci * to specify timeouts in natural units and spend a lot of 458c2ecf20Sopenharmony_ci * effort converting them to jiffies.. 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci msleep_interruptible(jiffies_to_msecs(delay)); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* FIXME: The following line needs to be somewhere else... */ 528c2ecf20Sopenharmony_ci#define WRONG_BUS_FREQUENCY 0x07 538c2ecf20Sopenharmony_cistatic u8 handle_switch_change(u8 change, struct controller *ctrl) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int hp_slot; 568c2ecf20Sopenharmony_ci u8 rc = 0; 578c2ecf20Sopenharmony_ci u16 temp_word; 588c2ecf20Sopenharmony_ci struct pci_func *func; 598c2ecf20Sopenharmony_ci struct event_info *taskInfo; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (!change) 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* Switch Change */ 658c2ecf20Sopenharmony_ci dbg("cpqsbd: Switch interrupt received.\n"); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci for (hp_slot = 0; hp_slot < 6; hp_slot++) { 688c2ecf20Sopenharmony_ci if (change & (0x1L << hp_slot)) { 698c2ecf20Sopenharmony_ci /* 708c2ecf20Sopenharmony_ci * this one changed. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci func = cpqhp_slot_find(ctrl->bus, 738c2ecf20Sopenharmony_ci (hp_slot + ctrl->slot_device_offset), 0); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci /* this is the structure that tells the worker thread 768c2ecf20Sopenharmony_ci * what to do 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci taskInfo = &(ctrl->event_queue[ctrl->next_event]); 798c2ecf20Sopenharmony_ci ctrl->next_event = (ctrl->next_event + 1) % 10; 808c2ecf20Sopenharmony_ci taskInfo->hp_slot = hp_slot; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci rc++; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci temp_word = ctrl->ctrl_int_comp >> 16; 858c2ecf20Sopenharmony_ci func->presence_save = (temp_word >> hp_slot) & 0x01; 868c2ecf20Sopenharmony_ci func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { 898c2ecf20Sopenharmony_ci /* 908c2ecf20Sopenharmony_ci * Switch opened 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci func->switch_save = 0; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci taskInfo->event_type = INT_SWITCH_OPEN; 968c2ecf20Sopenharmony_ci } else { 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * Switch closed 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci func->switch_save = 0x10; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci taskInfo->event_type = INT_SWITCH_CLOSE; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return rc; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/** 1128c2ecf20Sopenharmony_ci * cpqhp_find_slot - find the struct slot of given device 1138c2ecf20Sopenharmony_ci * @ctrl: scan lots of this controller 1148c2ecf20Sopenharmony_ci * @device: the device id to find 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_cistatic struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct slot *slot = ctrl->slot; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci while (slot && (slot->device != device)) 1218c2ecf20Sopenharmony_ci slot = slot->next; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return slot; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic u8 handle_presence_change(u16 change, struct controller *ctrl) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci int hp_slot; 1308c2ecf20Sopenharmony_ci u8 rc = 0; 1318c2ecf20Sopenharmony_ci u8 temp_byte; 1328c2ecf20Sopenharmony_ci u16 temp_word; 1338c2ecf20Sopenharmony_ci struct pci_func *func; 1348c2ecf20Sopenharmony_ci struct event_info *taskInfo; 1358c2ecf20Sopenharmony_ci struct slot *p_slot; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (!change) 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * Presence Change 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci dbg("cpqsbd: Presence/Notify input change.\n"); 1448c2ecf20Sopenharmony_ci dbg(" Changed bits are 0x%4.4x\n", change); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci for (hp_slot = 0; hp_slot < 6; hp_slot++) { 1478c2ecf20Sopenharmony_ci if (change & (0x0101 << hp_slot)) { 1488c2ecf20Sopenharmony_ci /* 1498c2ecf20Sopenharmony_ci * this one changed. 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ci func = cpqhp_slot_find(ctrl->bus, 1528c2ecf20Sopenharmony_ci (hp_slot + ctrl->slot_device_offset), 0); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci taskInfo = &(ctrl->event_queue[ctrl->next_event]); 1558c2ecf20Sopenharmony_ci ctrl->next_event = (ctrl->next_event + 1) % 10; 1568c2ecf20Sopenharmony_ci taskInfo->hp_slot = hp_slot; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci rc++; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci p_slot = cpqhp_find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4)); 1618c2ecf20Sopenharmony_ci if (!p_slot) 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* If the switch closed, must be a button 1658c2ecf20Sopenharmony_ci * If not in button mode, nevermind 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci if (func->switch_save && (ctrl->push_button == 1)) { 1688c2ecf20Sopenharmony_ci temp_word = ctrl->ctrl_int_comp >> 16; 1698c2ecf20Sopenharmony_ci temp_byte = (temp_word >> hp_slot) & 0x01; 1708c2ecf20Sopenharmony_ci temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (temp_byte != func->presence_save) { 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * button Pressed (doesn't do anything) 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci dbg("hp_slot %d button pressed\n", hp_slot); 1778c2ecf20Sopenharmony_ci taskInfo->event_type = INT_BUTTON_PRESS; 1788c2ecf20Sopenharmony_ci } else { 1798c2ecf20Sopenharmony_ci /* 1808c2ecf20Sopenharmony_ci * button Released - TAKE ACTION!!!! 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci dbg("hp_slot %d button released\n", hp_slot); 1838c2ecf20Sopenharmony_ci taskInfo->event_type = INT_BUTTON_RELEASE; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* Cancel if we are still blinking */ 1868c2ecf20Sopenharmony_ci if ((p_slot->state == BLINKINGON_STATE) 1878c2ecf20Sopenharmony_ci || (p_slot->state == BLINKINGOFF_STATE)) { 1888c2ecf20Sopenharmony_ci taskInfo->event_type = INT_BUTTON_CANCEL; 1898c2ecf20Sopenharmony_ci dbg("hp_slot %d button cancel\n", hp_slot); 1908c2ecf20Sopenharmony_ci } else if ((p_slot->state == POWERON_STATE) 1918c2ecf20Sopenharmony_ci || (p_slot->state == POWEROFF_STATE)) { 1928c2ecf20Sopenharmony_ci /* info(msg_button_ignore, p_slot->number); */ 1938c2ecf20Sopenharmony_ci taskInfo->event_type = INT_BUTTON_IGNORE; 1948c2ecf20Sopenharmony_ci dbg("hp_slot %d button ignore\n", hp_slot); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci } else { 1988c2ecf20Sopenharmony_ci /* Switch is open, assume a presence change 1998c2ecf20Sopenharmony_ci * Save the presence state 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_ci temp_word = ctrl->ctrl_int_comp >> 16; 2028c2ecf20Sopenharmony_ci func->presence_save = (temp_word >> hp_slot) & 0x01; 2038c2ecf20Sopenharmony_ci func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if ((!(ctrl->ctrl_int_comp & (0x010000 << hp_slot))) || 2068c2ecf20Sopenharmony_ci (!(ctrl->ctrl_int_comp & (0x01000000 << hp_slot)))) { 2078c2ecf20Sopenharmony_ci /* Present */ 2088c2ecf20Sopenharmony_ci taskInfo->event_type = INT_PRESENCE_ON; 2098c2ecf20Sopenharmony_ci } else { 2108c2ecf20Sopenharmony_ci /* Not Present */ 2118c2ecf20Sopenharmony_ci taskInfo->event_type = INT_PRESENCE_OFF; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return rc; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic u8 handle_power_fault(u8 change, struct controller *ctrl) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci int hp_slot; 2248c2ecf20Sopenharmony_ci u8 rc = 0; 2258c2ecf20Sopenharmony_ci struct pci_func *func; 2268c2ecf20Sopenharmony_ci struct event_info *taskInfo; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (!change) 2298c2ecf20Sopenharmony_ci return 0; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* 2328c2ecf20Sopenharmony_ci * power fault 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci info("power fault interrupt\n"); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci for (hp_slot = 0; hp_slot < 6; hp_slot++) { 2388c2ecf20Sopenharmony_ci if (change & (0x01 << hp_slot)) { 2398c2ecf20Sopenharmony_ci /* 2408c2ecf20Sopenharmony_ci * this one changed. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci func = cpqhp_slot_find(ctrl->bus, 2438c2ecf20Sopenharmony_ci (hp_slot + ctrl->slot_device_offset), 0); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci taskInfo = &(ctrl->event_queue[ctrl->next_event]); 2468c2ecf20Sopenharmony_ci ctrl->next_event = (ctrl->next_event + 1) % 10; 2478c2ecf20Sopenharmony_ci taskInfo->hp_slot = hp_slot; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci rc++; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) { 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * power fault Cleared 2548c2ecf20Sopenharmony_ci */ 2558c2ecf20Sopenharmony_ci func->status = 0x00; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci taskInfo->event_type = INT_POWER_FAULT_CLEAR; 2588c2ecf20Sopenharmony_ci } else { 2598c2ecf20Sopenharmony_ci /* 2608c2ecf20Sopenharmony_ci * power fault 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_ci taskInfo->event_type = INT_POWER_FAULT; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (ctrl->rev < 4) { 2658c2ecf20Sopenharmony_ci amber_LED_on(ctrl, hp_slot); 2668c2ecf20Sopenharmony_ci green_LED_off(ctrl, hp_slot); 2678c2ecf20Sopenharmony_ci set_SOGO(ctrl); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* this is a fatal condition, we want 2708c2ecf20Sopenharmony_ci * to crash the machine to protect from 2718c2ecf20Sopenharmony_ci * data corruption. simulated_NMI 2728c2ecf20Sopenharmony_ci * shouldn't ever return */ 2738c2ecf20Sopenharmony_ci /* FIXME 2748c2ecf20Sopenharmony_ci simulated_NMI(hp_slot, ctrl); */ 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* The following code causes a software 2778c2ecf20Sopenharmony_ci * crash just in case simulated_NMI did 2788c2ecf20Sopenharmony_ci * return */ 2798c2ecf20Sopenharmony_ci /*FIXME 2808c2ecf20Sopenharmony_ci panic(msg_power_fault); */ 2818c2ecf20Sopenharmony_ci } else { 2828c2ecf20Sopenharmony_ci /* set power fault status for this board */ 2838c2ecf20Sopenharmony_ci func->status = 0xFF; 2848c2ecf20Sopenharmony_ci info("power fault bit %x set\n", hp_slot); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return rc; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci/** 2958c2ecf20Sopenharmony_ci * sort_by_size - sort nodes on the list by their length, smallest first. 2968c2ecf20Sopenharmony_ci * @head: list to sort 2978c2ecf20Sopenharmony_ci */ 2988c2ecf20Sopenharmony_cistatic int sort_by_size(struct pci_resource **head) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct pci_resource *current_res; 3018c2ecf20Sopenharmony_ci struct pci_resource *next_res; 3028c2ecf20Sopenharmony_ci int out_of_order = 1; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci if (!(*head)) 3058c2ecf20Sopenharmony_ci return 1; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!((*head)->next)) 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci while (out_of_order) { 3118c2ecf20Sopenharmony_ci out_of_order = 0; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* Special case for swapping list head */ 3148c2ecf20Sopenharmony_ci if (((*head)->next) && 3158c2ecf20Sopenharmony_ci ((*head)->length > (*head)->next->length)) { 3168c2ecf20Sopenharmony_ci out_of_order++; 3178c2ecf20Sopenharmony_ci current_res = *head; 3188c2ecf20Sopenharmony_ci *head = (*head)->next; 3198c2ecf20Sopenharmony_ci current_res->next = (*head)->next; 3208c2ecf20Sopenharmony_ci (*head)->next = current_res; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci current_res = *head; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci while (current_res->next && current_res->next->next) { 3268c2ecf20Sopenharmony_ci if (current_res->next->length > current_res->next->next->length) { 3278c2ecf20Sopenharmony_ci out_of_order++; 3288c2ecf20Sopenharmony_ci next_res = current_res->next; 3298c2ecf20Sopenharmony_ci current_res->next = current_res->next->next; 3308c2ecf20Sopenharmony_ci current_res = current_res->next; 3318c2ecf20Sopenharmony_ci next_res->next = current_res->next; 3328c2ecf20Sopenharmony_ci current_res->next = next_res; 3338c2ecf20Sopenharmony_ci } else 3348c2ecf20Sopenharmony_ci current_res = current_res->next; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci } /* End of out_of_order loop */ 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/** 3438c2ecf20Sopenharmony_ci * sort_by_max_size - sort nodes on the list by their length, largest first. 3448c2ecf20Sopenharmony_ci * @head: list to sort 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_cistatic int sort_by_max_size(struct pci_resource **head) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci struct pci_resource *current_res; 3498c2ecf20Sopenharmony_ci struct pci_resource *next_res; 3508c2ecf20Sopenharmony_ci int out_of_order = 1; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (!(*head)) 3538c2ecf20Sopenharmony_ci return 1; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!((*head)->next)) 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci while (out_of_order) { 3598c2ecf20Sopenharmony_ci out_of_order = 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Special case for swapping list head */ 3628c2ecf20Sopenharmony_ci if (((*head)->next) && 3638c2ecf20Sopenharmony_ci ((*head)->length < (*head)->next->length)) { 3648c2ecf20Sopenharmony_ci out_of_order++; 3658c2ecf20Sopenharmony_ci current_res = *head; 3668c2ecf20Sopenharmony_ci *head = (*head)->next; 3678c2ecf20Sopenharmony_ci current_res->next = (*head)->next; 3688c2ecf20Sopenharmony_ci (*head)->next = current_res; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci current_res = *head; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci while (current_res->next && current_res->next->next) { 3748c2ecf20Sopenharmony_ci if (current_res->next->length < current_res->next->next->length) { 3758c2ecf20Sopenharmony_ci out_of_order++; 3768c2ecf20Sopenharmony_ci next_res = current_res->next; 3778c2ecf20Sopenharmony_ci current_res->next = current_res->next->next; 3788c2ecf20Sopenharmony_ci current_res = current_res->next; 3798c2ecf20Sopenharmony_ci next_res->next = current_res->next; 3808c2ecf20Sopenharmony_ci current_res->next = next_res; 3818c2ecf20Sopenharmony_ci } else 3828c2ecf20Sopenharmony_ci current_res = current_res->next; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci } /* End of out_of_order loop */ 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return 0; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/** 3918c2ecf20Sopenharmony_ci * do_pre_bridge_resource_split - find node of resources that are unused 3928c2ecf20Sopenharmony_ci * @head: new list head 3938c2ecf20Sopenharmony_ci * @orig_head: original list head 3948c2ecf20Sopenharmony_ci * @alignment: max node size (?) 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_cistatic struct pci_resource *do_pre_bridge_resource_split(struct pci_resource **head, 3978c2ecf20Sopenharmony_ci struct pci_resource **orig_head, u32 alignment) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct pci_resource *prevnode = NULL; 4008c2ecf20Sopenharmony_ci struct pci_resource *node; 4018c2ecf20Sopenharmony_ci struct pci_resource *split_node; 4028c2ecf20Sopenharmony_ci u32 rc; 4038c2ecf20Sopenharmony_ci u32 temp_dword; 4048c2ecf20Sopenharmony_ci dbg("do_pre_bridge_resource_split\n"); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (!(*head) || !(*orig_head)) 4078c2ecf20Sopenharmony_ci return NULL; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci rc = cpqhp_resource_sort_and_combine(head); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (rc) 4128c2ecf20Sopenharmony_ci return NULL; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if ((*head)->base != (*orig_head)->base) 4158c2ecf20Sopenharmony_ci return NULL; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if ((*head)->length == (*orig_head)->length) 4188c2ecf20Sopenharmony_ci return NULL; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* If we got here, there the bridge requires some of the resource, but 4228c2ecf20Sopenharmony_ci * we may be able to split some off of the front 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci node = *head; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (node->length & (alignment - 1)) { 4288c2ecf20Sopenharmony_ci /* this one isn't an aligned length, so we'll make a new entry 4298c2ecf20Sopenharmony_ci * and split it up. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ci split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci if (!split_node) 4348c2ecf20Sopenharmony_ci return NULL; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci temp_dword = (node->length | (alignment-1)) + 1 - alignment; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci split_node->base = node->base; 4398c2ecf20Sopenharmony_ci split_node->length = temp_dword; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci node->length -= temp_dword; 4428c2ecf20Sopenharmony_ci node->base += split_node->length; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* Put it in the list */ 4458c2ecf20Sopenharmony_ci *head = split_node; 4468c2ecf20Sopenharmony_ci split_node->next = node; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (node->length < alignment) 4508c2ecf20Sopenharmony_ci return NULL; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* Now unlink it */ 4538c2ecf20Sopenharmony_ci if (*head == node) { 4548c2ecf20Sopenharmony_ci *head = node->next; 4558c2ecf20Sopenharmony_ci } else { 4568c2ecf20Sopenharmony_ci prevnode = *head; 4578c2ecf20Sopenharmony_ci while (prevnode->next != node) 4588c2ecf20Sopenharmony_ci prevnode = prevnode->next; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci prevnode->next = node->next; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci node->next = NULL; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return node; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci/** 4698c2ecf20Sopenharmony_ci * do_bridge_resource_split - find one node of resources that aren't in use 4708c2ecf20Sopenharmony_ci * @head: list head 4718c2ecf20Sopenharmony_ci * @alignment: max node size (?) 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_cistatic struct pci_resource *do_bridge_resource_split(struct pci_resource **head, u32 alignment) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct pci_resource *prevnode = NULL; 4768c2ecf20Sopenharmony_ci struct pci_resource *node; 4778c2ecf20Sopenharmony_ci u32 rc; 4788c2ecf20Sopenharmony_ci u32 temp_dword; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci rc = cpqhp_resource_sort_and_combine(head); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (rc) 4838c2ecf20Sopenharmony_ci return NULL; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci node = *head; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci while (node->next) { 4888c2ecf20Sopenharmony_ci prevnode = node; 4898c2ecf20Sopenharmony_ci node = node->next; 4908c2ecf20Sopenharmony_ci kfree(prevnode); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (node->length < alignment) 4948c2ecf20Sopenharmony_ci goto error; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (node->base & (alignment - 1)) { 4978c2ecf20Sopenharmony_ci /* Short circuit if adjusted size is too small */ 4988c2ecf20Sopenharmony_ci temp_dword = (node->base | (alignment-1)) + 1; 4998c2ecf20Sopenharmony_ci if ((node->length - (temp_dword - node->base)) < alignment) 5008c2ecf20Sopenharmony_ci goto error; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci node->length -= (temp_dword - node->base); 5038c2ecf20Sopenharmony_ci node->base = temp_dword; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (node->length & (alignment - 1)) 5078c2ecf20Sopenharmony_ci /* There's stuff in use after this node */ 5088c2ecf20Sopenharmony_ci goto error; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return node; 5118c2ecf20Sopenharmony_cierror: 5128c2ecf20Sopenharmony_ci kfree(node); 5138c2ecf20Sopenharmony_ci return NULL; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci/** 5188c2ecf20Sopenharmony_ci * get_io_resource - find first node of given size not in ISA aliasing window. 5198c2ecf20Sopenharmony_ci * @head: list to search 5208c2ecf20Sopenharmony_ci * @size: size of node to find, must be a power of two. 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * Description: This function sorts the resource list by size and then returns 5238c2ecf20Sopenharmony_ci * returns the first node of "size" length that is not in the ISA aliasing 5248c2ecf20Sopenharmony_ci * window. If it finds a node larger than "size" it will split it up. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_cistatic struct pci_resource *get_io_resource(struct pci_resource **head, u32 size) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct pci_resource *prevnode; 5298c2ecf20Sopenharmony_ci struct pci_resource *node; 5308c2ecf20Sopenharmony_ci struct pci_resource *split_node; 5318c2ecf20Sopenharmony_ci u32 temp_dword; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (!(*head)) 5348c2ecf20Sopenharmony_ci return NULL; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (cpqhp_resource_sort_and_combine(head)) 5378c2ecf20Sopenharmony_ci return NULL; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci if (sort_by_size(head)) 5408c2ecf20Sopenharmony_ci return NULL; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci for (node = *head; node; node = node->next) { 5438c2ecf20Sopenharmony_ci if (node->length < size) 5448c2ecf20Sopenharmony_ci continue; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (node->base & (size - 1)) { 5478c2ecf20Sopenharmony_ci /* this one isn't base aligned properly 5488c2ecf20Sopenharmony_ci * so we'll make a new entry and split it up 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_ci temp_dword = (node->base | (size-1)) + 1; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Short circuit if adjusted size is too small */ 5538c2ecf20Sopenharmony_ci if ((node->length - (temp_dword - node->base)) < size) 5548c2ecf20Sopenharmony_ci continue; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (!split_node) 5598c2ecf20Sopenharmony_ci return NULL; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci split_node->base = node->base; 5628c2ecf20Sopenharmony_ci split_node->length = temp_dword - node->base; 5638c2ecf20Sopenharmony_ci node->base = temp_dword; 5648c2ecf20Sopenharmony_ci node->length -= split_node->length; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Put it in the list */ 5678c2ecf20Sopenharmony_ci split_node->next = node->next; 5688c2ecf20Sopenharmony_ci node->next = split_node; 5698c2ecf20Sopenharmony_ci } /* End of non-aligned base */ 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Don't need to check if too small since we already did */ 5728c2ecf20Sopenharmony_ci if (node->length > size) { 5738c2ecf20Sopenharmony_ci /* this one is longer than we need 5748c2ecf20Sopenharmony_ci * so we'll make a new entry and split it up 5758c2ecf20Sopenharmony_ci */ 5768c2ecf20Sopenharmony_ci split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (!split_node) 5798c2ecf20Sopenharmony_ci return NULL; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci split_node->base = node->base + size; 5828c2ecf20Sopenharmony_ci split_node->length = node->length - size; 5838c2ecf20Sopenharmony_ci node->length = size; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* Put it in the list */ 5868c2ecf20Sopenharmony_ci split_node->next = node->next; 5878c2ecf20Sopenharmony_ci node->next = split_node; 5888c2ecf20Sopenharmony_ci } /* End of too big on top end */ 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* For IO make sure it's not in the ISA aliasing space */ 5918c2ecf20Sopenharmony_ci if (node->base & 0x300L) 5928c2ecf20Sopenharmony_ci continue; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* If we got here, then it is the right size 5958c2ecf20Sopenharmony_ci * Now take it out of the list and break 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_ci if (*head == node) { 5988c2ecf20Sopenharmony_ci *head = node->next; 5998c2ecf20Sopenharmony_ci } else { 6008c2ecf20Sopenharmony_ci prevnode = *head; 6018c2ecf20Sopenharmony_ci while (prevnode->next != node) 6028c2ecf20Sopenharmony_ci prevnode = prevnode->next; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci prevnode->next = node->next; 6058c2ecf20Sopenharmony_ci } 6068c2ecf20Sopenharmony_ci node->next = NULL; 6078c2ecf20Sopenharmony_ci break; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return node; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci/** 6158c2ecf20Sopenharmony_ci * get_max_resource - get largest node which has at least the given size. 6168c2ecf20Sopenharmony_ci * @head: the list to search the node in 6178c2ecf20Sopenharmony_ci * @size: the minimum size of the node to find 6188c2ecf20Sopenharmony_ci * 6198c2ecf20Sopenharmony_ci * Description: Gets the largest node that is at least "size" big from the 6208c2ecf20Sopenharmony_ci * list pointed to by head. It aligns the node on top and bottom 6218c2ecf20Sopenharmony_ci * to "size" alignment before returning it. 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_cistatic struct pci_resource *get_max_resource(struct pci_resource **head, u32 size) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct pci_resource *max; 6268c2ecf20Sopenharmony_ci struct pci_resource *temp; 6278c2ecf20Sopenharmony_ci struct pci_resource *split_node; 6288c2ecf20Sopenharmony_ci u32 temp_dword; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (cpqhp_resource_sort_and_combine(head)) 6318c2ecf20Sopenharmony_ci return NULL; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (sort_by_max_size(head)) 6348c2ecf20Sopenharmony_ci return NULL; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci for (max = *head; max; max = max->next) { 6378c2ecf20Sopenharmony_ci /* If not big enough we could probably just bail, 6388c2ecf20Sopenharmony_ci * instead we'll continue to the next. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci if (max->length < size) 6418c2ecf20Sopenharmony_ci continue; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (max->base & (size - 1)) { 6448c2ecf20Sopenharmony_ci /* this one isn't base aligned properly 6458c2ecf20Sopenharmony_ci * so we'll make a new entry and split it up 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_ci temp_dword = (max->base | (size-1)) + 1; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* Short circuit if adjusted size is too small */ 6508c2ecf20Sopenharmony_ci if ((max->length - (temp_dword - max->base)) < size) 6518c2ecf20Sopenharmony_ci continue; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (!split_node) 6568c2ecf20Sopenharmony_ci return NULL; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci split_node->base = max->base; 6598c2ecf20Sopenharmony_ci split_node->length = temp_dword - max->base; 6608c2ecf20Sopenharmony_ci max->base = temp_dword; 6618c2ecf20Sopenharmony_ci max->length -= split_node->length; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci split_node->next = max->next; 6648c2ecf20Sopenharmony_ci max->next = split_node; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if ((max->base + max->length) & (size - 1)) { 6688c2ecf20Sopenharmony_ci /* this one isn't end aligned properly at the top 6698c2ecf20Sopenharmony_ci * so we'll make a new entry and split it up 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_ci split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (!split_node) 6748c2ecf20Sopenharmony_ci return NULL; 6758c2ecf20Sopenharmony_ci temp_dword = ((max->base + max->length) & ~(size - 1)); 6768c2ecf20Sopenharmony_ci split_node->base = temp_dword; 6778c2ecf20Sopenharmony_ci split_node->length = max->length + max->base 6788c2ecf20Sopenharmony_ci - split_node->base; 6798c2ecf20Sopenharmony_ci max->length -= split_node->length; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci split_node->next = max->next; 6828c2ecf20Sopenharmony_ci max->next = split_node; 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* Make sure it didn't shrink too much when we aligned it */ 6868c2ecf20Sopenharmony_ci if (max->length < size) 6878c2ecf20Sopenharmony_ci continue; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* Now take it out of the list */ 6908c2ecf20Sopenharmony_ci temp = *head; 6918c2ecf20Sopenharmony_ci if (temp == max) { 6928c2ecf20Sopenharmony_ci *head = max->next; 6938c2ecf20Sopenharmony_ci } else { 6948c2ecf20Sopenharmony_ci while (temp && temp->next != max) 6958c2ecf20Sopenharmony_ci temp = temp->next; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci if (temp) 6988c2ecf20Sopenharmony_ci temp->next = max->next; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci max->next = NULL; 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci return max; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci/** 7108c2ecf20Sopenharmony_ci * get_resource - find resource of given size and split up larger ones. 7118c2ecf20Sopenharmony_ci * @head: the list to search for resources 7128c2ecf20Sopenharmony_ci * @size: the size limit to use 7138c2ecf20Sopenharmony_ci * 7148c2ecf20Sopenharmony_ci * Description: This function sorts the resource list by size and then 7158c2ecf20Sopenharmony_ci * returns the first node of "size" length. If it finds a node 7168c2ecf20Sopenharmony_ci * larger than "size" it will split it up. 7178c2ecf20Sopenharmony_ci * 7188c2ecf20Sopenharmony_ci * size must be a power of two. 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_cistatic struct pci_resource *get_resource(struct pci_resource **head, u32 size) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct pci_resource *prevnode; 7238c2ecf20Sopenharmony_ci struct pci_resource *node; 7248c2ecf20Sopenharmony_ci struct pci_resource *split_node; 7258c2ecf20Sopenharmony_ci u32 temp_dword; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (cpqhp_resource_sort_and_combine(head)) 7288c2ecf20Sopenharmony_ci return NULL; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (sort_by_size(head)) 7318c2ecf20Sopenharmony_ci return NULL; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci for (node = *head; node; node = node->next) { 7348c2ecf20Sopenharmony_ci dbg("%s: req_size =%x node=%p, base=%x, length=%x\n", 7358c2ecf20Sopenharmony_ci __func__, size, node, node->base, node->length); 7368c2ecf20Sopenharmony_ci if (node->length < size) 7378c2ecf20Sopenharmony_ci continue; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (node->base & (size - 1)) { 7408c2ecf20Sopenharmony_ci dbg("%s: not aligned\n", __func__); 7418c2ecf20Sopenharmony_ci /* this one isn't base aligned properly 7428c2ecf20Sopenharmony_ci * so we'll make a new entry and split it up 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_ci temp_dword = (node->base | (size-1)) + 1; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* Short circuit if adjusted size is too small */ 7478c2ecf20Sopenharmony_ci if ((node->length - (temp_dword - node->base)) < size) 7488c2ecf20Sopenharmony_ci continue; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (!split_node) 7538c2ecf20Sopenharmony_ci return NULL; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci split_node->base = node->base; 7568c2ecf20Sopenharmony_ci split_node->length = temp_dword - node->base; 7578c2ecf20Sopenharmony_ci node->base = temp_dword; 7588c2ecf20Sopenharmony_ci node->length -= split_node->length; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci split_node->next = node->next; 7618c2ecf20Sopenharmony_ci node->next = split_node; 7628c2ecf20Sopenharmony_ci } /* End of non-aligned base */ 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci /* Don't need to check if too small since we already did */ 7658c2ecf20Sopenharmony_ci if (node->length > size) { 7668c2ecf20Sopenharmony_ci dbg("%s: too big\n", __func__); 7678c2ecf20Sopenharmony_ci /* this one is longer than we need 7688c2ecf20Sopenharmony_ci * so we'll make a new entry and split it up 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_ci split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (!split_node) 7738c2ecf20Sopenharmony_ci return NULL; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci split_node->base = node->base + size; 7768c2ecf20Sopenharmony_ci split_node->length = node->length - size; 7778c2ecf20Sopenharmony_ci node->length = size; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci /* Put it in the list */ 7808c2ecf20Sopenharmony_ci split_node->next = node->next; 7818c2ecf20Sopenharmony_ci node->next = split_node; 7828c2ecf20Sopenharmony_ci } /* End of too big on top end */ 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci dbg("%s: got one!!!\n", __func__); 7858c2ecf20Sopenharmony_ci /* If we got here, then it is the right size 7868c2ecf20Sopenharmony_ci * Now take it out of the list */ 7878c2ecf20Sopenharmony_ci if (*head == node) { 7888c2ecf20Sopenharmony_ci *head = node->next; 7898c2ecf20Sopenharmony_ci } else { 7908c2ecf20Sopenharmony_ci prevnode = *head; 7918c2ecf20Sopenharmony_ci while (prevnode->next != node) 7928c2ecf20Sopenharmony_ci prevnode = prevnode->next; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci prevnode->next = node->next; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci node->next = NULL; 7978c2ecf20Sopenharmony_ci break; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci return node; 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci/** 8048c2ecf20Sopenharmony_ci * cpqhp_resource_sort_and_combine - sort nodes by base addresses and clean up 8058c2ecf20Sopenharmony_ci * @head: the list to sort and clean up 8068c2ecf20Sopenharmony_ci * 8078c2ecf20Sopenharmony_ci * Description: Sorts all of the nodes in the list in ascending order by 8088c2ecf20Sopenharmony_ci * their base addresses. Also does garbage collection by 8098c2ecf20Sopenharmony_ci * combining adjacent nodes. 8108c2ecf20Sopenharmony_ci * 8118c2ecf20Sopenharmony_ci * Returns %0 if success. 8128c2ecf20Sopenharmony_ci */ 8138c2ecf20Sopenharmony_ciint cpqhp_resource_sort_and_combine(struct pci_resource **head) 8148c2ecf20Sopenharmony_ci{ 8158c2ecf20Sopenharmony_ci struct pci_resource *node1; 8168c2ecf20Sopenharmony_ci struct pci_resource *node2; 8178c2ecf20Sopenharmony_ci int out_of_order = 1; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci dbg("%s: head = %p, *head = %p\n", __func__, head, *head); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (!(*head)) 8228c2ecf20Sopenharmony_ci return 1; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci dbg("*head->next = %p\n", (*head)->next); 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci if (!(*head)->next) 8278c2ecf20Sopenharmony_ci return 0; /* only one item on the list, already sorted! */ 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci dbg("*head->base = 0x%x\n", (*head)->base); 8308c2ecf20Sopenharmony_ci dbg("*head->next->base = 0x%x\n", (*head)->next->base); 8318c2ecf20Sopenharmony_ci while (out_of_order) { 8328c2ecf20Sopenharmony_ci out_of_order = 0; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci /* Special case for swapping list head */ 8358c2ecf20Sopenharmony_ci if (((*head)->next) && 8368c2ecf20Sopenharmony_ci ((*head)->base > (*head)->next->base)) { 8378c2ecf20Sopenharmony_ci node1 = *head; 8388c2ecf20Sopenharmony_ci (*head) = (*head)->next; 8398c2ecf20Sopenharmony_ci node1->next = (*head)->next; 8408c2ecf20Sopenharmony_ci (*head)->next = node1; 8418c2ecf20Sopenharmony_ci out_of_order++; 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci node1 = (*head); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci while (node1->next && node1->next->next) { 8478c2ecf20Sopenharmony_ci if (node1->next->base > node1->next->next->base) { 8488c2ecf20Sopenharmony_ci out_of_order++; 8498c2ecf20Sopenharmony_ci node2 = node1->next; 8508c2ecf20Sopenharmony_ci node1->next = node1->next->next; 8518c2ecf20Sopenharmony_ci node1 = node1->next; 8528c2ecf20Sopenharmony_ci node2->next = node1->next; 8538c2ecf20Sopenharmony_ci node1->next = node2; 8548c2ecf20Sopenharmony_ci } else 8558c2ecf20Sopenharmony_ci node1 = node1->next; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci } /* End of out_of_order loop */ 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci node1 = *head; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci while (node1 && node1->next) { 8628c2ecf20Sopenharmony_ci if ((node1->base + node1->length) == node1->next->base) { 8638c2ecf20Sopenharmony_ci /* Combine */ 8648c2ecf20Sopenharmony_ci dbg("8..\n"); 8658c2ecf20Sopenharmony_ci node1->length += node1->next->length; 8668c2ecf20Sopenharmony_ci node2 = node1->next; 8678c2ecf20Sopenharmony_ci node1->next = node1->next->next; 8688c2ecf20Sopenharmony_ci kfree(node2); 8698c2ecf20Sopenharmony_ci } else 8708c2ecf20Sopenharmony_ci node1 = node1->next; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci return 0; 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ciirqreturn_t cpqhp_ctrl_intr(int IRQ, void *data) 8788c2ecf20Sopenharmony_ci{ 8798c2ecf20Sopenharmony_ci struct controller *ctrl = data; 8808c2ecf20Sopenharmony_ci u8 schedule_flag = 0; 8818c2ecf20Sopenharmony_ci u8 reset; 8828c2ecf20Sopenharmony_ci u16 misc; 8838c2ecf20Sopenharmony_ci u32 Diff; 8848c2ecf20Sopenharmony_ci u32 temp_dword; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci misc = readw(ctrl->hpc_reg + MISC); 8888c2ecf20Sopenharmony_ci /* 8898c2ecf20Sopenharmony_ci * Check to see if it was our interrupt 8908c2ecf20Sopenharmony_ci */ 8918c2ecf20Sopenharmony_ci if (!(misc & 0x000C)) 8928c2ecf20Sopenharmony_ci return IRQ_NONE; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (misc & 0x0004) { 8958c2ecf20Sopenharmony_ci /* 8968c2ecf20Sopenharmony_ci * Serial Output interrupt Pending 8978c2ecf20Sopenharmony_ci */ 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci /* Clear the interrupt */ 9008c2ecf20Sopenharmony_ci misc |= 0x0004; 9018c2ecf20Sopenharmony_ci writew(misc, ctrl->hpc_reg + MISC); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* Read to clear posted writes */ 9048c2ecf20Sopenharmony_ci misc = readw(ctrl->hpc_reg + MISC); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci dbg("%s - waking up\n", __func__); 9078c2ecf20Sopenharmony_ci wake_up_interruptible(&ctrl->queue); 9088c2ecf20Sopenharmony_ci } 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (misc & 0x0008) { 9118c2ecf20Sopenharmony_ci /* General-interrupt-input interrupt Pending */ 9128c2ecf20Sopenharmony_ci Diff = readl(ctrl->hpc_reg + INT_INPUT_CLEAR) ^ ctrl->ctrl_int_comp; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci /* Clear the interrupt */ 9178c2ecf20Sopenharmony_ci writel(Diff, ctrl->hpc_reg + INT_INPUT_CLEAR); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci /* Read it back to clear any posted writes */ 9208c2ecf20Sopenharmony_ci temp_dword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci if (!Diff) 9238c2ecf20Sopenharmony_ci /* Clear all interrupts */ 9248c2ecf20Sopenharmony_ci writel(0xFFFFFFFF, ctrl->hpc_reg + INT_INPUT_CLEAR); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci schedule_flag += handle_switch_change((u8)(Diff & 0xFFL), ctrl); 9278c2ecf20Sopenharmony_ci schedule_flag += handle_presence_change((u16)((Diff & 0xFFFF0000L) >> 16), ctrl); 9288c2ecf20Sopenharmony_ci schedule_flag += handle_power_fault((u8)((Diff & 0xFF00L) >> 8), ctrl); 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE); 9328c2ecf20Sopenharmony_ci if (reset & 0x40) { 9338c2ecf20Sopenharmony_ci /* Bus reset has completed */ 9348c2ecf20Sopenharmony_ci reset &= 0xCF; 9358c2ecf20Sopenharmony_ci writeb(reset, ctrl->hpc_reg + RESET_FREQ_MODE); 9368c2ecf20Sopenharmony_ci reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE); 9378c2ecf20Sopenharmony_ci wake_up_interruptible(&ctrl->queue); 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (schedule_flag) { 9418c2ecf20Sopenharmony_ci wake_up_process(cpqhp_event_thread); 9428c2ecf20Sopenharmony_ci dbg("Waking even thread"); 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci/** 9498c2ecf20Sopenharmony_ci * cpqhp_slot_create - Creates a node and adds it to the proper bus. 9508c2ecf20Sopenharmony_ci * @busnumber: bus where new node is to be located 9518c2ecf20Sopenharmony_ci * 9528c2ecf20Sopenharmony_ci * Returns pointer to the new node or %NULL if unsuccessful. 9538c2ecf20Sopenharmony_ci */ 9548c2ecf20Sopenharmony_cistruct pci_func *cpqhp_slot_create(u8 busnumber) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci struct pci_func *new_slot; 9578c2ecf20Sopenharmony_ci struct pci_func *next; 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci new_slot = kzalloc(sizeof(*new_slot), GFP_KERNEL); 9608c2ecf20Sopenharmony_ci if (new_slot == NULL) 9618c2ecf20Sopenharmony_ci return new_slot; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci new_slot->next = NULL; 9648c2ecf20Sopenharmony_ci new_slot->configured = 1; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci if (cpqhp_slot_list[busnumber] == NULL) { 9678c2ecf20Sopenharmony_ci cpqhp_slot_list[busnumber] = new_slot; 9688c2ecf20Sopenharmony_ci } else { 9698c2ecf20Sopenharmony_ci next = cpqhp_slot_list[busnumber]; 9708c2ecf20Sopenharmony_ci while (next->next != NULL) 9718c2ecf20Sopenharmony_ci next = next->next; 9728c2ecf20Sopenharmony_ci next->next = new_slot; 9738c2ecf20Sopenharmony_ci } 9748c2ecf20Sopenharmony_ci return new_slot; 9758c2ecf20Sopenharmony_ci} 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci/** 9798c2ecf20Sopenharmony_ci * slot_remove - Removes a node from the linked list of slots. 9808c2ecf20Sopenharmony_ci * @old_slot: slot to remove 9818c2ecf20Sopenharmony_ci * 9828c2ecf20Sopenharmony_ci * Returns %0 if successful, !0 otherwise. 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_cistatic int slot_remove(struct pci_func *old_slot) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci struct pci_func *next; 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (old_slot == NULL) 9898c2ecf20Sopenharmony_ci return 1; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci next = cpqhp_slot_list[old_slot->bus]; 9928c2ecf20Sopenharmony_ci if (next == NULL) 9938c2ecf20Sopenharmony_ci return 1; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (next == old_slot) { 9968c2ecf20Sopenharmony_ci cpqhp_slot_list[old_slot->bus] = old_slot->next; 9978c2ecf20Sopenharmony_ci cpqhp_destroy_board_resources(old_slot); 9988c2ecf20Sopenharmony_ci kfree(old_slot); 9998c2ecf20Sopenharmony_ci return 0; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci while ((next->next != old_slot) && (next->next != NULL)) 10038c2ecf20Sopenharmony_ci next = next->next; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (next->next == old_slot) { 10068c2ecf20Sopenharmony_ci next->next = old_slot->next; 10078c2ecf20Sopenharmony_ci cpqhp_destroy_board_resources(old_slot); 10088c2ecf20Sopenharmony_ci kfree(old_slot); 10098c2ecf20Sopenharmony_ci return 0; 10108c2ecf20Sopenharmony_ci } else 10118c2ecf20Sopenharmony_ci return 2; 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci/** 10168c2ecf20Sopenharmony_ci * bridge_slot_remove - Removes a node from the linked list of slots. 10178c2ecf20Sopenharmony_ci * @bridge: bridge to remove 10188c2ecf20Sopenharmony_ci * 10198c2ecf20Sopenharmony_ci * Returns %0 if successful, !0 otherwise. 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_cistatic int bridge_slot_remove(struct pci_func *bridge) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci u8 subordinateBus, secondaryBus; 10248c2ecf20Sopenharmony_ci u8 tempBus; 10258c2ecf20Sopenharmony_ci struct pci_func *next; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF; 10288c2ecf20Sopenharmony_ci subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) { 10318c2ecf20Sopenharmony_ci next = cpqhp_slot_list[tempBus]; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci while (!slot_remove(next)) 10348c2ecf20Sopenharmony_ci next = cpqhp_slot_list[tempBus]; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci next = cpqhp_slot_list[bridge->bus]; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (next == NULL) 10408c2ecf20Sopenharmony_ci return 1; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci if (next == bridge) { 10438c2ecf20Sopenharmony_ci cpqhp_slot_list[bridge->bus] = bridge->next; 10448c2ecf20Sopenharmony_ci goto out; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci while ((next->next != bridge) && (next->next != NULL)) 10488c2ecf20Sopenharmony_ci next = next->next; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci if (next->next != bridge) 10518c2ecf20Sopenharmony_ci return 2; 10528c2ecf20Sopenharmony_ci next->next = bridge->next; 10538c2ecf20Sopenharmony_ciout: 10548c2ecf20Sopenharmony_ci kfree(bridge); 10558c2ecf20Sopenharmony_ci return 0; 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci/** 10608c2ecf20Sopenharmony_ci * cpqhp_slot_find - Looks for a node by bus, and device, multiple functions accessed 10618c2ecf20Sopenharmony_ci * @bus: bus to find 10628c2ecf20Sopenharmony_ci * @device: device to find 10638c2ecf20Sopenharmony_ci * @index: is %0 for first function found, %1 for the second... 10648c2ecf20Sopenharmony_ci * 10658c2ecf20Sopenharmony_ci * Returns pointer to the node if successful, %NULL otherwise. 10668c2ecf20Sopenharmony_ci */ 10678c2ecf20Sopenharmony_cistruct pci_func *cpqhp_slot_find(u8 bus, u8 device, u8 index) 10688c2ecf20Sopenharmony_ci{ 10698c2ecf20Sopenharmony_ci int found = -1; 10708c2ecf20Sopenharmony_ci struct pci_func *func; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci func = cpqhp_slot_list[bus]; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci if ((func == NULL) || ((func->device == device) && (index == 0))) 10758c2ecf20Sopenharmony_ci return func; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (func->device == device) 10788c2ecf20Sopenharmony_ci found++; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci while (func->next != NULL) { 10818c2ecf20Sopenharmony_ci func = func->next; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci if (func->device == device) 10848c2ecf20Sopenharmony_ci found++; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (found == index) 10878c2ecf20Sopenharmony_ci return func; 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci return NULL; 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci/* DJZ: I don't think is_bridge will work as is. 10958c2ecf20Sopenharmony_ci * FIXME */ 10968c2ecf20Sopenharmony_cistatic int is_bridge(struct pci_func *func) 10978c2ecf20Sopenharmony_ci{ 10988c2ecf20Sopenharmony_ci /* Check the header type */ 10998c2ecf20Sopenharmony_ci if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01) 11008c2ecf20Sopenharmony_ci return 1; 11018c2ecf20Sopenharmony_ci else 11028c2ecf20Sopenharmony_ci return 0; 11038c2ecf20Sopenharmony_ci} 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci/** 11078c2ecf20Sopenharmony_ci * set_controller_speed - set the frequency and/or mode of a specific controller segment. 11088c2ecf20Sopenharmony_ci * @ctrl: controller to change frequency/mode for. 11098c2ecf20Sopenharmony_ci * @adapter_speed: the speed of the adapter we want to match. 11108c2ecf20Sopenharmony_ci * @hp_slot: the slot number where the adapter is installed. 11118c2ecf20Sopenharmony_ci * 11128c2ecf20Sopenharmony_ci * Returns %0 if we successfully change frequency and/or mode to match the 11138c2ecf20Sopenharmony_ci * adapter speed. 11148c2ecf20Sopenharmony_ci */ 11158c2ecf20Sopenharmony_cistatic u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci struct slot *slot; 11188c2ecf20Sopenharmony_ci struct pci_bus *bus = ctrl->pci_bus; 11198c2ecf20Sopenharmony_ci u8 reg; 11208c2ecf20Sopenharmony_ci u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER); 11218c2ecf20Sopenharmony_ci u16 reg16; 11228c2ecf20Sopenharmony_ci u32 leds = readl(ctrl->hpc_reg + LED_CONTROL); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci if (bus->cur_bus_speed == adapter_speed) 11258c2ecf20Sopenharmony_ci return 0; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* We don't allow freq/mode changes if we find another adapter running 11288c2ecf20Sopenharmony_ci * in another slot on this controller 11298c2ecf20Sopenharmony_ci */ 11308c2ecf20Sopenharmony_ci for (slot = ctrl->slot; slot; slot = slot->next) { 11318c2ecf20Sopenharmony_ci if (slot->device == (hp_slot + ctrl->slot_device_offset)) 11328c2ecf20Sopenharmony_ci continue; 11338c2ecf20Sopenharmony_ci if (get_presence_status(ctrl, slot) == 0) 11348c2ecf20Sopenharmony_ci continue; 11358c2ecf20Sopenharmony_ci /* If another adapter is running on the same segment but at a 11368c2ecf20Sopenharmony_ci * lower speed/mode, we allow the new adapter to function at 11378c2ecf20Sopenharmony_ci * this rate if supported 11388c2ecf20Sopenharmony_ci */ 11398c2ecf20Sopenharmony_ci if (bus->cur_bus_speed < adapter_speed) 11408c2ecf20Sopenharmony_ci return 0; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci return 1; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci /* If the controller doesn't support freq/mode changes and the 11468c2ecf20Sopenharmony_ci * controller is running at a higher mode, we bail 11478c2ecf20Sopenharmony_ci */ 11488c2ecf20Sopenharmony_ci if ((bus->cur_bus_speed > adapter_speed) && (!ctrl->pcix_speed_capability)) 11498c2ecf20Sopenharmony_ci return 1; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci /* But we allow the adapter to run at a lower rate if possible */ 11528c2ecf20Sopenharmony_ci if ((bus->cur_bus_speed < adapter_speed) && (!ctrl->pcix_speed_capability)) 11538c2ecf20Sopenharmony_ci return 0; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci /* We try to set the max speed supported by both the adapter and 11568c2ecf20Sopenharmony_ci * controller 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_ci if (bus->max_bus_speed < adapter_speed) { 11598c2ecf20Sopenharmony_ci if (bus->cur_bus_speed == bus->max_bus_speed) 11608c2ecf20Sopenharmony_ci return 0; 11618c2ecf20Sopenharmony_ci adapter_speed = bus->max_bus_speed; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci writel(0x0L, ctrl->hpc_reg + LED_CONTROL); 11658c2ecf20Sopenharmony_ci writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci set_SOGO(ctrl); 11688c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if (adapter_speed != PCI_SPEED_133MHz_PCIX) 11718c2ecf20Sopenharmony_ci reg = 0xF5; 11728c2ecf20Sopenharmony_ci else 11738c2ecf20Sopenharmony_ci reg = 0xF4; 11748c2ecf20Sopenharmony_ci pci_write_config_byte(ctrl->pci_dev, 0x41, reg); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ); 11778c2ecf20Sopenharmony_ci reg16 &= ~0x000F; 11788c2ecf20Sopenharmony_ci switch (adapter_speed) { 11798c2ecf20Sopenharmony_ci case(PCI_SPEED_133MHz_PCIX): 11808c2ecf20Sopenharmony_ci reg = 0x75; 11818c2ecf20Sopenharmony_ci reg16 |= 0xB; 11828c2ecf20Sopenharmony_ci break; 11838c2ecf20Sopenharmony_ci case(PCI_SPEED_100MHz_PCIX): 11848c2ecf20Sopenharmony_ci reg = 0x74; 11858c2ecf20Sopenharmony_ci reg16 |= 0xA; 11868c2ecf20Sopenharmony_ci break; 11878c2ecf20Sopenharmony_ci case(PCI_SPEED_66MHz_PCIX): 11888c2ecf20Sopenharmony_ci reg = 0x73; 11898c2ecf20Sopenharmony_ci reg16 |= 0x9; 11908c2ecf20Sopenharmony_ci break; 11918c2ecf20Sopenharmony_ci case(PCI_SPEED_66MHz): 11928c2ecf20Sopenharmony_ci reg = 0x73; 11938c2ecf20Sopenharmony_ci reg16 |= 0x1; 11948c2ecf20Sopenharmony_ci break; 11958c2ecf20Sopenharmony_ci default: /* 33MHz PCI 2.2 */ 11968c2ecf20Sopenharmony_ci reg = 0x71; 11978c2ecf20Sopenharmony_ci break; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci reg16 |= 0xB << 12; 12018c2ecf20Sopenharmony_ci writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci mdelay(5); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci /* Reenable interrupts */ 12068c2ecf20Sopenharmony_ci writel(0, ctrl->hpc_reg + INT_MASK); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci pci_write_config_byte(ctrl->pci_dev, 0x41, reg); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci /* Restart state machine */ 12118c2ecf20Sopenharmony_ci reg = ~0xF; 12128c2ecf20Sopenharmony_ci pci_read_config_byte(ctrl->pci_dev, 0x43, ®); 12138c2ecf20Sopenharmony_ci pci_write_config_byte(ctrl->pci_dev, 0x43, reg); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* Only if mode change...*/ 12168c2ecf20Sopenharmony_ci if (((bus->cur_bus_speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) || 12178c2ecf20Sopenharmony_ci ((bus->cur_bus_speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz))) 12188c2ecf20Sopenharmony_ci set_SOGO(ctrl); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 12218c2ecf20Sopenharmony_ci mdelay(1100); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* Restore LED/Slot state */ 12248c2ecf20Sopenharmony_ci writel(leds, ctrl->hpc_reg + LED_CONTROL); 12258c2ecf20Sopenharmony_ci writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci set_SOGO(ctrl); 12288c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci bus->cur_bus_speed = adapter_speed; 12318c2ecf20Sopenharmony_ci slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci info("Successfully changed frequency/mode for adapter in slot %d\n", 12348c2ecf20Sopenharmony_ci slot->number); 12358c2ecf20Sopenharmony_ci return 0; 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci/* the following routines constitute the bulk of the 12398c2ecf20Sopenharmony_ci * hotplug controller logic 12408c2ecf20Sopenharmony_ci */ 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci/** 12448c2ecf20Sopenharmony_ci * board_replaced - Called after a board has been replaced in the system. 12458c2ecf20Sopenharmony_ci * @func: PCI device/function information 12468c2ecf20Sopenharmony_ci * @ctrl: hotplug controller 12478c2ecf20Sopenharmony_ci * 12488c2ecf20Sopenharmony_ci * This is only used if we don't have resources for hot add. 12498c2ecf20Sopenharmony_ci * Turns power on for the board. 12508c2ecf20Sopenharmony_ci * Checks to see if board is the same. 12518c2ecf20Sopenharmony_ci * If board is same, reconfigures it. 12528c2ecf20Sopenharmony_ci * If board isn't same, turns it back off. 12538c2ecf20Sopenharmony_ci */ 12548c2ecf20Sopenharmony_cistatic u32 board_replaced(struct pci_func *func, struct controller *ctrl) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci struct pci_bus *bus = ctrl->pci_bus; 12578c2ecf20Sopenharmony_ci u8 hp_slot; 12588c2ecf20Sopenharmony_ci u8 temp_byte; 12598c2ecf20Sopenharmony_ci u8 adapter_speed; 12608c2ecf20Sopenharmony_ci u32 rc = 0; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci hp_slot = func->device - ctrl->slot_device_offset; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci /* 12658c2ecf20Sopenharmony_ci * The switch is open. 12668c2ecf20Sopenharmony_ci */ 12678c2ecf20Sopenharmony_ci if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)) 12688c2ecf20Sopenharmony_ci rc = INTERLOCK_OPEN; 12698c2ecf20Sopenharmony_ci /* 12708c2ecf20Sopenharmony_ci * The board is already on 12718c2ecf20Sopenharmony_ci */ 12728c2ecf20Sopenharmony_ci else if (is_slot_enabled(ctrl, hp_slot)) 12738c2ecf20Sopenharmony_ci rc = CARD_FUNCTIONING; 12748c2ecf20Sopenharmony_ci else { 12758c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci /* turn on board without attaching to the bus */ 12788c2ecf20Sopenharmony_ci enable_slot_power(ctrl, hp_slot); 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci set_SOGO(ctrl); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 12838c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci /* Change bits in slot power register to force another shift out 12868c2ecf20Sopenharmony_ci * NOTE: this is to work around the timer bug */ 12878c2ecf20Sopenharmony_ci temp_byte = readb(ctrl->hpc_reg + SLOT_POWER); 12888c2ecf20Sopenharmony_ci writeb(0x00, ctrl->hpc_reg + SLOT_POWER); 12898c2ecf20Sopenharmony_ci writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci set_SOGO(ctrl); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 12948c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci adapter_speed = get_adapter_speed(ctrl, hp_slot); 12978c2ecf20Sopenharmony_ci if (bus->cur_bus_speed != adapter_speed) 12988c2ecf20Sopenharmony_ci if (set_controller_speed(ctrl, adapter_speed, hp_slot)) 12998c2ecf20Sopenharmony_ci rc = WRONG_BUS_FREQUENCY; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci /* turn off board without attaching to the bus */ 13028c2ecf20Sopenharmony_ci disable_slot_power(ctrl, hp_slot); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci set_SOGO(ctrl); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 13078c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (rc) 13128c2ecf20Sopenharmony_ci return rc; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci slot_enable(ctrl, hp_slot); 13178c2ecf20Sopenharmony_ci green_LED_blink(ctrl, hp_slot); 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci amber_LED_off(ctrl, hp_slot); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci set_SOGO(ctrl); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 13248c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* Wait for ~1 second because of hot plug spec */ 13298c2ecf20Sopenharmony_ci long_delay(1*HZ); 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci /* Check for a power fault */ 13328c2ecf20Sopenharmony_ci if (func->status == 0xFF) { 13338c2ecf20Sopenharmony_ci /* power fault occurred, but it was benign */ 13348c2ecf20Sopenharmony_ci rc = POWER_FAILURE; 13358c2ecf20Sopenharmony_ci func->status = 0; 13368c2ecf20Sopenharmony_ci } else 13378c2ecf20Sopenharmony_ci rc = cpqhp_valid_replace(ctrl, func); 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci if (!rc) { 13408c2ecf20Sopenharmony_ci /* It must be the same board */ 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci rc = cpqhp_configure_board(ctrl, func); 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci /* If configuration fails, turn it off 13458c2ecf20Sopenharmony_ci * Get slot won't work for devices behind 13468c2ecf20Sopenharmony_ci * bridges, but in this case it will always be 13478c2ecf20Sopenharmony_ci * called for the "base" bus/dev/func of an 13488c2ecf20Sopenharmony_ci * adapter. 13498c2ecf20Sopenharmony_ci */ 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci amber_LED_on(ctrl, hp_slot); 13548c2ecf20Sopenharmony_ci green_LED_off(ctrl, hp_slot); 13558c2ecf20Sopenharmony_ci slot_disable(ctrl, hp_slot); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci set_SOGO(ctrl); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 13608c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci if (rc) 13658c2ecf20Sopenharmony_ci return rc; 13668c2ecf20Sopenharmony_ci else 13678c2ecf20Sopenharmony_ci return 1; 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci } else { 13708c2ecf20Sopenharmony_ci /* Something is wrong 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci * Get slot won't work for devices behind bridges, but 13738c2ecf20Sopenharmony_ci * in this case it will always be called for the "base" 13748c2ecf20Sopenharmony_ci * bus/dev/func of an adapter. 13758c2ecf20Sopenharmony_ci */ 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci amber_LED_on(ctrl, hp_slot); 13808c2ecf20Sopenharmony_ci green_LED_off(ctrl, hp_slot); 13818c2ecf20Sopenharmony_ci slot_disable(ctrl, hp_slot); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci set_SOGO(ctrl); 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 13868c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci return rc; 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci} 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci/** 13988c2ecf20Sopenharmony_ci * board_added - Called after a board has been added to the system. 13998c2ecf20Sopenharmony_ci * @func: PCI device/function info 14008c2ecf20Sopenharmony_ci * @ctrl: hotplug controller 14018c2ecf20Sopenharmony_ci * 14028c2ecf20Sopenharmony_ci * Turns power on for the board. 14038c2ecf20Sopenharmony_ci * Configures board. 14048c2ecf20Sopenharmony_ci */ 14058c2ecf20Sopenharmony_cistatic u32 board_added(struct pci_func *func, struct controller *ctrl) 14068c2ecf20Sopenharmony_ci{ 14078c2ecf20Sopenharmony_ci u8 hp_slot; 14088c2ecf20Sopenharmony_ci u8 temp_byte; 14098c2ecf20Sopenharmony_ci u8 adapter_speed; 14108c2ecf20Sopenharmony_ci int index; 14118c2ecf20Sopenharmony_ci u32 temp_register = 0xFFFFFFFF; 14128c2ecf20Sopenharmony_ci u32 rc = 0; 14138c2ecf20Sopenharmony_ci struct pci_func *new_slot = NULL; 14148c2ecf20Sopenharmony_ci struct pci_bus *bus = ctrl->pci_bus; 14158c2ecf20Sopenharmony_ci struct slot *p_slot; 14168c2ecf20Sopenharmony_ci struct resource_lists res_lists; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci hp_slot = func->device - ctrl->slot_device_offset; 14198c2ecf20Sopenharmony_ci dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", 14208c2ecf20Sopenharmony_ci __func__, func->device, ctrl->slot_device_offset, hp_slot); 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci /* turn on board without attaching to the bus */ 14258c2ecf20Sopenharmony_ci enable_slot_power(ctrl, hp_slot); 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci set_SOGO(ctrl); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 14308c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci /* Change bits in slot power register to force another shift out 14338c2ecf20Sopenharmony_ci * NOTE: this is to work around the timer bug 14348c2ecf20Sopenharmony_ci */ 14358c2ecf20Sopenharmony_ci temp_byte = readb(ctrl->hpc_reg + SLOT_POWER); 14368c2ecf20Sopenharmony_ci writeb(0x00, ctrl->hpc_reg + SLOT_POWER); 14378c2ecf20Sopenharmony_ci writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER); 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci set_SOGO(ctrl); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 14428c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci adapter_speed = get_adapter_speed(ctrl, hp_slot); 14458c2ecf20Sopenharmony_ci if (bus->cur_bus_speed != adapter_speed) 14468c2ecf20Sopenharmony_ci if (set_controller_speed(ctrl, adapter_speed, hp_slot)) 14478c2ecf20Sopenharmony_ci rc = WRONG_BUS_FREQUENCY; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci /* turn off board without attaching to the bus */ 14508c2ecf20Sopenharmony_ci disable_slot_power(ctrl, hp_slot); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci set_SOGO(ctrl); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 14558c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci if (rc) 14608c2ecf20Sopenharmony_ci return rc; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci /* turn on board and blink green LED */ 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci dbg("%s: before down\n", __func__); 14678c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 14688c2ecf20Sopenharmony_ci dbg("%s: after down\n", __func__); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci dbg("%s: before slot_enable\n", __func__); 14718c2ecf20Sopenharmony_ci slot_enable(ctrl, hp_slot); 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci dbg("%s: before green_LED_blink\n", __func__); 14748c2ecf20Sopenharmony_ci green_LED_blink(ctrl, hp_slot); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci dbg("%s: before amber_LED_blink\n", __func__); 14778c2ecf20Sopenharmony_ci amber_LED_off(ctrl, hp_slot); 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci dbg("%s: before set_SOGO\n", __func__); 14808c2ecf20Sopenharmony_ci set_SOGO(ctrl); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 14838c2ecf20Sopenharmony_ci dbg("%s: before wait_for_ctrl_irq\n", __func__); 14848c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 14858c2ecf20Sopenharmony_ci dbg("%s: after wait_for_ctrl_irq\n", __func__); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci dbg("%s: before up\n", __func__); 14888c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 14898c2ecf20Sopenharmony_ci dbg("%s: after up\n", __func__); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci /* Wait for ~1 second because of hot plug spec */ 14928c2ecf20Sopenharmony_ci dbg("%s: before long_delay\n", __func__); 14938c2ecf20Sopenharmony_ci long_delay(1*HZ); 14948c2ecf20Sopenharmony_ci dbg("%s: after long_delay\n", __func__); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci dbg("%s: func status = %x\n", __func__, func->status); 14978c2ecf20Sopenharmony_ci /* Check for a power fault */ 14988c2ecf20Sopenharmony_ci if (func->status == 0xFF) { 14998c2ecf20Sopenharmony_ci /* power fault occurred, but it was benign */ 15008c2ecf20Sopenharmony_ci temp_register = 0xFFFFFFFF; 15018c2ecf20Sopenharmony_ci dbg("%s: temp register set to %x by power fault\n", __func__, temp_register); 15028c2ecf20Sopenharmony_ci rc = POWER_FAILURE; 15038c2ecf20Sopenharmony_ci func->status = 0; 15048c2ecf20Sopenharmony_ci } else { 15058c2ecf20Sopenharmony_ci /* Get vendor/device ID u32 */ 15068c2ecf20Sopenharmony_ci ctrl->pci_bus->number = func->bus; 15078c2ecf20Sopenharmony_ci rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register); 15088c2ecf20Sopenharmony_ci dbg("%s: pci_read_config_dword returns %d\n", __func__, rc); 15098c2ecf20Sopenharmony_ci dbg("%s: temp_register is %x\n", __func__, temp_register); 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci if (rc != 0) { 15128c2ecf20Sopenharmony_ci /* Something's wrong here */ 15138c2ecf20Sopenharmony_ci temp_register = 0xFFFFFFFF; 15148c2ecf20Sopenharmony_ci dbg("%s: temp register set to %x by error\n", __func__, temp_register); 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci /* Preset return code. It will be changed later if things go okay. */ 15178c2ecf20Sopenharmony_ci rc = NO_ADAPTER_PRESENT; 15188c2ecf20Sopenharmony_ci } 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci /* All F's is an empty slot or an invalid board */ 15218c2ecf20Sopenharmony_ci if (temp_register != 0xFFFFFFFF) { 15228c2ecf20Sopenharmony_ci res_lists.io_head = ctrl->io_head; 15238c2ecf20Sopenharmony_ci res_lists.mem_head = ctrl->mem_head; 15248c2ecf20Sopenharmony_ci res_lists.p_mem_head = ctrl->p_mem_head; 15258c2ecf20Sopenharmony_ci res_lists.bus_head = ctrl->bus_head; 15268c2ecf20Sopenharmony_ci res_lists.irqs = NULL; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci rc = configure_new_device(ctrl, func, 0, &res_lists); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci dbg("%s: back from configure_new_device\n", __func__); 15318c2ecf20Sopenharmony_ci ctrl->io_head = res_lists.io_head; 15328c2ecf20Sopenharmony_ci ctrl->mem_head = res_lists.mem_head; 15338c2ecf20Sopenharmony_ci ctrl->p_mem_head = res_lists.p_mem_head; 15348c2ecf20Sopenharmony_ci ctrl->bus_head = res_lists.bus_head; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); 15378c2ecf20Sopenharmony_ci cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); 15388c2ecf20Sopenharmony_ci cpqhp_resource_sort_and_combine(&(ctrl->io_head)); 15398c2ecf20Sopenharmony_ci cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci if (rc) { 15428c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci amber_LED_on(ctrl, hp_slot); 15458c2ecf20Sopenharmony_ci green_LED_off(ctrl, hp_slot); 15468c2ecf20Sopenharmony_ci slot_disable(ctrl, hp_slot); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci set_SOGO(ctrl); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 15518c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 15548c2ecf20Sopenharmony_ci return rc; 15558c2ecf20Sopenharmony_ci } else { 15568c2ecf20Sopenharmony_ci cpqhp_save_slot_config(ctrl, func); 15578c2ecf20Sopenharmony_ci } 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci func->status = 0; 15618c2ecf20Sopenharmony_ci func->switch_save = 0x10; 15628c2ecf20Sopenharmony_ci func->is_a_board = 0x01; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci /* next, we will instantiate the linux pci_dev structures (with 15658c2ecf20Sopenharmony_ci * appropriate driver notification, if already present) */ 15668c2ecf20Sopenharmony_ci dbg("%s: configure linux pci_dev structure\n", __func__); 15678c2ecf20Sopenharmony_ci index = 0; 15688c2ecf20Sopenharmony_ci do { 15698c2ecf20Sopenharmony_ci new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++); 15708c2ecf20Sopenharmony_ci if (new_slot && !new_slot->pci_dev) 15718c2ecf20Sopenharmony_ci cpqhp_configure_device(ctrl, new_slot); 15728c2ecf20Sopenharmony_ci } while (new_slot); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci green_LED_on(ctrl, hp_slot); 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci set_SOGO(ctrl); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 15818c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 15848c2ecf20Sopenharmony_ci } else { 15858c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci amber_LED_on(ctrl, hp_slot); 15888c2ecf20Sopenharmony_ci green_LED_off(ctrl, hp_slot); 15898c2ecf20Sopenharmony_ci slot_disable(ctrl, hp_slot); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci set_SOGO(ctrl); 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 15948c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci return rc; 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci return 0; 16018c2ecf20Sopenharmony_ci} 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci/** 16058c2ecf20Sopenharmony_ci * remove_board - Turns off slot and LEDs 16068c2ecf20Sopenharmony_ci * @func: PCI device/function info 16078c2ecf20Sopenharmony_ci * @replace_flag: whether replacing or adding a new device 16088c2ecf20Sopenharmony_ci * @ctrl: target controller 16098c2ecf20Sopenharmony_ci */ 16108c2ecf20Sopenharmony_cistatic u32 remove_board(struct pci_func *func, u32 replace_flag, struct controller *ctrl) 16118c2ecf20Sopenharmony_ci{ 16128c2ecf20Sopenharmony_ci int index; 16138c2ecf20Sopenharmony_ci u8 skip = 0; 16148c2ecf20Sopenharmony_ci u8 device; 16158c2ecf20Sopenharmony_ci u8 hp_slot; 16168c2ecf20Sopenharmony_ci u8 temp_byte; 16178c2ecf20Sopenharmony_ci u32 rc; 16188c2ecf20Sopenharmony_ci struct resource_lists res_lists; 16198c2ecf20Sopenharmony_ci struct pci_func *temp_func; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci if (cpqhp_unconfigure_device(func)) 16228c2ecf20Sopenharmony_ci return 1; 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci device = func->device; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci hp_slot = func->device - ctrl->slot_device_offset; 16278c2ecf20Sopenharmony_ci dbg("In %s, hp_slot = %d\n", __func__, hp_slot); 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* When we get here, it is safe to change base address registers. 16308c2ecf20Sopenharmony_ci * We will attempt to save the base address register lengths */ 16318c2ecf20Sopenharmony_ci if (replace_flag || !ctrl->add_support) 16328c2ecf20Sopenharmony_ci rc = cpqhp_save_base_addr_length(ctrl, func); 16338c2ecf20Sopenharmony_ci else if (!func->bus_head && !func->mem_head && 16348c2ecf20Sopenharmony_ci !func->p_mem_head && !func->io_head) { 16358c2ecf20Sopenharmony_ci /* Here we check to see if we've saved any of the board's 16368c2ecf20Sopenharmony_ci * resources already. If so, we'll skip the attempt to 16378c2ecf20Sopenharmony_ci * determine what's being used. */ 16388c2ecf20Sopenharmony_ci index = 0; 16398c2ecf20Sopenharmony_ci temp_func = cpqhp_slot_find(func->bus, func->device, index++); 16408c2ecf20Sopenharmony_ci while (temp_func) { 16418c2ecf20Sopenharmony_ci if (temp_func->bus_head || temp_func->mem_head 16428c2ecf20Sopenharmony_ci || temp_func->p_mem_head || temp_func->io_head) { 16438c2ecf20Sopenharmony_ci skip = 1; 16448c2ecf20Sopenharmony_ci break; 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ci temp_func = cpqhp_slot_find(temp_func->bus, temp_func->device, index++); 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci if (!skip) 16508c2ecf20Sopenharmony_ci rc = cpqhp_save_used_resources(ctrl, func); 16518c2ecf20Sopenharmony_ci } 16528c2ecf20Sopenharmony_ci /* Change status to shutdown */ 16538c2ecf20Sopenharmony_ci if (func->is_a_board) 16548c2ecf20Sopenharmony_ci func->status = 0x01; 16558c2ecf20Sopenharmony_ci func->configured = 0; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci green_LED_off(ctrl, hp_slot); 16608c2ecf20Sopenharmony_ci slot_disable(ctrl, hp_slot); 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci set_SOGO(ctrl); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci /* turn off SERR for slot */ 16658c2ecf20Sopenharmony_ci temp_byte = readb(ctrl->hpc_reg + SLOT_SERR); 16668c2ecf20Sopenharmony_ci temp_byte &= ~(0x01 << hp_slot); 16678c2ecf20Sopenharmony_ci writeb(temp_byte, ctrl->hpc_reg + SLOT_SERR); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 16708c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci if (!replace_flag && ctrl->add_support) { 16758c2ecf20Sopenharmony_ci while (func) { 16768c2ecf20Sopenharmony_ci res_lists.io_head = ctrl->io_head; 16778c2ecf20Sopenharmony_ci res_lists.mem_head = ctrl->mem_head; 16788c2ecf20Sopenharmony_ci res_lists.p_mem_head = ctrl->p_mem_head; 16798c2ecf20Sopenharmony_ci res_lists.bus_head = ctrl->bus_head; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci cpqhp_return_board_resources(func, &res_lists); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci ctrl->io_head = res_lists.io_head; 16848c2ecf20Sopenharmony_ci ctrl->mem_head = res_lists.mem_head; 16858c2ecf20Sopenharmony_ci ctrl->p_mem_head = res_lists.p_mem_head; 16868c2ecf20Sopenharmony_ci ctrl->bus_head = res_lists.bus_head; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); 16898c2ecf20Sopenharmony_ci cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); 16908c2ecf20Sopenharmony_ci cpqhp_resource_sort_and_combine(&(ctrl->io_head)); 16918c2ecf20Sopenharmony_ci cpqhp_resource_sort_and_combine(&(ctrl->bus_head)); 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci if (is_bridge(func)) { 16948c2ecf20Sopenharmony_ci bridge_slot_remove(func); 16958c2ecf20Sopenharmony_ci } else 16968c2ecf20Sopenharmony_ci slot_remove(func); 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci func = cpqhp_slot_find(ctrl->bus, device, 0); 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci /* Setup slot structure with entry for empty slot */ 17028c2ecf20Sopenharmony_ci func = cpqhp_slot_create(ctrl->bus); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci if (func == NULL) 17058c2ecf20Sopenharmony_ci return 1; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci func->bus = ctrl->bus; 17088c2ecf20Sopenharmony_ci func->device = device; 17098c2ecf20Sopenharmony_ci func->function = 0; 17108c2ecf20Sopenharmony_ci func->configured = 0; 17118c2ecf20Sopenharmony_ci func->switch_save = 0x10; 17128c2ecf20Sopenharmony_ci func->is_a_board = 0; 17138c2ecf20Sopenharmony_ci func->p_task_event = NULL; 17148c2ecf20Sopenharmony_ci } 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci return 0; 17178c2ecf20Sopenharmony_ci} 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_cistatic void pushbutton_helper_thread(struct timer_list *t) 17208c2ecf20Sopenharmony_ci{ 17218c2ecf20Sopenharmony_ci pushbutton_pending = t; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci wake_up_process(cpqhp_event_thread); 17248c2ecf20Sopenharmony_ci} 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci/* this is the main worker thread */ 17288c2ecf20Sopenharmony_cistatic int event_thread(void *data) 17298c2ecf20Sopenharmony_ci{ 17308c2ecf20Sopenharmony_ci struct controller *ctrl; 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci while (1) { 17338c2ecf20Sopenharmony_ci dbg("!!!!event_thread sleeping\n"); 17348c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 17358c2ecf20Sopenharmony_ci schedule(); 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci if (kthread_should_stop()) 17388c2ecf20Sopenharmony_ci break; 17398c2ecf20Sopenharmony_ci /* Do stuff here */ 17408c2ecf20Sopenharmony_ci if (pushbutton_pending) 17418c2ecf20Sopenharmony_ci cpqhp_pushbutton_thread(pushbutton_pending); 17428c2ecf20Sopenharmony_ci else 17438c2ecf20Sopenharmony_ci for (ctrl = cpqhp_ctrl_list; ctrl; ctrl = ctrl->next) 17448c2ecf20Sopenharmony_ci interrupt_event_handler(ctrl); 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci dbg("event_thread signals exit\n"); 17478c2ecf20Sopenharmony_ci return 0; 17488c2ecf20Sopenharmony_ci} 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ciint cpqhp_event_start_thread(void) 17518c2ecf20Sopenharmony_ci{ 17528c2ecf20Sopenharmony_ci cpqhp_event_thread = kthread_run(event_thread, NULL, "phpd_event"); 17538c2ecf20Sopenharmony_ci if (IS_ERR(cpqhp_event_thread)) { 17548c2ecf20Sopenharmony_ci err("Can't start up our event thread\n"); 17558c2ecf20Sopenharmony_ci return PTR_ERR(cpqhp_event_thread); 17568c2ecf20Sopenharmony_ci } 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci return 0; 17598c2ecf20Sopenharmony_ci} 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_civoid cpqhp_event_stop_thread(void) 17638c2ecf20Sopenharmony_ci{ 17648c2ecf20Sopenharmony_ci kthread_stop(cpqhp_event_thread); 17658c2ecf20Sopenharmony_ci} 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_cistatic void interrupt_event_handler(struct controller *ctrl) 17698c2ecf20Sopenharmony_ci{ 17708c2ecf20Sopenharmony_ci int loop = 0; 17718c2ecf20Sopenharmony_ci int change = 1; 17728c2ecf20Sopenharmony_ci struct pci_func *func; 17738c2ecf20Sopenharmony_ci u8 hp_slot; 17748c2ecf20Sopenharmony_ci struct slot *p_slot; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci while (change) { 17778c2ecf20Sopenharmony_ci change = 0; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci for (loop = 0; loop < 10; loop++) { 17808c2ecf20Sopenharmony_ci /* dbg("loop %d\n", loop); */ 17818c2ecf20Sopenharmony_ci if (ctrl->event_queue[loop].event_type != 0) { 17828c2ecf20Sopenharmony_ci hp_slot = ctrl->event_queue[loop].hp_slot; 17838c2ecf20Sopenharmony_ci 17848c2ecf20Sopenharmony_ci func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); 17858c2ecf20Sopenharmony_ci if (!func) 17868c2ecf20Sopenharmony_ci return; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); 17898c2ecf20Sopenharmony_ci if (!p_slot) 17908c2ecf20Sopenharmony_ci return; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci dbg("hp_slot %d, func %p, p_slot %p\n", 17938c2ecf20Sopenharmony_ci hp_slot, func, p_slot); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) { 17968c2ecf20Sopenharmony_ci dbg("button pressed\n"); 17978c2ecf20Sopenharmony_ci } else if (ctrl->event_queue[loop].event_type == 17988c2ecf20Sopenharmony_ci INT_BUTTON_CANCEL) { 17998c2ecf20Sopenharmony_ci dbg("button cancel\n"); 18008c2ecf20Sopenharmony_ci del_timer(&p_slot->task_event); 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci if (p_slot->state == BLINKINGOFF_STATE) { 18058c2ecf20Sopenharmony_ci /* slot is on */ 18068c2ecf20Sopenharmony_ci dbg("turn on green LED\n"); 18078c2ecf20Sopenharmony_ci green_LED_on(ctrl, hp_slot); 18088c2ecf20Sopenharmony_ci } else if (p_slot->state == BLINKINGON_STATE) { 18098c2ecf20Sopenharmony_ci /* slot is off */ 18108c2ecf20Sopenharmony_ci dbg("turn off green LED\n"); 18118c2ecf20Sopenharmony_ci green_LED_off(ctrl, hp_slot); 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci info(msg_button_cancel, p_slot->number); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci p_slot->state = STATIC_STATE; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci amber_LED_off(ctrl, hp_slot); 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci set_SOGO(ctrl); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 18238c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 18268c2ecf20Sopenharmony_ci } 18278c2ecf20Sopenharmony_ci /*** button Released (No action on press...) */ 18288c2ecf20Sopenharmony_ci else if (ctrl->event_queue[loop].event_type == INT_BUTTON_RELEASE) { 18298c2ecf20Sopenharmony_ci dbg("button release\n"); 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci if (is_slot_enabled(ctrl, hp_slot)) { 18328c2ecf20Sopenharmony_ci dbg("slot is on\n"); 18338c2ecf20Sopenharmony_ci p_slot->state = BLINKINGOFF_STATE; 18348c2ecf20Sopenharmony_ci info(msg_button_off, p_slot->number); 18358c2ecf20Sopenharmony_ci } else { 18368c2ecf20Sopenharmony_ci dbg("slot is off\n"); 18378c2ecf20Sopenharmony_ci p_slot->state = BLINKINGON_STATE; 18388c2ecf20Sopenharmony_ci info(msg_button_on, p_slot->number); 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci mutex_lock(&ctrl->crit_sect); 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci dbg("blink green LED and turn off amber\n"); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci amber_LED_off(ctrl, hp_slot); 18458c2ecf20Sopenharmony_ci green_LED_blink(ctrl, hp_slot); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci set_SOGO(ctrl); 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 18508c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->crit_sect); 18538c2ecf20Sopenharmony_ci timer_setup(&p_slot->task_event, 18548c2ecf20Sopenharmony_ci pushbutton_helper_thread, 18558c2ecf20Sopenharmony_ci 0); 18568c2ecf20Sopenharmony_ci p_slot->hp_slot = hp_slot; 18578c2ecf20Sopenharmony_ci p_slot->ctrl = ctrl; 18588c2ecf20Sopenharmony_ci/* p_slot->physical_slot = physical_slot; */ 18598c2ecf20Sopenharmony_ci p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */ 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci dbg("add_timer p_slot = %p\n", p_slot); 18628c2ecf20Sopenharmony_ci add_timer(&p_slot->task_event); 18638c2ecf20Sopenharmony_ci } 18648c2ecf20Sopenharmony_ci /***********POWER FAULT */ 18658c2ecf20Sopenharmony_ci else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) { 18668c2ecf20Sopenharmony_ci dbg("power fault\n"); 18678c2ecf20Sopenharmony_ci } 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci ctrl->event_queue[loop].event_type = 0; 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ci change = 1; 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci } /* End of FOR loop */ 18748c2ecf20Sopenharmony_ci } 18758c2ecf20Sopenharmony_ci} 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci/** 18798c2ecf20Sopenharmony_ci * cpqhp_pushbutton_thread - handle pushbutton events 18808c2ecf20Sopenharmony_ci * @slot: target slot (struct) 18818c2ecf20Sopenharmony_ci * 18828c2ecf20Sopenharmony_ci * Scheduled procedure to handle blocking stuff for the pushbuttons. 18838c2ecf20Sopenharmony_ci * Handles all pending events and exits. 18848c2ecf20Sopenharmony_ci */ 18858c2ecf20Sopenharmony_civoid cpqhp_pushbutton_thread(struct timer_list *t) 18868c2ecf20Sopenharmony_ci{ 18878c2ecf20Sopenharmony_ci u8 hp_slot; 18888c2ecf20Sopenharmony_ci u8 device; 18898c2ecf20Sopenharmony_ci struct pci_func *func; 18908c2ecf20Sopenharmony_ci struct slot *p_slot = from_timer(p_slot, t, task_event); 18918c2ecf20Sopenharmony_ci struct controller *ctrl = (struct controller *) p_slot->ctrl; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci pushbutton_pending = NULL; 18948c2ecf20Sopenharmony_ci hp_slot = p_slot->hp_slot; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci device = p_slot->device; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci if (is_slot_enabled(ctrl, hp_slot)) { 18998c2ecf20Sopenharmony_ci p_slot->state = POWEROFF_STATE; 19008c2ecf20Sopenharmony_ci /* power Down board */ 19018c2ecf20Sopenharmony_ci func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0); 19028c2ecf20Sopenharmony_ci dbg("In power_down_board, func = %p, ctrl = %p\n", func, ctrl); 19038c2ecf20Sopenharmony_ci if (!func) { 19048c2ecf20Sopenharmony_ci dbg("Error! func NULL in %s\n", __func__); 19058c2ecf20Sopenharmony_ci return; 19068c2ecf20Sopenharmony_ci } 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci if (cpqhp_process_SS(ctrl, func) != 0) { 19098c2ecf20Sopenharmony_ci amber_LED_on(ctrl, hp_slot); 19108c2ecf20Sopenharmony_ci green_LED_on(ctrl, hp_slot); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci set_SOGO(ctrl); 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 19158c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci p_slot->state = STATIC_STATE; 19198c2ecf20Sopenharmony_ci } else { 19208c2ecf20Sopenharmony_ci p_slot->state = POWERON_STATE; 19218c2ecf20Sopenharmony_ci /* slot is off */ 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0); 19248c2ecf20Sopenharmony_ci dbg("In add_board, func = %p, ctrl = %p\n", func, ctrl); 19258c2ecf20Sopenharmony_ci if (!func) { 19268c2ecf20Sopenharmony_ci dbg("Error! func NULL in %s\n", __func__); 19278c2ecf20Sopenharmony_ci return; 19288c2ecf20Sopenharmony_ci } 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci if (ctrl != NULL) { 19318c2ecf20Sopenharmony_ci if (cpqhp_process_SI(ctrl, func) != 0) { 19328c2ecf20Sopenharmony_ci amber_LED_on(ctrl, hp_slot); 19338c2ecf20Sopenharmony_ci green_LED_off(ctrl, hp_slot); 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci set_SOGO(ctrl); 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 19388c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci p_slot->state = STATIC_STATE; 19438c2ecf20Sopenharmony_ci } 19448c2ecf20Sopenharmony_ci} 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ciint cpqhp_process_SI(struct controller *ctrl, struct pci_func *func) 19488c2ecf20Sopenharmony_ci{ 19498c2ecf20Sopenharmony_ci u8 device, hp_slot; 19508c2ecf20Sopenharmony_ci u16 temp_word; 19518c2ecf20Sopenharmony_ci u32 tempdword; 19528c2ecf20Sopenharmony_ci int rc; 19538c2ecf20Sopenharmony_ci struct slot *p_slot; 19548c2ecf20Sopenharmony_ci int physical_slot = 0; 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci tempdword = 0; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci device = func->device; 19598c2ecf20Sopenharmony_ci hp_slot = device - ctrl->slot_device_offset; 19608c2ecf20Sopenharmony_ci p_slot = cpqhp_find_slot(ctrl, device); 19618c2ecf20Sopenharmony_ci if (p_slot) 19628c2ecf20Sopenharmony_ci physical_slot = p_slot->number; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci /* Check to see if the interlock is closed */ 19658c2ecf20Sopenharmony_ci tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci if (tempdword & (0x01 << hp_slot)) 19688c2ecf20Sopenharmony_ci return 1; 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci if (func->is_a_board) { 19718c2ecf20Sopenharmony_ci rc = board_replaced(func, ctrl); 19728c2ecf20Sopenharmony_ci } else { 19738c2ecf20Sopenharmony_ci /* add board */ 19748c2ecf20Sopenharmony_ci slot_remove(func); 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci func = cpqhp_slot_create(ctrl->bus); 19778c2ecf20Sopenharmony_ci if (func == NULL) 19788c2ecf20Sopenharmony_ci return 1; 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci func->bus = ctrl->bus; 19818c2ecf20Sopenharmony_ci func->device = device; 19828c2ecf20Sopenharmony_ci func->function = 0; 19838c2ecf20Sopenharmony_ci func->configured = 0; 19848c2ecf20Sopenharmony_ci func->is_a_board = 1; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci /* We have to save the presence info for these slots */ 19878c2ecf20Sopenharmony_ci temp_word = ctrl->ctrl_int_comp >> 16; 19888c2ecf20Sopenharmony_ci func->presence_save = (temp_word >> hp_slot) & 0x01; 19898c2ecf20Sopenharmony_ci func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { 19928c2ecf20Sopenharmony_ci func->switch_save = 0; 19938c2ecf20Sopenharmony_ci } else { 19948c2ecf20Sopenharmony_ci func->switch_save = 0x10; 19958c2ecf20Sopenharmony_ci } 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci rc = board_added(func, ctrl); 19988c2ecf20Sopenharmony_ci if (rc) { 19998c2ecf20Sopenharmony_ci if (is_bridge(func)) { 20008c2ecf20Sopenharmony_ci bridge_slot_remove(func); 20018c2ecf20Sopenharmony_ci } else 20028c2ecf20Sopenharmony_ci slot_remove(func); 20038c2ecf20Sopenharmony_ci 20048c2ecf20Sopenharmony_ci /* Setup slot structure with entry for empty slot */ 20058c2ecf20Sopenharmony_ci func = cpqhp_slot_create(ctrl->bus); 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci if (func == NULL) 20088c2ecf20Sopenharmony_ci return 1; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci func->bus = ctrl->bus; 20118c2ecf20Sopenharmony_ci func->device = device; 20128c2ecf20Sopenharmony_ci func->function = 0; 20138c2ecf20Sopenharmony_ci func->configured = 0; 20148c2ecf20Sopenharmony_ci func->is_a_board = 0; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci /* We have to save the presence info for these slots */ 20178c2ecf20Sopenharmony_ci temp_word = ctrl->ctrl_int_comp >> 16; 20188c2ecf20Sopenharmony_ci func->presence_save = (temp_word >> hp_slot) & 0x01; 20198c2ecf20Sopenharmony_ci func->presence_save |= 20208c2ecf20Sopenharmony_ci (temp_word >> (hp_slot + 7)) & 0x02; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { 20238c2ecf20Sopenharmony_ci func->switch_save = 0; 20248c2ecf20Sopenharmony_ci } else { 20258c2ecf20Sopenharmony_ci func->switch_save = 0x10; 20268c2ecf20Sopenharmony_ci } 20278c2ecf20Sopenharmony_ci } 20288c2ecf20Sopenharmony_ci } 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci if (rc) 20318c2ecf20Sopenharmony_ci dbg("%s: rc = %d\n", __func__, rc); 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci return rc; 20348c2ecf20Sopenharmony_ci} 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ciint cpqhp_process_SS(struct controller *ctrl, struct pci_func *func) 20388c2ecf20Sopenharmony_ci{ 20398c2ecf20Sopenharmony_ci u8 device, class_code, header_type, BCR; 20408c2ecf20Sopenharmony_ci u8 index = 0; 20418c2ecf20Sopenharmony_ci u8 replace_flag; 20428c2ecf20Sopenharmony_ci u32 rc = 0; 20438c2ecf20Sopenharmony_ci unsigned int devfn; 20448c2ecf20Sopenharmony_ci struct slot *p_slot; 20458c2ecf20Sopenharmony_ci struct pci_bus *pci_bus = ctrl->pci_bus; 20468c2ecf20Sopenharmony_ci int physical_slot = 0; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci device = func->device; 20498c2ecf20Sopenharmony_ci func = cpqhp_slot_find(ctrl->bus, device, index++); 20508c2ecf20Sopenharmony_ci p_slot = cpqhp_find_slot(ctrl, device); 20518c2ecf20Sopenharmony_ci if (p_slot) 20528c2ecf20Sopenharmony_ci physical_slot = p_slot->number; 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci /* Make sure there are no video controllers here */ 20558c2ecf20Sopenharmony_ci while (func && !rc) { 20568c2ecf20Sopenharmony_ci pci_bus->number = func->bus; 20578c2ecf20Sopenharmony_ci devfn = PCI_DEVFN(func->device, func->function); 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci /* Check the Class Code */ 20608c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code); 20618c2ecf20Sopenharmony_ci if (rc) 20628c2ecf20Sopenharmony_ci return rc; 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci if (class_code == PCI_BASE_CLASS_DISPLAY) { 20658c2ecf20Sopenharmony_ci /* Display/Video adapter (not supported) */ 20668c2ecf20Sopenharmony_ci rc = REMOVE_NOT_SUPPORTED; 20678c2ecf20Sopenharmony_ci } else { 20688c2ecf20Sopenharmony_ci /* See if it's a bridge */ 20698c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); 20708c2ecf20Sopenharmony_ci if (rc) 20718c2ecf20Sopenharmony_ci return rc; 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_ci /* If it's a bridge, check the VGA Enable bit */ 20748c2ecf20Sopenharmony_ci if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { 20758c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR); 20768c2ecf20Sopenharmony_ci if (rc) 20778c2ecf20Sopenharmony_ci return rc; 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci /* If the VGA Enable bit is set, remove isn't 20808c2ecf20Sopenharmony_ci * supported */ 20818c2ecf20Sopenharmony_ci if (BCR & PCI_BRIDGE_CTL_VGA) 20828c2ecf20Sopenharmony_ci rc = REMOVE_NOT_SUPPORTED; 20838c2ecf20Sopenharmony_ci } 20848c2ecf20Sopenharmony_ci } 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_ci func = cpqhp_slot_find(ctrl->bus, device, index++); 20878c2ecf20Sopenharmony_ci } 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci func = cpqhp_slot_find(ctrl->bus, device, 0); 20908c2ecf20Sopenharmony_ci if ((func != NULL) && !rc) { 20918c2ecf20Sopenharmony_ci /* FIXME: Replace flag should be passed into process_SS */ 20928c2ecf20Sopenharmony_ci replace_flag = !(ctrl->add_support); 20938c2ecf20Sopenharmony_ci rc = remove_board(func, replace_flag, ctrl); 20948c2ecf20Sopenharmony_ci } else if (!rc) { 20958c2ecf20Sopenharmony_ci rc = 1; 20968c2ecf20Sopenharmony_ci } 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci return rc; 20998c2ecf20Sopenharmony_ci} 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci/** 21028c2ecf20Sopenharmony_ci * switch_leds - switch the leds, go from one site to the other. 21038c2ecf20Sopenharmony_ci * @ctrl: controller to use 21048c2ecf20Sopenharmony_ci * @num_of_slots: number of slots to use 21058c2ecf20Sopenharmony_ci * @work_LED: LED control value 21068c2ecf20Sopenharmony_ci * @direction: 1 to start from the left side, 0 to start right. 21078c2ecf20Sopenharmony_ci */ 21088c2ecf20Sopenharmony_cistatic void switch_leds(struct controller *ctrl, const int num_of_slots, 21098c2ecf20Sopenharmony_ci u32 *work_LED, const int direction) 21108c2ecf20Sopenharmony_ci{ 21118c2ecf20Sopenharmony_ci int loop; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci for (loop = 0; loop < num_of_slots; loop++) { 21148c2ecf20Sopenharmony_ci if (direction) 21158c2ecf20Sopenharmony_ci *work_LED = *work_LED >> 1; 21168c2ecf20Sopenharmony_ci else 21178c2ecf20Sopenharmony_ci *work_LED = *work_LED << 1; 21188c2ecf20Sopenharmony_ci writel(*work_LED, ctrl->hpc_reg + LED_CONTROL); 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci set_SOGO(ctrl); 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci /* Wait for SOGO interrupt */ 21238c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci /* Get ready for next iteration */ 21268c2ecf20Sopenharmony_ci long_delay((2*HZ)/10); 21278c2ecf20Sopenharmony_ci } 21288c2ecf20Sopenharmony_ci} 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci/** 21318c2ecf20Sopenharmony_ci * cpqhp_hardware_test - runs hardware tests 21328c2ecf20Sopenharmony_ci * @ctrl: target controller 21338c2ecf20Sopenharmony_ci * @test_num: the number written to the "test" file in sysfs. 21348c2ecf20Sopenharmony_ci * 21358c2ecf20Sopenharmony_ci * For hot plug ctrl folks to play with. 21368c2ecf20Sopenharmony_ci */ 21378c2ecf20Sopenharmony_ciint cpqhp_hardware_test(struct controller *ctrl, int test_num) 21388c2ecf20Sopenharmony_ci{ 21398c2ecf20Sopenharmony_ci u32 save_LED; 21408c2ecf20Sopenharmony_ci u32 work_LED; 21418c2ecf20Sopenharmony_ci int loop; 21428c2ecf20Sopenharmony_ci int num_of_slots; 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0f; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci switch (test_num) { 21478c2ecf20Sopenharmony_ci case 1: 21488c2ecf20Sopenharmony_ci /* Do stuff here! */ 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci /* Do that funky LED thing */ 21518c2ecf20Sopenharmony_ci /* so we can restore them later */ 21528c2ecf20Sopenharmony_ci save_LED = readl(ctrl->hpc_reg + LED_CONTROL); 21538c2ecf20Sopenharmony_ci work_LED = 0x01010101; 21548c2ecf20Sopenharmony_ci switch_leds(ctrl, num_of_slots, &work_LED, 0); 21558c2ecf20Sopenharmony_ci switch_leds(ctrl, num_of_slots, &work_LED, 1); 21568c2ecf20Sopenharmony_ci switch_leds(ctrl, num_of_slots, &work_LED, 0); 21578c2ecf20Sopenharmony_ci switch_leds(ctrl, num_of_slots, &work_LED, 1); 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci work_LED = 0x01010000; 21608c2ecf20Sopenharmony_ci writel(work_LED, ctrl->hpc_reg + LED_CONTROL); 21618c2ecf20Sopenharmony_ci switch_leds(ctrl, num_of_slots, &work_LED, 0); 21628c2ecf20Sopenharmony_ci switch_leds(ctrl, num_of_slots, &work_LED, 1); 21638c2ecf20Sopenharmony_ci work_LED = 0x00000101; 21648c2ecf20Sopenharmony_ci writel(work_LED, ctrl->hpc_reg + LED_CONTROL); 21658c2ecf20Sopenharmony_ci switch_leds(ctrl, num_of_slots, &work_LED, 0); 21668c2ecf20Sopenharmony_ci switch_leds(ctrl, num_of_slots, &work_LED, 1); 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci work_LED = 0x01010000; 21698c2ecf20Sopenharmony_ci writel(work_LED, ctrl->hpc_reg + LED_CONTROL); 21708c2ecf20Sopenharmony_ci for (loop = 0; loop < num_of_slots; loop++) { 21718c2ecf20Sopenharmony_ci set_SOGO(ctrl); 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci /* Wait for SOGO interrupt */ 21748c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ci /* Get ready for next iteration */ 21778c2ecf20Sopenharmony_ci long_delay((3*HZ)/10); 21788c2ecf20Sopenharmony_ci work_LED = work_LED >> 16; 21798c2ecf20Sopenharmony_ci writel(work_LED, ctrl->hpc_reg + LED_CONTROL); 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci set_SOGO(ctrl); 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci /* Wait for SOGO interrupt */ 21848c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 21858c2ecf20Sopenharmony_ci 21868c2ecf20Sopenharmony_ci /* Get ready for next iteration */ 21878c2ecf20Sopenharmony_ci long_delay((3*HZ)/10); 21888c2ecf20Sopenharmony_ci work_LED = work_LED << 16; 21898c2ecf20Sopenharmony_ci writel(work_LED, ctrl->hpc_reg + LED_CONTROL); 21908c2ecf20Sopenharmony_ci work_LED = work_LED << 1; 21918c2ecf20Sopenharmony_ci writel(work_LED, ctrl->hpc_reg + LED_CONTROL); 21928c2ecf20Sopenharmony_ci } 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci /* put it back the way it was */ 21958c2ecf20Sopenharmony_ci writel(save_LED, ctrl->hpc_reg + LED_CONTROL); 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci set_SOGO(ctrl); 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci /* Wait for SOBS to be unset */ 22008c2ecf20Sopenharmony_ci wait_for_ctrl_irq(ctrl); 22018c2ecf20Sopenharmony_ci break; 22028c2ecf20Sopenharmony_ci case 2: 22038c2ecf20Sopenharmony_ci /* Do other stuff here! */ 22048c2ecf20Sopenharmony_ci break; 22058c2ecf20Sopenharmony_ci case 3: 22068c2ecf20Sopenharmony_ci /* and more... */ 22078c2ecf20Sopenharmony_ci break; 22088c2ecf20Sopenharmony_ci } 22098c2ecf20Sopenharmony_ci return 0; 22108c2ecf20Sopenharmony_ci} 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci/** 22148c2ecf20Sopenharmony_ci * configure_new_device - Configures the PCI header information of one board. 22158c2ecf20Sopenharmony_ci * @ctrl: pointer to controller structure 22168c2ecf20Sopenharmony_ci * @func: pointer to function structure 22178c2ecf20Sopenharmony_ci * @behind_bridge: 1 if this is a recursive call, 0 if not 22188c2ecf20Sopenharmony_ci * @resources: pointer to set of resource lists 22198c2ecf20Sopenharmony_ci * 22208c2ecf20Sopenharmony_ci * Returns 0 if success. 22218c2ecf20Sopenharmony_ci */ 22228c2ecf20Sopenharmony_cistatic u32 configure_new_device(struct controller *ctrl, struct pci_func *func, 22238c2ecf20Sopenharmony_ci u8 behind_bridge, struct resource_lists *resources) 22248c2ecf20Sopenharmony_ci{ 22258c2ecf20Sopenharmony_ci u8 temp_byte, function, max_functions, stop_it; 22268c2ecf20Sopenharmony_ci int rc; 22278c2ecf20Sopenharmony_ci u32 ID; 22288c2ecf20Sopenharmony_ci struct pci_func *new_slot; 22298c2ecf20Sopenharmony_ci int index; 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci new_slot = func; 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci dbg("%s\n", __func__); 22348c2ecf20Sopenharmony_ci /* Check for Multi-function device */ 22358c2ecf20Sopenharmony_ci ctrl->pci_bus->number = func->bus; 22368c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte); 22378c2ecf20Sopenharmony_ci if (rc) { 22388c2ecf20Sopenharmony_ci dbg("%s: rc = %d\n", __func__, rc); 22398c2ecf20Sopenharmony_ci return rc; 22408c2ecf20Sopenharmony_ci } 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci if (temp_byte & 0x80) /* Multi-function device */ 22438c2ecf20Sopenharmony_ci max_functions = 8; 22448c2ecf20Sopenharmony_ci else 22458c2ecf20Sopenharmony_ci max_functions = 1; 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci function = 0; 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci do { 22508c2ecf20Sopenharmony_ci rc = configure_new_function(ctrl, new_slot, behind_bridge, resources); 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci if (rc) { 22538c2ecf20Sopenharmony_ci dbg("configure_new_function failed %d\n", rc); 22548c2ecf20Sopenharmony_ci index = 0; 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci while (new_slot) { 22578c2ecf20Sopenharmony_ci new_slot = cpqhp_slot_find(new_slot->bus, new_slot->device, index++); 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci if (new_slot) 22608c2ecf20Sopenharmony_ci cpqhp_return_board_resources(new_slot, resources); 22618c2ecf20Sopenharmony_ci } 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci return rc; 22648c2ecf20Sopenharmony_ci } 22658c2ecf20Sopenharmony_ci 22668c2ecf20Sopenharmony_ci function++; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci stop_it = 0; 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci /* The following loop skips to the next present function 22718c2ecf20Sopenharmony_ci * and creates a board structure */ 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci while ((function < max_functions) && (!stop_it)) { 22748c2ecf20Sopenharmony_ci pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID); 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci if (ID == 0xFFFFFFFF) { 22778c2ecf20Sopenharmony_ci function++; 22788c2ecf20Sopenharmony_ci } else { 22798c2ecf20Sopenharmony_ci /* Setup slot structure. */ 22808c2ecf20Sopenharmony_ci new_slot = cpqhp_slot_create(func->bus); 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci if (new_slot == NULL) 22838c2ecf20Sopenharmony_ci return 1; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci new_slot->bus = func->bus; 22868c2ecf20Sopenharmony_ci new_slot->device = func->device; 22878c2ecf20Sopenharmony_ci new_slot->function = function; 22888c2ecf20Sopenharmony_ci new_slot->is_a_board = 1; 22898c2ecf20Sopenharmony_ci new_slot->status = 0; 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci stop_it++; 22928c2ecf20Sopenharmony_ci } 22938c2ecf20Sopenharmony_ci } 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci } while (function < max_functions); 22968c2ecf20Sopenharmony_ci dbg("returning from configure_new_device\n"); 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci return 0; 22998c2ecf20Sopenharmony_ci} 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci/* 23038c2ecf20Sopenharmony_ci * Configuration logic that involves the hotplug data structures and 23048c2ecf20Sopenharmony_ci * their bookkeeping 23058c2ecf20Sopenharmony_ci */ 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci/** 23098c2ecf20Sopenharmony_ci * configure_new_function - Configures the PCI header information of one device 23108c2ecf20Sopenharmony_ci * @ctrl: pointer to controller structure 23118c2ecf20Sopenharmony_ci * @func: pointer to function structure 23128c2ecf20Sopenharmony_ci * @behind_bridge: 1 if this is a recursive call, 0 if not 23138c2ecf20Sopenharmony_ci * @resources: pointer to set of resource lists 23148c2ecf20Sopenharmony_ci * 23158c2ecf20Sopenharmony_ci * Calls itself recursively for bridged devices. 23168c2ecf20Sopenharmony_ci * Returns 0 if success. 23178c2ecf20Sopenharmony_ci */ 23188c2ecf20Sopenharmony_cistatic int configure_new_function(struct controller *ctrl, struct pci_func *func, 23198c2ecf20Sopenharmony_ci u8 behind_bridge, 23208c2ecf20Sopenharmony_ci struct resource_lists *resources) 23218c2ecf20Sopenharmony_ci{ 23228c2ecf20Sopenharmony_ci int cloop; 23238c2ecf20Sopenharmony_ci u8 IRQ = 0; 23248c2ecf20Sopenharmony_ci u8 temp_byte; 23258c2ecf20Sopenharmony_ci u8 device; 23268c2ecf20Sopenharmony_ci u8 class_code; 23278c2ecf20Sopenharmony_ci u16 command; 23288c2ecf20Sopenharmony_ci u16 temp_word; 23298c2ecf20Sopenharmony_ci u32 temp_dword; 23308c2ecf20Sopenharmony_ci u32 rc; 23318c2ecf20Sopenharmony_ci u32 temp_register; 23328c2ecf20Sopenharmony_ci u32 base; 23338c2ecf20Sopenharmony_ci u32 ID; 23348c2ecf20Sopenharmony_ci unsigned int devfn; 23358c2ecf20Sopenharmony_ci struct pci_resource *mem_node; 23368c2ecf20Sopenharmony_ci struct pci_resource *p_mem_node; 23378c2ecf20Sopenharmony_ci struct pci_resource *io_node; 23388c2ecf20Sopenharmony_ci struct pci_resource *bus_node; 23398c2ecf20Sopenharmony_ci struct pci_resource *hold_mem_node; 23408c2ecf20Sopenharmony_ci struct pci_resource *hold_p_mem_node; 23418c2ecf20Sopenharmony_ci struct pci_resource *hold_IO_node; 23428c2ecf20Sopenharmony_ci struct pci_resource *hold_bus_node; 23438c2ecf20Sopenharmony_ci struct irq_mapping irqs; 23448c2ecf20Sopenharmony_ci struct pci_func *new_slot; 23458c2ecf20Sopenharmony_ci struct pci_bus *pci_bus; 23468c2ecf20Sopenharmony_ci struct resource_lists temp_resources; 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci pci_bus = ctrl->pci_bus; 23498c2ecf20Sopenharmony_ci pci_bus->number = func->bus; 23508c2ecf20Sopenharmony_ci devfn = PCI_DEVFN(func->device, func->function); 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci /* Check for Bridge */ 23538c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte); 23548c2ecf20Sopenharmony_ci if (rc) 23558c2ecf20Sopenharmony_ci return rc; 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { 23588c2ecf20Sopenharmony_ci /* set Primary bus */ 23598c2ecf20Sopenharmony_ci dbg("set Primary bus = %d\n", func->bus); 23608c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus); 23618c2ecf20Sopenharmony_ci if (rc) 23628c2ecf20Sopenharmony_ci return rc; 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci /* find range of buses to use */ 23658c2ecf20Sopenharmony_ci dbg("find ranges of buses to use\n"); 23668c2ecf20Sopenharmony_ci bus_node = get_max_resource(&(resources->bus_head), 1); 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci /* If we don't have any buses to allocate, we can't continue */ 23698c2ecf20Sopenharmony_ci if (!bus_node) 23708c2ecf20Sopenharmony_ci return -ENOMEM; 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci /* set Secondary bus */ 23738c2ecf20Sopenharmony_ci temp_byte = bus_node->base; 23748c2ecf20Sopenharmony_ci dbg("set Secondary bus = %d\n", bus_node->base); 23758c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte); 23768c2ecf20Sopenharmony_ci if (rc) 23778c2ecf20Sopenharmony_ci return rc; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci /* set subordinate bus */ 23808c2ecf20Sopenharmony_ci temp_byte = bus_node->base + bus_node->length - 1; 23818c2ecf20Sopenharmony_ci dbg("set subordinate bus = %d\n", bus_node->base + bus_node->length - 1); 23828c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); 23838c2ecf20Sopenharmony_ci if (rc) 23848c2ecf20Sopenharmony_ci return rc; 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ci /* set subordinate Latency Timer and base Latency Timer */ 23878c2ecf20Sopenharmony_ci temp_byte = 0x40; 23888c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte); 23898c2ecf20Sopenharmony_ci if (rc) 23908c2ecf20Sopenharmony_ci return rc; 23918c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte); 23928c2ecf20Sopenharmony_ci if (rc) 23938c2ecf20Sopenharmony_ci return rc; 23948c2ecf20Sopenharmony_ci 23958c2ecf20Sopenharmony_ci /* set Cache Line size */ 23968c2ecf20Sopenharmony_ci temp_byte = 0x08; 23978c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte); 23988c2ecf20Sopenharmony_ci if (rc) 23998c2ecf20Sopenharmony_ci return rc; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci /* Setup the IO, memory, and prefetchable windows */ 24028c2ecf20Sopenharmony_ci io_node = get_max_resource(&(resources->io_head), 0x1000); 24038c2ecf20Sopenharmony_ci if (!io_node) 24048c2ecf20Sopenharmony_ci return -ENOMEM; 24058c2ecf20Sopenharmony_ci mem_node = get_max_resource(&(resources->mem_head), 0x100000); 24068c2ecf20Sopenharmony_ci if (!mem_node) 24078c2ecf20Sopenharmony_ci return -ENOMEM; 24088c2ecf20Sopenharmony_ci p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000); 24098c2ecf20Sopenharmony_ci if (!p_mem_node) 24108c2ecf20Sopenharmony_ci return -ENOMEM; 24118c2ecf20Sopenharmony_ci dbg("Setup the IO, memory, and prefetchable windows\n"); 24128c2ecf20Sopenharmony_ci dbg("io_node\n"); 24138c2ecf20Sopenharmony_ci dbg("(base, len, next) (%x, %x, %p)\n", io_node->base, 24148c2ecf20Sopenharmony_ci io_node->length, io_node->next); 24158c2ecf20Sopenharmony_ci dbg("mem_node\n"); 24168c2ecf20Sopenharmony_ci dbg("(base, len, next) (%x, %x, %p)\n", mem_node->base, 24178c2ecf20Sopenharmony_ci mem_node->length, mem_node->next); 24188c2ecf20Sopenharmony_ci dbg("p_mem_node\n"); 24198c2ecf20Sopenharmony_ci dbg("(base, len, next) (%x, %x, %p)\n", p_mem_node->base, 24208c2ecf20Sopenharmony_ci p_mem_node->length, p_mem_node->next); 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci /* set up the IRQ info */ 24238c2ecf20Sopenharmony_ci if (!resources->irqs) { 24248c2ecf20Sopenharmony_ci irqs.barber_pole = 0; 24258c2ecf20Sopenharmony_ci irqs.interrupt[0] = 0; 24268c2ecf20Sopenharmony_ci irqs.interrupt[1] = 0; 24278c2ecf20Sopenharmony_ci irqs.interrupt[2] = 0; 24288c2ecf20Sopenharmony_ci irqs.interrupt[3] = 0; 24298c2ecf20Sopenharmony_ci irqs.valid_INT = 0; 24308c2ecf20Sopenharmony_ci } else { 24318c2ecf20Sopenharmony_ci irqs.barber_pole = resources->irqs->barber_pole; 24328c2ecf20Sopenharmony_ci irqs.interrupt[0] = resources->irqs->interrupt[0]; 24338c2ecf20Sopenharmony_ci irqs.interrupt[1] = resources->irqs->interrupt[1]; 24348c2ecf20Sopenharmony_ci irqs.interrupt[2] = resources->irqs->interrupt[2]; 24358c2ecf20Sopenharmony_ci irqs.interrupt[3] = resources->irqs->interrupt[3]; 24368c2ecf20Sopenharmony_ci irqs.valid_INT = resources->irqs->valid_INT; 24378c2ecf20Sopenharmony_ci } 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci /* set up resource lists that are now aligned on top and bottom 24408c2ecf20Sopenharmony_ci * for anything behind the bridge. */ 24418c2ecf20Sopenharmony_ci temp_resources.bus_head = bus_node; 24428c2ecf20Sopenharmony_ci temp_resources.io_head = io_node; 24438c2ecf20Sopenharmony_ci temp_resources.mem_head = mem_node; 24448c2ecf20Sopenharmony_ci temp_resources.p_mem_head = p_mem_node; 24458c2ecf20Sopenharmony_ci temp_resources.irqs = &irqs; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci /* Make copies of the nodes we are going to pass down so that 24488c2ecf20Sopenharmony_ci * if there is a problem,we can just use these to free resources 24498c2ecf20Sopenharmony_ci */ 24508c2ecf20Sopenharmony_ci hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL); 24518c2ecf20Sopenharmony_ci hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL); 24528c2ecf20Sopenharmony_ci hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL); 24538c2ecf20Sopenharmony_ci hold_p_mem_node = kmalloc(sizeof(*hold_p_mem_node), GFP_KERNEL); 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) { 24568c2ecf20Sopenharmony_ci kfree(hold_bus_node); 24578c2ecf20Sopenharmony_ci kfree(hold_IO_node); 24588c2ecf20Sopenharmony_ci kfree(hold_mem_node); 24598c2ecf20Sopenharmony_ci kfree(hold_p_mem_node); 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci return 1; 24628c2ecf20Sopenharmony_ci } 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource)); 24658c2ecf20Sopenharmony_ci 24668c2ecf20Sopenharmony_ci bus_node->base += 1; 24678c2ecf20Sopenharmony_ci bus_node->length -= 1; 24688c2ecf20Sopenharmony_ci bus_node->next = NULL; 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci /* If we have IO resources copy them and fill in the bridge's 24718c2ecf20Sopenharmony_ci * IO range registers */ 24728c2ecf20Sopenharmony_ci memcpy(hold_IO_node, io_node, sizeof(struct pci_resource)); 24738c2ecf20Sopenharmony_ci io_node->next = NULL; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci /* set IO base and Limit registers */ 24768c2ecf20Sopenharmony_ci temp_byte = io_node->base >> 8; 24778c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte); 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci temp_byte = (io_node->base + io_node->length - 1) >> 8; 24808c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_ci /* Copy the memory resources and fill in the bridge's memory 24838c2ecf20Sopenharmony_ci * range registers. 24848c2ecf20Sopenharmony_ci */ 24858c2ecf20Sopenharmony_ci memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource)); 24868c2ecf20Sopenharmony_ci mem_node->next = NULL; 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci /* set Mem base and Limit registers */ 24898c2ecf20Sopenharmony_ci temp_word = mem_node->base >> 16; 24908c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci temp_word = (mem_node->base + mem_node->length - 1) >> 16; 24938c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource)); 24968c2ecf20Sopenharmony_ci p_mem_node->next = NULL; 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci /* set Pre Mem base and Limit registers */ 24998c2ecf20Sopenharmony_ci temp_word = p_mem_node->base >> 16; 25008c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16; 25038c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci /* Adjust this to compensate for extra adjustment in first loop 25068c2ecf20Sopenharmony_ci */ 25078c2ecf20Sopenharmony_ci irqs.barber_pole--; 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_ci rc = 0; 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci /* Here we actually find the devices and configure them */ 25128c2ecf20Sopenharmony_ci for (device = 0; (device <= 0x1F) && !rc; device++) { 25138c2ecf20Sopenharmony_ci irqs.barber_pole = (irqs.barber_pole + 1) & 0x03; 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci ID = 0xFFFFFFFF; 25168c2ecf20Sopenharmony_ci pci_bus->number = hold_bus_node->base; 25178c2ecf20Sopenharmony_ci pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), 0x00, &ID); 25188c2ecf20Sopenharmony_ci pci_bus->number = func->bus; 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci if (ID != 0xFFFFFFFF) { /* device present */ 25218c2ecf20Sopenharmony_ci /* Setup slot structure. */ 25228c2ecf20Sopenharmony_ci new_slot = cpqhp_slot_create(hold_bus_node->base); 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci if (new_slot == NULL) { 25258c2ecf20Sopenharmony_ci rc = -ENOMEM; 25268c2ecf20Sopenharmony_ci continue; 25278c2ecf20Sopenharmony_ci } 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci new_slot->bus = hold_bus_node->base; 25308c2ecf20Sopenharmony_ci new_slot->device = device; 25318c2ecf20Sopenharmony_ci new_slot->function = 0; 25328c2ecf20Sopenharmony_ci new_slot->is_a_board = 1; 25338c2ecf20Sopenharmony_ci new_slot->status = 0; 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_ci rc = configure_new_device(ctrl, new_slot, 1, &temp_resources); 25368c2ecf20Sopenharmony_ci dbg("configure_new_device rc=0x%x\n", rc); 25378c2ecf20Sopenharmony_ci } /* End of IF (device in slot?) */ 25388c2ecf20Sopenharmony_ci } /* End of FOR loop */ 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci if (rc) 25418c2ecf20Sopenharmony_ci goto free_and_out; 25428c2ecf20Sopenharmony_ci /* save the interrupt routing information */ 25438c2ecf20Sopenharmony_ci if (resources->irqs) { 25448c2ecf20Sopenharmony_ci resources->irqs->interrupt[0] = irqs.interrupt[0]; 25458c2ecf20Sopenharmony_ci resources->irqs->interrupt[1] = irqs.interrupt[1]; 25468c2ecf20Sopenharmony_ci resources->irqs->interrupt[2] = irqs.interrupt[2]; 25478c2ecf20Sopenharmony_ci resources->irqs->interrupt[3] = irqs.interrupt[3]; 25488c2ecf20Sopenharmony_ci resources->irqs->valid_INT = irqs.valid_INT; 25498c2ecf20Sopenharmony_ci } else if (!behind_bridge) { 25508c2ecf20Sopenharmony_ci /* We need to hook up the interrupts here */ 25518c2ecf20Sopenharmony_ci for (cloop = 0; cloop < 4; cloop++) { 25528c2ecf20Sopenharmony_ci if (irqs.valid_INT & (0x01 << cloop)) { 25538c2ecf20Sopenharmony_ci rc = cpqhp_set_irq(func->bus, func->device, 25548c2ecf20Sopenharmony_ci cloop + 1, irqs.interrupt[cloop]); 25558c2ecf20Sopenharmony_ci if (rc) 25568c2ecf20Sopenharmony_ci goto free_and_out; 25578c2ecf20Sopenharmony_ci } 25588c2ecf20Sopenharmony_ci } /* end of for loop */ 25598c2ecf20Sopenharmony_ci } 25608c2ecf20Sopenharmony_ci /* Return unused bus resources 25618c2ecf20Sopenharmony_ci * First use the temporary node to store information for 25628c2ecf20Sopenharmony_ci * the board */ 25638c2ecf20Sopenharmony_ci if (bus_node && temp_resources.bus_head) { 25648c2ecf20Sopenharmony_ci hold_bus_node->length = bus_node->base - hold_bus_node->base; 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci hold_bus_node->next = func->bus_head; 25678c2ecf20Sopenharmony_ci func->bus_head = hold_bus_node; 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci temp_byte = temp_resources.bus_head->base - 1; 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_ci /* set subordinate bus */ 25728c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte); 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_ci if (temp_resources.bus_head->length == 0) { 25758c2ecf20Sopenharmony_ci kfree(temp_resources.bus_head); 25768c2ecf20Sopenharmony_ci temp_resources.bus_head = NULL; 25778c2ecf20Sopenharmony_ci } else { 25788c2ecf20Sopenharmony_ci return_resource(&(resources->bus_head), temp_resources.bus_head); 25798c2ecf20Sopenharmony_ci } 25808c2ecf20Sopenharmony_ci } 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci /* If we have IO space available and there is some left, 25838c2ecf20Sopenharmony_ci * return the unused portion */ 25848c2ecf20Sopenharmony_ci if (hold_IO_node && temp_resources.io_head) { 25858c2ecf20Sopenharmony_ci io_node = do_pre_bridge_resource_split(&(temp_resources.io_head), 25868c2ecf20Sopenharmony_ci &hold_IO_node, 0x1000); 25878c2ecf20Sopenharmony_ci 25888c2ecf20Sopenharmony_ci /* Check if we were able to split something off */ 25898c2ecf20Sopenharmony_ci if (io_node) { 25908c2ecf20Sopenharmony_ci hold_IO_node->base = io_node->base + io_node->length; 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci temp_byte = (hold_IO_node->base) >> 8; 25938c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_IO_BASE, temp_byte); 25948c2ecf20Sopenharmony_ci 25958c2ecf20Sopenharmony_ci return_resource(&(resources->io_head), io_node); 25968c2ecf20Sopenharmony_ci } 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000); 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci /* Check if we were able to split something off */ 26018c2ecf20Sopenharmony_ci if (io_node) { 26028c2ecf20Sopenharmony_ci /* First use the temporary node to store 26038c2ecf20Sopenharmony_ci * information for the board */ 26048c2ecf20Sopenharmony_ci hold_IO_node->length = io_node->base - hold_IO_node->base; 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci /* If we used any, add it to the board's list */ 26078c2ecf20Sopenharmony_ci if (hold_IO_node->length) { 26088c2ecf20Sopenharmony_ci hold_IO_node->next = func->io_head; 26098c2ecf20Sopenharmony_ci func->io_head = hold_IO_node; 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci temp_byte = (io_node->base - 1) >> 8; 26128c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte); 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci return_resource(&(resources->io_head), io_node); 26158c2ecf20Sopenharmony_ci } else { 26168c2ecf20Sopenharmony_ci /* it doesn't need any IO */ 26178c2ecf20Sopenharmony_ci temp_word = 0x0000; 26188c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_IO_LIMIT, temp_word); 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci return_resource(&(resources->io_head), io_node); 26218c2ecf20Sopenharmony_ci kfree(hold_IO_node); 26228c2ecf20Sopenharmony_ci } 26238c2ecf20Sopenharmony_ci } else { 26248c2ecf20Sopenharmony_ci /* it used most of the range */ 26258c2ecf20Sopenharmony_ci hold_IO_node->next = func->io_head; 26268c2ecf20Sopenharmony_ci func->io_head = hold_IO_node; 26278c2ecf20Sopenharmony_ci } 26288c2ecf20Sopenharmony_ci } else if (hold_IO_node) { 26298c2ecf20Sopenharmony_ci /* it used the whole range */ 26308c2ecf20Sopenharmony_ci hold_IO_node->next = func->io_head; 26318c2ecf20Sopenharmony_ci func->io_head = hold_IO_node; 26328c2ecf20Sopenharmony_ci } 26338c2ecf20Sopenharmony_ci /* If we have memory space available and there is some left, 26348c2ecf20Sopenharmony_ci * return the unused portion */ 26358c2ecf20Sopenharmony_ci if (hold_mem_node && temp_resources.mem_head) { 26368c2ecf20Sopenharmony_ci mem_node = do_pre_bridge_resource_split(&(temp_resources. mem_head), 26378c2ecf20Sopenharmony_ci &hold_mem_node, 0x100000); 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci /* Check if we were able to split something off */ 26408c2ecf20Sopenharmony_ci if (mem_node) { 26418c2ecf20Sopenharmony_ci hold_mem_node->base = mem_node->base + mem_node->length; 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci temp_word = (hold_mem_node->base) >> 16; 26448c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word); 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci return_resource(&(resources->mem_head), mem_node); 26478c2ecf20Sopenharmony_ci } 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_ci mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000); 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci /* Check if we were able to split something off */ 26528c2ecf20Sopenharmony_ci if (mem_node) { 26538c2ecf20Sopenharmony_ci /* First use the temporary node to store 26548c2ecf20Sopenharmony_ci * information for the board */ 26558c2ecf20Sopenharmony_ci hold_mem_node->length = mem_node->base - hold_mem_node->base; 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_ci if (hold_mem_node->length) { 26588c2ecf20Sopenharmony_ci hold_mem_node->next = func->mem_head; 26598c2ecf20Sopenharmony_ci func->mem_head = hold_mem_node; 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci /* configure end address */ 26628c2ecf20Sopenharmony_ci temp_word = (mem_node->base - 1) >> 16; 26638c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci /* Return unused resources to the pool */ 26668c2ecf20Sopenharmony_ci return_resource(&(resources->mem_head), mem_node); 26678c2ecf20Sopenharmony_ci } else { 26688c2ecf20Sopenharmony_ci /* it doesn't need any Mem */ 26698c2ecf20Sopenharmony_ci temp_word = 0x0000; 26708c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word); 26718c2ecf20Sopenharmony_ci 26728c2ecf20Sopenharmony_ci return_resource(&(resources->mem_head), mem_node); 26738c2ecf20Sopenharmony_ci kfree(hold_mem_node); 26748c2ecf20Sopenharmony_ci } 26758c2ecf20Sopenharmony_ci } else { 26768c2ecf20Sopenharmony_ci /* it used most of the range */ 26778c2ecf20Sopenharmony_ci hold_mem_node->next = func->mem_head; 26788c2ecf20Sopenharmony_ci func->mem_head = hold_mem_node; 26798c2ecf20Sopenharmony_ci } 26808c2ecf20Sopenharmony_ci } else if (hold_mem_node) { 26818c2ecf20Sopenharmony_ci /* it used the whole range */ 26828c2ecf20Sopenharmony_ci hold_mem_node->next = func->mem_head; 26838c2ecf20Sopenharmony_ci func->mem_head = hold_mem_node; 26848c2ecf20Sopenharmony_ci } 26858c2ecf20Sopenharmony_ci /* If we have prefetchable memory space available and there 26868c2ecf20Sopenharmony_ci * is some left at the end, return the unused portion */ 26878c2ecf20Sopenharmony_ci if (temp_resources.p_mem_head) { 26888c2ecf20Sopenharmony_ci p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head), 26898c2ecf20Sopenharmony_ci &hold_p_mem_node, 0x100000); 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_ci /* Check if we were able to split something off */ 26928c2ecf20Sopenharmony_ci if (p_mem_node) { 26938c2ecf20Sopenharmony_ci hold_p_mem_node->base = p_mem_node->base + p_mem_node->length; 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci temp_word = (hold_p_mem_node->base) >> 16; 26968c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word); 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci return_resource(&(resources->p_mem_head), p_mem_node); 26998c2ecf20Sopenharmony_ci } 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_ci p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000); 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_ci /* Check if we were able to split something off */ 27048c2ecf20Sopenharmony_ci if (p_mem_node) { 27058c2ecf20Sopenharmony_ci /* First use the temporary node to store 27068c2ecf20Sopenharmony_ci * information for the board */ 27078c2ecf20Sopenharmony_ci hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base; 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci /* If we used any, add it to the board's list */ 27108c2ecf20Sopenharmony_ci if (hold_p_mem_node->length) { 27118c2ecf20Sopenharmony_ci hold_p_mem_node->next = func->p_mem_head; 27128c2ecf20Sopenharmony_ci func->p_mem_head = hold_p_mem_node; 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci temp_word = (p_mem_node->base - 1) >> 16; 27158c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); 27168c2ecf20Sopenharmony_ci 27178c2ecf20Sopenharmony_ci return_resource(&(resources->p_mem_head), p_mem_node); 27188c2ecf20Sopenharmony_ci } else { 27198c2ecf20Sopenharmony_ci /* it doesn't need any PMem */ 27208c2ecf20Sopenharmony_ci temp_word = 0x0000; 27218c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci return_resource(&(resources->p_mem_head), p_mem_node); 27248c2ecf20Sopenharmony_ci kfree(hold_p_mem_node); 27258c2ecf20Sopenharmony_ci } 27268c2ecf20Sopenharmony_ci } else { 27278c2ecf20Sopenharmony_ci /* it used the most of the range */ 27288c2ecf20Sopenharmony_ci hold_p_mem_node->next = func->p_mem_head; 27298c2ecf20Sopenharmony_ci func->p_mem_head = hold_p_mem_node; 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci } else if (hold_p_mem_node) { 27328c2ecf20Sopenharmony_ci /* it used the whole range */ 27338c2ecf20Sopenharmony_ci hold_p_mem_node->next = func->p_mem_head; 27348c2ecf20Sopenharmony_ci func->p_mem_head = hold_p_mem_node; 27358c2ecf20Sopenharmony_ci } 27368c2ecf20Sopenharmony_ci /* We should be configuring an IRQ and the bridge's base address 27378c2ecf20Sopenharmony_ci * registers if it needs them. Although we have never seen such 27388c2ecf20Sopenharmony_ci * a device */ 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci /* enable card */ 27418c2ecf20Sopenharmony_ci command = 0x0157; /* = PCI_COMMAND_IO | 27428c2ecf20Sopenharmony_ci * PCI_COMMAND_MEMORY | 27438c2ecf20Sopenharmony_ci * PCI_COMMAND_MASTER | 27448c2ecf20Sopenharmony_ci * PCI_COMMAND_INVALIDATE | 27458c2ecf20Sopenharmony_ci * PCI_COMMAND_PARITY | 27468c2ecf20Sopenharmony_ci * PCI_COMMAND_SERR */ 27478c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_ci /* set Bridge Control Register */ 27508c2ecf20Sopenharmony_ci command = 0x07; /* = PCI_BRIDGE_CTL_PARITY | 27518c2ecf20Sopenharmony_ci * PCI_BRIDGE_CTL_SERR | 27528c2ecf20Sopenharmony_ci * PCI_BRIDGE_CTL_NO_ISA */ 27538c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); 27548c2ecf20Sopenharmony_ci } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) { 27558c2ecf20Sopenharmony_ci /* Standard device */ 27568c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code); 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci if (class_code == PCI_BASE_CLASS_DISPLAY) { 27598c2ecf20Sopenharmony_ci /* Display (video) adapter (not supported) */ 27608c2ecf20Sopenharmony_ci return DEVICE_TYPE_NOT_SUPPORTED; 27618c2ecf20Sopenharmony_ci } 27628c2ecf20Sopenharmony_ci /* Figure out IO and memory needs */ 27638c2ecf20Sopenharmony_ci for (cloop = 0x10; cloop <= 0x24; cloop += 4) { 27648c2ecf20Sopenharmony_ci temp_register = 0xFFFFFFFF; 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_ci dbg("CND: bus=%d, devfn=%d, offset=%d\n", pci_bus->number, devfn, cloop); 27678c2ecf20Sopenharmony_ci rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register); 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register); 27708c2ecf20Sopenharmony_ci dbg("CND: base = 0x%x\n", temp_register); 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci if (temp_register) { /* If this register is implemented */ 27738c2ecf20Sopenharmony_ci if ((temp_register & 0x03L) == 0x01) { 27748c2ecf20Sopenharmony_ci /* Map IO */ 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci /* set base = amount of IO space */ 27778c2ecf20Sopenharmony_ci base = temp_register & 0xFFFFFFFC; 27788c2ecf20Sopenharmony_ci base = ~base + 1; 27798c2ecf20Sopenharmony_ci 27808c2ecf20Sopenharmony_ci dbg("CND: length = 0x%x\n", base); 27818c2ecf20Sopenharmony_ci io_node = get_io_resource(&(resources->io_head), base); 27828c2ecf20Sopenharmony_ci if (!io_node) 27838c2ecf20Sopenharmony_ci return -ENOMEM; 27848c2ecf20Sopenharmony_ci dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n", 27858c2ecf20Sopenharmony_ci io_node->base, io_node->length, io_node->next); 27868c2ecf20Sopenharmony_ci dbg("func (%p) io_head (%p)\n", func, func->io_head); 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci /* allocate the resource to the board */ 27898c2ecf20Sopenharmony_ci base = io_node->base; 27908c2ecf20Sopenharmony_ci io_node->next = func->io_head; 27918c2ecf20Sopenharmony_ci func->io_head = io_node; 27928c2ecf20Sopenharmony_ci } else if ((temp_register & 0x0BL) == 0x08) { 27938c2ecf20Sopenharmony_ci /* Map prefetchable memory */ 27948c2ecf20Sopenharmony_ci base = temp_register & 0xFFFFFFF0; 27958c2ecf20Sopenharmony_ci base = ~base + 1; 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci dbg("CND: length = 0x%x\n", base); 27988c2ecf20Sopenharmony_ci p_mem_node = get_resource(&(resources->p_mem_head), base); 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci /* allocate the resource to the board */ 28018c2ecf20Sopenharmony_ci if (p_mem_node) { 28028c2ecf20Sopenharmony_ci base = p_mem_node->base; 28038c2ecf20Sopenharmony_ci 28048c2ecf20Sopenharmony_ci p_mem_node->next = func->p_mem_head; 28058c2ecf20Sopenharmony_ci func->p_mem_head = p_mem_node; 28068c2ecf20Sopenharmony_ci } else 28078c2ecf20Sopenharmony_ci return -ENOMEM; 28088c2ecf20Sopenharmony_ci } else if ((temp_register & 0x0BL) == 0x00) { 28098c2ecf20Sopenharmony_ci /* Map memory */ 28108c2ecf20Sopenharmony_ci base = temp_register & 0xFFFFFFF0; 28118c2ecf20Sopenharmony_ci base = ~base + 1; 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci dbg("CND: length = 0x%x\n", base); 28148c2ecf20Sopenharmony_ci mem_node = get_resource(&(resources->mem_head), base); 28158c2ecf20Sopenharmony_ci 28168c2ecf20Sopenharmony_ci /* allocate the resource to the board */ 28178c2ecf20Sopenharmony_ci if (mem_node) { 28188c2ecf20Sopenharmony_ci base = mem_node->base; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci mem_node->next = func->mem_head; 28218c2ecf20Sopenharmony_ci func->mem_head = mem_node; 28228c2ecf20Sopenharmony_ci } else 28238c2ecf20Sopenharmony_ci return -ENOMEM; 28248c2ecf20Sopenharmony_ci } else { 28258c2ecf20Sopenharmony_ci /* Reserved bits or requesting space below 1M */ 28268c2ecf20Sopenharmony_ci return NOT_ENOUGH_RESOURCES; 28278c2ecf20Sopenharmony_ci } 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base); 28308c2ecf20Sopenharmony_ci 28318c2ecf20Sopenharmony_ci /* Check for 64-bit base */ 28328c2ecf20Sopenharmony_ci if ((temp_register & 0x07L) == 0x04) { 28338c2ecf20Sopenharmony_ci cloop += 4; 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci /* Upper 32 bits of address always zero 28368c2ecf20Sopenharmony_ci * on today's systems */ 28378c2ecf20Sopenharmony_ci /* FIXME this is probably not true on 28388c2ecf20Sopenharmony_ci * Alpha and ia64??? */ 28398c2ecf20Sopenharmony_ci base = 0; 28408c2ecf20Sopenharmony_ci rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base); 28418c2ecf20Sopenharmony_ci } 28428c2ecf20Sopenharmony_ci } 28438c2ecf20Sopenharmony_ci } /* End of base register loop */ 28448c2ecf20Sopenharmony_ci if (cpqhp_legacy_mode) { 28458c2ecf20Sopenharmony_ci /* Figure out which interrupt pin this function uses */ 28468c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(pci_bus, devfn, 28478c2ecf20Sopenharmony_ci PCI_INTERRUPT_PIN, &temp_byte); 28488c2ecf20Sopenharmony_ci 28498c2ecf20Sopenharmony_ci /* If this function needs an interrupt and we are behind 28508c2ecf20Sopenharmony_ci * a bridge and the pin is tied to something that's 28518c2ecf20Sopenharmony_ci * already mapped, set this one the same */ 28528c2ecf20Sopenharmony_ci if (temp_byte && resources->irqs && 28538c2ecf20Sopenharmony_ci (resources->irqs->valid_INT & 28548c2ecf20Sopenharmony_ci (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) { 28558c2ecf20Sopenharmony_ci /* We have to share with something already set up */ 28568c2ecf20Sopenharmony_ci IRQ = resources->irqs->interrupt[(temp_byte + 28578c2ecf20Sopenharmony_ci resources->irqs->barber_pole - 1) & 0x03]; 28588c2ecf20Sopenharmony_ci } else { 28598c2ecf20Sopenharmony_ci /* Program IRQ based on card type */ 28608c2ecf20Sopenharmony_ci rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code); 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci if (class_code == PCI_BASE_CLASS_STORAGE) 28638c2ecf20Sopenharmony_ci IRQ = cpqhp_disk_irq; 28648c2ecf20Sopenharmony_ci else 28658c2ecf20Sopenharmony_ci IRQ = cpqhp_nic_irq; 28668c2ecf20Sopenharmony_ci } 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci /* IRQ Line */ 28698c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ); 28708c2ecf20Sopenharmony_ci } 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ci if (!behind_bridge) { 28738c2ecf20Sopenharmony_ci rc = cpqhp_set_irq(func->bus, func->device, temp_byte, IRQ); 28748c2ecf20Sopenharmony_ci if (rc) 28758c2ecf20Sopenharmony_ci return 1; 28768c2ecf20Sopenharmony_ci } else { 28778c2ecf20Sopenharmony_ci /* TBD - this code may also belong in the other clause 28788c2ecf20Sopenharmony_ci * of this If statement */ 28798c2ecf20Sopenharmony_ci resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ; 28808c2ecf20Sopenharmony_ci resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03; 28818c2ecf20Sopenharmony_ci } 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci /* Latency Timer */ 28848c2ecf20Sopenharmony_ci temp_byte = 0x40; 28858c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, 28868c2ecf20Sopenharmony_ci PCI_LATENCY_TIMER, temp_byte); 28878c2ecf20Sopenharmony_ci 28888c2ecf20Sopenharmony_ci /* Cache Line size */ 28898c2ecf20Sopenharmony_ci temp_byte = 0x08; 28908c2ecf20Sopenharmony_ci rc = pci_bus_write_config_byte(pci_bus, devfn, 28918c2ecf20Sopenharmony_ci PCI_CACHE_LINE_SIZE, temp_byte); 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci /* disable ROM base Address */ 28948c2ecf20Sopenharmony_ci temp_dword = 0x00L; 28958c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, 28968c2ecf20Sopenharmony_ci PCI_ROM_ADDRESS, temp_dword); 28978c2ecf20Sopenharmony_ci 28988c2ecf20Sopenharmony_ci /* enable card */ 28998c2ecf20Sopenharmony_ci temp_word = 0x0157; /* = PCI_COMMAND_IO | 29008c2ecf20Sopenharmony_ci * PCI_COMMAND_MEMORY | 29018c2ecf20Sopenharmony_ci * PCI_COMMAND_MASTER | 29028c2ecf20Sopenharmony_ci * PCI_COMMAND_INVALIDATE | 29038c2ecf20Sopenharmony_ci * PCI_COMMAND_PARITY | 29048c2ecf20Sopenharmony_ci * PCI_COMMAND_SERR */ 29058c2ecf20Sopenharmony_ci rc = pci_bus_write_config_word(pci_bus, devfn, 29068c2ecf20Sopenharmony_ci PCI_COMMAND, temp_word); 29078c2ecf20Sopenharmony_ci } else { /* End of Not-A-Bridge else */ 29088c2ecf20Sopenharmony_ci /* It's some strange type of PCI adapter (Cardbus?) */ 29098c2ecf20Sopenharmony_ci return DEVICE_TYPE_NOT_SUPPORTED; 29108c2ecf20Sopenharmony_ci } 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_ci func->configured = 1; 29138c2ecf20Sopenharmony_ci 29148c2ecf20Sopenharmony_ci return 0; 29158c2ecf20Sopenharmony_cifree_and_out: 29168c2ecf20Sopenharmony_ci cpqhp_destroy_resource_list(&temp_resources); 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci return_resource(&(resources->bus_head), hold_bus_node); 29198c2ecf20Sopenharmony_ci return_resource(&(resources->io_head), hold_IO_node); 29208c2ecf20Sopenharmony_ci return_resource(&(resources->mem_head), hold_mem_node); 29218c2ecf20Sopenharmony_ci return_resource(&(resources->p_mem_head), hold_p_mem_node); 29228c2ecf20Sopenharmony_ci return rc; 29238c2ecf20Sopenharmony_ci} 2924