1736759efSBjorn Helgaas // SPDX-License-Identifier: GPL-2.0+
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Compaq Hot Plug Controller Driver
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 1995,2001 Compaq Computer Corporation
61da177e4SLinus Torvalds * Copyright (C) 2001 Greg Kroah-Hartman ([email protected])
71da177e4SLinus Torvalds * Copyright (C) 2001 IBM Corp.
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * All rights reserved.
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds * Send feedback to <[email protected]>
121da177e4SLinus Torvalds *
131da177e4SLinus Torvalds */
141da177e4SLinus Torvalds
151da177e4SLinus Torvalds #include <linux/module.h>
161da177e4SLinus Torvalds #include <linux/kernel.h>
171da177e4SLinus Torvalds #include <linux/types.h>
181da177e4SLinus Torvalds #include <linux/slab.h>
191da177e4SLinus Torvalds #include <linux/workqueue.h>
201da177e4SLinus Torvalds #include <linux/interrupt.h>
211da177e4SLinus Torvalds #include <linux/delay.h>
221da177e4SLinus Torvalds #include <linux/wait.h>
231da177e4SLinus Torvalds #include <linux/pci.h>
247a54f25cSGreg Kroah-Hartman #include <linux/pci_hotplug.h>
25fa007d8bSChristoph Hellwig #include <linux/kthread.h>
261da177e4SLinus Torvalds #include "cpqphp.h"
271da177e4SLinus Torvalds
281da177e4SLinus Torvalds static u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
291da177e4SLinus Torvalds u8 behind_bridge, struct resource_lists *resources);
301da177e4SLinus Torvalds static int configure_new_function(struct controller *ctrl, struct pci_func *func,
311da177e4SLinus Torvalds u8 behind_bridge, struct resource_lists *resources);
321da177e4SLinus Torvalds static void interrupt_event_handler(struct controller *ctrl);
331da177e4SLinus Torvalds
341da177e4SLinus Torvalds
35fa007d8bSChristoph Hellwig static struct task_struct *cpqhp_event_thread;
3634d773f6SKees Cook static struct timer_list *pushbutton_pending; /* = NULL */
371da177e4SLinus Torvalds
381da177e4SLinus Torvalds /* delay is in jiffies to wait for */
long_delay(int delay)391da177e4SLinus Torvalds static void long_delay(int delay)
401da177e4SLinus Torvalds {
41fa007d8bSChristoph Hellwig /*
42fa007d8bSChristoph Hellwig * XXX(hch): if someone is bored please convert all callers
43fa007d8bSChristoph Hellwig * to call msleep_interruptible directly. They really want
44fa007d8bSChristoph Hellwig * to specify timeouts in natural units and spend a lot of
45fa007d8bSChristoph Hellwig * effort converting them to jiffies..
461da177e4SLinus Torvalds */
471da177e4SLinus Torvalds msleep_interruptible(jiffies_to_msecs(delay));
481da177e4SLinus Torvalds }
491da177e4SLinus Torvalds
501da177e4SLinus Torvalds
511da177e4SLinus Torvalds /* FIXME: The following line needs to be somewhere else... */
521da177e4SLinus Torvalds #define WRONG_BUS_FREQUENCY 0x07
handle_switch_change(u8 change,struct controller * ctrl)531da177e4SLinus Torvalds static u8 handle_switch_change(u8 change, struct controller *ctrl)
541da177e4SLinus Torvalds {
551da177e4SLinus Torvalds int hp_slot;
561da177e4SLinus Torvalds u8 rc = 0;
571da177e4SLinus Torvalds u16 temp_word;
581da177e4SLinus Torvalds struct pci_func *func;
591da177e4SLinus Torvalds struct event_info *taskInfo;
601da177e4SLinus Torvalds
611da177e4SLinus Torvalds if (!change)
621da177e4SLinus Torvalds return 0;
631da177e4SLinus Torvalds
641da177e4SLinus Torvalds /* Switch Change */
651da177e4SLinus Torvalds dbg("cpqsbd: Switch interrupt received.\n");
661da177e4SLinus Torvalds
671da177e4SLinus Torvalds for (hp_slot = 0; hp_slot < 6; hp_slot++) {
681da177e4SLinus Torvalds if (change & (0x1L << hp_slot)) {
69427438c6SAlex Chiang /*
701da177e4SLinus Torvalds * this one changed.
71427438c6SAlex Chiang */
721da177e4SLinus Torvalds func = cpqhp_slot_find(ctrl->bus,
731da177e4SLinus Torvalds (hp_slot + ctrl->slot_device_offset), 0);
741da177e4SLinus Torvalds
751da177e4SLinus Torvalds /* this is the structure that tells the worker thread
76427438c6SAlex Chiang * what to do
77427438c6SAlex Chiang */
781da177e4SLinus Torvalds taskInfo = &(ctrl->event_queue[ctrl->next_event]);
791da177e4SLinus Torvalds ctrl->next_event = (ctrl->next_event + 1) % 10;
801da177e4SLinus Torvalds taskInfo->hp_slot = hp_slot;
811da177e4SLinus Torvalds
821da177e4SLinus Torvalds rc++;
831da177e4SLinus Torvalds
841da177e4SLinus Torvalds temp_word = ctrl->ctrl_int_comp >> 16;
851da177e4SLinus Torvalds func->presence_save = (temp_word >> hp_slot) & 0x01;
861da177e4SLinus Torvalds func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
871da177e4SLinus Torvalds
881da177e4SLinus Torvalds if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
89427438c6SAlex Chiang /*
901da177e4SLinus Torvalds * Switch opened
91427438c6SAlex Chiang */
921da177e4SLinus Torvalds
931da177e4SLinus Torvalds func->switch_save = 0;
941da177e4SLinus Torvalds
951da177e4SLinus Torvalds taskInfo->event_type = INT_SWITCH_OPEN;
961da177e4SLinus Torvalds } else {
97427438c6SAlex Chiang /*
981da177e4SLinus Torvalds * Switch closed
99427438c6SAlex Chiang */
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds func->switch_save = 0x10;
1021da177e4SLinus Torvalds
1031da177e4SLinus Torvalds taskInfo->event_type = INT_SWITCH_CLOSE;
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds
1081da177e4SLinus Torvalds return rc;
1091da177e4SLinus Torvalds }
1101da177e4SLinus Torvalds
1111da177e4SLinus Torvalds /**
11226e6c66eSRandy Dunlap * cpqhp_find_slot - find the struct slot of given device
1131da177e4SLinus Torvalds * @ctrl: scan lots of this controller
1141da177e4SLinus Torvalds * @device: the device id to find
1151da177e4SLinus Torvalds */
cpqhp_find_slot(struct controller * ctrl,u8 device)1161da177e4SLinus Torvalds static struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device)
1171da177e4SLinus Torvalds {
1181da177e4SLinus Torvalds struct slot *slot = ctrl->slot;
1191da177e4SLinus Torvalds
1201d3ecf13SAlex Chiang while (slot && (slot->device != device))
1211da177e4SLinus Torvalds slot = slot->next;
1221da177e4SLinus Torvalds
1231da177e4SLinus Torvalds return slot;
1241da177e4SLinus Torvalds }
1251da177e4SLinus Torvalds
1261da177e4SLinus Torvalds
handle_presence_change(u16 change,struct controller * ctrl)1271da177e4SLinus Torvalds static u8 handle_presence_change(u16 change, struct controller *ctrl)
1281da177e4SLinus Torvalds {
1291da177e4SLinus Torvalds int hp_slot;
1301da177e4SLinus Torvalds u8 rc = 0;
1311da177e4SLinus Torvalds u8 temp_byte;
1321da177e4SLinus Torvalds u16 temp_word;
1331da177e4SLinus Torvalds struct pci_func *func;
1341da177e4SLinus Torvalds struct event_info *taskInfo;
1351da177e4SLinus Torvalds struct slot *p_slot;
1361da177e4SLinus Torvalds
1371da177e4SLinus Torvalds if (!change)
1381da177e4SLinus Torvalds return 0;
1391da177e4SLinus Torvalds
140427438c6SAlex Chiang /*
1411da177e4SLinus Torvalds * Presence Change
142427438c6SAlex Chiang */
1431da177e4SLinus Torvalds dbg("cpqsbd: Presence/Notify input change.\n");
1441da177e4SLinus Torvalds dbg(" Changed bits are 0x%4.4x\n", change);
1451da177e4SLinus Torvalds
1461da177e4SLinus Torvalds for (hp_slot = 0; hp_slot < 6; hp_slot++) {
1471da177e4SLinus Torvalds if (change & (0x0101 << hp_slot)) {
148427438c6SAlex Chiang /*
1491da177e4SLinus Torvalds * this one changed.
150427438c6SAlex Chiang */
1511da177e4SLinus Torvalds func = cpqhp_slot_find(ctrl->bus,
1521da177e4SLinus Torvalds (hp_slot + ctrl->slot_device_offset), 0);
1531da177e4SLinus Torvalds
1541da177e4SLinus Torvalds taskInfo = &(ctrl->event_queue[ctrl->next_event]);
1551da177e4SLinus Torvalds ctrl->next_event = (ctrl->next_event + 1) % 10;
1561da177e4SLinus Torvalds taskInfo->hp_slot = hp_slot;
1571da177e4SLinus Torvalds
1581da177e4SLinus Torvalds rc++;
1591da177e4SLinus Torvalds
1601da177e4SLinus Torvalds p_slot = cpqhp_find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4));
1611da177e4SLinus Torvalds if (!p_slot)
1621da177e4SLinus Torvalds return 0;
1631da177e4SLinus Torvalds
1641da177e4SLinus Torvalds /* If the switch closed, must be a button
165427438c6SAlex Chiang * If not in button mode, nevermind
166427438c6SAlex Chiang */
1671da177e4SLinus Torvalds if (func->switch_save && (ctrl->push_button == 1)) {
1681da177e4SLinus Torvalds temp_word = ctrl->ctrl_int_comp >> 16;
1691da177e4SLinus Torvalds temp_byte = (temp_word >> hp_slot) & 0x01;
1701da177e4SLinus Torvalds temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02;
1711da177e4SLinus Torvalds
1721da177e4SLinus Torvalds if (temp_byte != func->presence_save) {
173427438c6SAlex Chiang /*
1741da177e4SLinus Torvalds * button Pressed (doesn't do anything)
175427438c6SAlex Chiang */
1761da177e4SLinus Torvalds dbg("hp_slot %d button pressed\n", hp_slot);
1771da177e4SLinus Torvalds taskInfo->event_type = INT_BUTTON_PRESS;
1781da177e4SLinus Torvalds } else {
179427438c6SAlex Chiang /*
1801da177e4SLinus Torvalds * button Released - TAKE ACTION!!!!
181427438c6SAlex Chiang */
1821da177e4SLinus Torvalds dbg("hp_slot %d button released\n", hp_slot);
1831da177e4SLinus Torvalds taskInfo->event_type = INT_BUTTON_RELEASE;
1841da177e4SLinus Torvalds
1851da177e4SLinus Torvalds /* Cancel if we are still blinking */
1861da177e4SLinus Torvalds if ((p_slot->state == BLINKINGON_STATE)
1871da177e4SLinus Torvalds || (p_slot->state == BLINKINGOFF_STATE)) {
1881da177e4SLinus Torvalds taskInfo->event_type = INT_BUTTON_CANCEL;
1891da177e4SLinus Torvalds dbg("hp_slot %d button cancel\n", hp_slot);
1901da177e4SLinus Torvalds } else if ((p_slot->state == POWERON_STATE)
1911da177e4SLinus Torvalds || (p_slot->state == POWEROFF_STATE)) {
1921da177e4SLinus Torvalds /* info(msg_button_ignore, p_slot->number); */
1931da177e4SLinus Torvalds taskInfo->event_type = INT_BUTTON_IGNORE;
1941da177e4SLinus Torvalds dbg("hp_slot %d button ignore\n", hp_slot);
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds } else {
1981da177e4SLinus Torvalds /* Switch is open, assume a presence change
199427438c6SAlex Chiang * Save the presence state
200427438c6SAlex Chiang */
2011da177e4SLinus Torvalds temp_word = ctrl->ctrl_int_comp >> 16;
2021da177e4SLinus Torvalds func->presence_save = (temp_word >> hp_slot) & 0x01;
2031da177e4SLinus Torvalds func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
2041da177e4SLinus Torvalds
2051da177e4SLinus Torvalds if ((!(ctrl->ctrl_int_comp & (0x010000 << hp_slot))) ||
2061da177e4SLinus Torvalds (!(ctrl->ctrl_int_comp & (0x01000000 << hp_slot)))) {
2071da177e4SLinus Torvalds /* Present */
2081da177e4SLinus Torvalds taskInfo->event_type = INT_PRESENCE_ON;
2091da177e4SLinus Torvalds } else {
2101da177e4SLinus Torvalds /* Not Present */
2111da177e4SLinus Torvalds taskInfo->event_type = INT_PRESENCE_OFF;
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds }
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds }
2161da177e4SLinus Torvalds
2171da177e4SLinus Torvalds return rc;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds
2201da177e4SLinus Torvalds
handle_power_fault(u8 change,struct controller * ctrl)2211da177e4SLinus Torvalds static u8 handle_power_fault(u8 change, struct controller *ctrl)
2221da177e4SLinus Torvalds {
2231da177e4SLinus Torvalds int hp_slot;
2241da177e4SLinus Torvalds u8 rc = 0;
2251da177e4SLinus Torvalds struct pci_func *func;
2261da177e4SLinus Torvalds struct event_info *taskInfo;
2271da177e4SLinus Torvalds
2281da177e4SLinus Torvalds if (!change)
2291da177e4SLinus Torvalds return 0;
2301da177e4SLinus Torvalds
231427438c6SAlex Chiang /*
2321da177e4SLinus Torvalds * power fault
233427438c6SAlex Chiang */
2341da177e4SLinus Torvalds
2351da177e4SLinus Torvalds info("power fault interrupt\n");
2361da177e4SLinus Torvalds
2371da177e4SLinus Torvalds for (hp_slot = 0; hp_slot < 6; hp_slot++) {
2381da177e4SLinus Torvalds if (change & (0x01 << hp_slot)) {
239427438c6SAlex Chiang /*
2401da177e4SLinus Torvalds * this one changed.
241427438c6SAlex Chiang */
2421da177e4SLinus Torvalds func = cpqhp_slot_find(ctrl->bus,
2431da177e4SLinus Torvalds (hp_slot + ctrl->slot_device_offset), 0);
2441da177e4SLinus Torvalds
2451da177e4SLinus Torvalds taskInfo = &(ctrl->event_queue[ctrl->next_event]);
2461da177e4SLinus Torvalds ctrl->next_event = (ctrl->next_event + 1) % 10;
2471da177e4SLinus Torvalds taskInfo->hp_slot = hp_slot;
2481da177e4SLinus Torvalds
2491da177e4SLinus Torvalds rc++;
2501da177e4SLinus Torvalds
2511da177e4SLinus Torvalds if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) {
252427438c6SAlex Chiang /*
2531da177e4SLinus Torvalds * power fault Cleared
254427438c6SAlex Chiang */
2551da177e4SLinus Torvalds func->status = 0x00;
2561da177e4SLinus Torvalds
2571da177e4SLinus Torvalds taskInfo->event_type = INT_POWER_FAULT_CLEAR;
2581da177e4SLinus Torvalds } else {
259427438c6SAlex Chiang /*
2601da177e4SLinus Torvalds * power fault
261427438c6SAlex Chiang */
2621da177e4SLinus Torvalds taskInfo->event_type = INT_POWER_FAULT;
2631da177e4SLinus Torvalds
2641da177e4SLinus Torvalds if (ctrl->rev < 4) {
2651da177e4SLinus Torvalds amber_LED_on(ctrl, hp_slot);
2661da177e4SLinus Torvalds green_LED_off(ctrl, hp_slot);
2671da177e4SLinus Torvalds set_SOGO(ctrl);
2681da177e4SLinus Torvalds
2691da177e4SLinus Torvalds /* this is a fatal condition, we want
2701da177e4SLinus Torvalds * to crash the machine to protect from
2711da177e4SLinus Torvalds * data corruption. simulated_NMI
2721da177e4SLinus Torvalds * shouldn't ever return */
2731da177e4SLinus Torvalds /* FIXME
2741da177e4SLinus Torvalds simulated_NMI(hp_slot, ctrl); */
2751da177e4SLinus Torvalds
2761da177e4SLinus Torvalds /* The following code causes a software
2771da177e4SLinus Torvalds * crash just in case simulated_NMI did
2781da177e4SLinus Torvalds * return */
2791da177e4SLinus Torvalds /*FIXME
2801da177e4SLinus Torvalds panic(msg_power_fault); */
2811da177e4SLinus Torvalds } else {
2821da177e4SLinus Torvalds /* set power fault status for this board */
2831da177e4SLinus Torvalds func->status = 0xFF;
2841da177e4SLinus Torvalds info("power fault bit %x set\n", hp_slot);
2851da177e4SLinus Torvalds }
2861da177e4SLinus Torvalds }
2871da177e4SLinus Torvalds }
2881da177e4SLinus Torvalds }
2891da177e4SLinus Torvalds
2901da177e4SLinus Torvalds return rc;
2911da177e4SLinus Torvalds }
2921da177e4SLinus Torvalds
2931da177e4SLinus Torvalds
2941da177e4SLinus Torvalds /**
29526e6c66eSRandy Dunlap * sort_by_size - sort nodes on the list by their length, smallest first.
2961da177e4SLinus Torvalds * @head: list to sort
2971da177e4SLinus Torvalds */
sort_by_size(struct pci_resource ** head)2981da177e4SLinus Torvalds static int sort_by_size(struct pci_resource **head)
2991da177e4SLinus Torvalds {
3001da177e4SLinus Torvalds struct pci_resource *current_res;
3011da177e4SLinus Torvalds struct pci_resource *next_res;
3021da177e4SLinus Torvalds int out_of_order = 1;
3031da177e4SLinus Torvalds
3041da177e4SLinus Torvalds if (!(*head))
3051da177e4SLinus Torvalds return 1;
3061da177e4SLinus Torvalds
3071da177e4SLinus Torvalds if (!((*head)->next))
3081da177e4SLinus Torvalds return 0;
3091da177e4SLinus Torvalds
3101da177e4SLinus Torvalds while (out_of_order) {
3111da177e4SLinus Torvalds out_of_order = 0;
3121da177e4SLinus Torvalds
3131da177e4SLinus Torvalds /* Special case for swapping list head */
3141da177e4SLinus Torvalds if (((*head)->next) &&
3151da177e4SLinus Torvalds ((*head)->length > (*head)->next->length)) {
3161da177e4SLinus Torvalds out_of_order++;
3171da177e4SLinus Torvalds current_res = *head;
3181da177e4SLinus Torvalds *head = (*head)->next;
3191da177e4SLinus Torvalds current_res->next = (*head)->next;
3201da177e4SLinus Torvalds (*head)->next = current_res;
3211da177e4SLinus Torvalds }
3221da177e4SLinus Torvalds
3231da177e4SLinus Torvalds current_res = *head;
3241da177e4SLinus Torvalds
3251da177e4SLinus Torvalds while (current_res->next && current_res->next->next) {
3261da177e4SLinus Torvalds if (current_res->next->length > current_res->next->next->length) {
3271da177e4SLinus Torvalds out_of_order++;
3281da177e4SLinus Torvalds next_res = current_res->next;
3291da177e4SLinus Torvalds current_res->next = current_res->next->next;
3301da177e4SLinus Torvalds current_res = current_res->next;
3311da177e4SLinus Torvalds next_res->next = current_res->next;
3321da177e4SLinus Torvalds current_res->next = next_res;
3331da177e4SLinus Torvalds } else
3341da177e4SLinus Torvalds current_res = current_res->next;
3351da177e4SLinus Torvalds }
3361da177e4SLinus Torvalds } /* End of out_of_order loop */
3371da177e4SLinus Torvalds
3381da177e4SLinus Torvalds return 0;
3391da177e4SLinus Torvalds }
3401da177e4SLinus Torvalds
3411da177e4SLinus Torvalds
3421da177e4SLinus Torvalds /**
34326e6c66eSRandy Dunlap * sort_by_max_size - sort nodes on the list by their length, largest first.
3441da177e4SLinus Torvalds * @head: list to sort
3451da177e4SLinus Torvalds */
sort_by_max_size(struct pci_resource ** head)3461da177e4SLinus Torvalds static int sort_by_max_size(struct pci_resource **head)
3471da177e4SLinus Torvalds {
3481da177e4SLinus Torvalds struct pci_resource *current_res;
3491da177e4SLinus Torvalds struct pci_resource *next_res;
3501da177e4SLinus Torvalds int out_of_order = 1;
3511da177e4SLinus Torvalds
3521da177e4SLinus Torvalds if (!(*head))
3531da177e4SLinus Torvalds return 1;
3541da177e4SLinus Torvalds
3551da177e4SLinus Torvalds if (!((*head)->next))
3561da177e4SLinus Torvalds return 0;
3571da177e4SLinus Torvalds
3581da177e4SLinus Torvalds while (out_of_order) {
3591da177e4SLinus Torvalds out_of_order = 0;
3601da177e4SLinus Torvalds
3611da177e4SLinus Torvalds /* Special case for swapping list head */
3621da177e4SLinus Torvalds if (((*head)->next) &&
3631da177e4SLinus Torvalds ((*head)->length < (*head)->next->length)) {
3641da177e4SLinus Torvalds out_of_order++;
3651da177e4SLinus Torvalds current_res = *head;
3661da177e4SLinus Torvalds *head = (*head)->next;
3671da177e4SLinus Torvalds current_res->next = (*head)->next;
3681da177e4SLinus Torvalds (*head)->next = current_res;
3691da177e4SLinus Torvalds }
3701da177e4SLinus Torvalds
3711da177e4SLinus Torvalds current_res = *head;
3721da177e4SLinus Torvalds
3731da177e4SLinus Torvalds while (current_res->next && current_res->next->next) {
3741da177e4SLinus Torvalds if (current_res->next->length < current_res->next->next->length) {
3751da177e4SLinus Torvalds out_of_order++;
3761da177e4SLinus Torvalds next_res = current_res->next;
3771da177e4SLinus Torvalds current_res->next = current_res->next->next;
3781da177e4SLinus Torvalds current_res = current_res->next;
3791da177e4SLinus Torvalds next_res->next = current_res->next;
3801da177e4SLinus Torvalds current_res->next = next_res;
3811da177e4SLinus Torvalds } else
3821da177e4SLinus Torvalds current_res = current_res->next;
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds } /* End of out_of_order loop */
3851da177e4SLinus Torvalds
3861da177e4SLinus Torvalds return 0;
3871da177e4SLinus Torvalds }
3881da177e4SLinus Torvalds
3891da177e4SLinus Torvalds
3901da177e4SLinus Torvalds /**
39126e6c66eSRandy Dunlap * do_pre_bridge_resource_split - find node of resources that are unused
39226e6c66eSRandy Dunlap * @head: new list head
39326e6c66eSRandy Dunlap * @orig_head: original list head
39426e6c66eSRandy Dunlap * @alignment: max node size (?)
3951da177e4SLinus Torvalds */
do_pre_bridge_resource_split(struct pci_resource ** head,struct pci_resource ** orig_head,u32 alignment)3961da177e4SLinus Torvalds static struct pci_resource *do_pre_bridge_resource_split(struct pci_resource **head,
3971da177e4SLinus Torvalds struct pci_resource **orig_head, u32 alignment)
3981da177e4SLinus Torvalds {
3991da177e4SLinus Torvalds struct pci_resource *prevnode = NULL;
4001da177e4SLinus Torvalds struct pci_resource *node;
4011da177e4SLinus Torvalds struct pci_resource *split_node;
4021da177e4SLinus Torvalds u32 rc;
4031da177e4SLinus Torvalds u32 temp_dword;
4041da177e4SLinus Torvalds dbg("do_pre_bridge_resource_split\n");
4051da177e4SLinus Torvalds
4061da177e4SLinus Torvalds if (!(*head) || !(*orig_head))
4071da177e4SLinus Torvalds return NULL;
4081da177e4SLinus Torvalds
4091da177e4SLinus Torvalds rc = cpqhp_resource_sort_and_combine(head);
4101da177e4SLinus Torvalds
4111da177e4SLinus Torvalds if (rc)
4121da177e4SLinus Torvalds return NULL;
4131da177e4SLinus Torvalds
4141da177e4SLinus Torvalds if ((*head)->base != (*orig_head)->base)
4151da177e4SLinus Torvalds return NULL;
4161da177e4SLinus Torvalds
4171da177e4SLinus Torvalds if ((*head)->length == (*orig_head)->length)
4181da177e4SLinus Torvalds return NULL;
4191da177e4SLinus Torvalds
4201da177e4SLinus Torvalds
4211da177e4SLinus Torvalds /* If we got here, there the bridge requires some of the resource, but
422427438c6SAlex Chiang * we may be able to split some off of the front
423427438c6SAlex Chiang */
4241da177e4SLinus Torvalds
4251da177e4SLinus Torvalds node = *head;
4261da177e4SLinus Torvalds
4271da177e4SLinus Torvalds if (node->length & (alignment - 1)) {
4281da177e4SLinus Torvalds /* this one isn't an aligned length, so we'll make a new entry
429427438c6SAlex Chiang * and split it up.
430427438c6SAlex Chiang */
4311da177e4SLinus Torvalds split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
4321da177e4SLinus Torvalds
4331da177e4SLinus Torvalds if (!split_node)
4341da177e4SLinus Torvalds return NULL;
4351da177e4SLinus Torvalds
4361da177e4SLinus Torvalds temp_dword = (node->length | (alignment-1)) + 1 - alignment;
4371da177e4SLinus Torvalds
4381da177e4SLinus Torvalds split_node->base = node->base;
4391da177e4SLinus Torvalds split_node->length = temp_dword;
4401da177e4SLinus Torvalds
4411da177e4SLinus Torvalds node->length -= temp_dword;
4421da177e4SLinus Torvalds node->base += split_node->length;
4431da177e4SLinus Torvalds
4441da177e4SLinus Torvalds /* Put it in the list */
4451da177e4SLinus Torvalds *head = split_node;
4461da177e4SLinus Torvalds split_node->next = node;
4471da177e4SLinus Torvalds }
4481da177e4SLinus Torvalds
4491da177e4SLinus Torvalds if (node->length < alignment)
4501da177e4SLinus Torvalds return NULL;
4511da177e4SLinus Torvalds
4521da177e4SLinus Torvalds /* Now unlink it */
4531da177e4SLinus Torvalds if (*head == node) {
4541da177e4SLinus Torvalds *head = node->next;
4551da177e4SLinus Torvalds } else {
4561da177e4SLinus Torvalds prevnode = *head;
4571da177e4SLinus Torvalds while (prevnode->next != node)
4581da177e4SLinus Torvalds prevnode = prevnode->next;
4591da177e4SLinus Torvalds
4601da177e4SLinus Torvalds prevnode->next = node->next;
4611da177e4SLinus Torvalds }
4621da177e4SLinus Torvalds node->next = NULL;
4631da177e4SLinus Torvalds
4641da177e4SLinus Torvalds return node;
4651da177e4SLinus Torvalds }
4661da177e4SLinus Torvalds
4671da177e4SLinus Torvalds
4681da177e4SLinus Torvalds /**
46926e6c66eSRandy Dunlap * do_bridge_resource_split - find one node of resources that aren't in use
47026e6c66eSRandy Dunlap * @head: list head
47126e6c66eSRandy Dunlap * @alignment: max node size (?)
4721da177e4SLinus Torvalds */
do_bridge_resource_split(struct pci_resource ** head,u32 alignment)4731da177e4SLinus Torvalds static struct pci_resource *do_bridge_resource_split(struct pci_resource **head, u32 alignment)
4741da177e4SLinus Torvalds {
4751da177e4SLinus Torvalds struct pci_resource *prevnode = NULL;
4761da177e4SLinus Torvalds struct pci_resource *node;
4771da177e4SLinus Torvalds u32 rc;
4781da177e4SLinus Torvalds u32 temp_dword;
4791da177e4SLinus Torvalds
4801da177e4SLinus Torvalds rc = cpqhp_resource_sort_and_combine(head);
4811da177e4SLinus Torvalds
4821da177e4SLinus Torvalds if (rc)
4831da177e4SLinus Torvalds return NULL;
4841da177e4SLinus Torvalds
4851da177e4SLinus Torvalds node = *head;
4861da177e4SLinus Torvalds
4871da177e4SLinus Torvalds while (node->next) {
4881da177e4SLinus Torvalds prevnode = node;
4891da177e4SLinus Torvalds node = node->next;
4901da177e4SLinus Torvalds kfree(prevnode);
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds
4931da177e4SLinus Torvalds if (node->length < alignment)
4941da177e4SLinus Torvalds goto error;
4951da177e4SLinus Torvalds
4961da177e4SLinus Torvalds if (node->base & (alignment - 1)) {
4971da177e4SLinus Torvalds /* Short circuit if adjusted size is too small */
4981da177e4SLinus Torvalds temp_dword = (node->base | (alignment-1)) + 1;
4991da177e4SLinus Torvalds if ((node->length - (temp_dword - node->base)) < alignment)
5001da177e4SLinus Torvalds goto error;
5011da177e4SLinus Torvalds
5021da177e4SLinus Torvalds node->length -= (temp_dword - node->base);
5031da177e4SLinus Torvalds node->base = temp_dword;
5041da177e4SLinus Torvalds }
5051da177e4SLinus Torvalds
5061da177e4SLinus Torvalds if (node->length & (alignment - 1))
5071da177e4SLinus Torvalds /* There's stuff in use after this node */
5081da177e4SLinus Torvalds goto error;
5091da177e4SLinus Torvalds
5101da177e4SLinus Torvalds return node;
5111da177e4SLinus Torvalds error:
5121da177e4SLinus Torvalds kfree(node);
5131da177e4SLinus Torvalds return NULL;
5141da177e4SLinus Torvalds }
5151da177e4SLinus Torvalds
5161da177e4SLinus Torvalds
5171da177e4SLinus Torvalds /**
51826e6c66eSRandy Dunlap * get_io_resource - find first node of given size not in ISA aliasing window.
5191da177e4SLinus Torvalds * @head: list to search
5201da177e4SLinus Torvalds * @size: size of node to find, must be a power of two.
5211da177e4SLinus Torvalds *
522b2105b9fSKrzysztof Wilczyński * Description: This function sorts the resource list by size and then
5231da177e4SLinus Torvalds * returns the first node of "size" length that is not in the ISA aliasing
5241da177e4SLinus Torvalds * window. If it finds a node larger than "size" it will split it up.
5251da177e4SLinus Torvalds */
get_io_resource(struct pci_resource ** head,u32 size)5261da177e4SLinus Torvalds static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size)
5271da177e4SLinus Torvalds {
5281da177e4SLinus Torvalds struct pci_resource *prevnode;
5291da177e4SLinus Torvalds struct pci_resource *node;
5301da177e4SLinus Torvalds struct pci_resource *split_node;
5311da177e4SLinus Torvalds u32 temp_dword;
5321da177e4SLinus Torvalds
5331da177e4SLinus Torvalds if (!(*head))
5341da177e4SLinus Torvalds return NULL;
5351da177e4SLinus Torvalds
5361da177e4SLinus Torvalds if (cpqhp_resource_sort_and_combine(head))
5371da177e4SLinus Torvalds return NULL;
5381da177e4SLinus Torvalds
5391da177e4SLinus Torvalds if (sort_by_size(head))
5401da177e4SLinus Torvalds return NULL;
5411da177e4SLinus Torvalds
5421da177e4SLinus Torvalds for (node = *head; node; node = node->next) {
5431da177e4SLinus Torvalds if (node->length < size)
5441da177e4SLinus Torvalds continue;
5451da177e4SLinus Torvalds
5461da177e4SLinus Torvalds if (node->base & (size - 1)) {
5471da177e4SLinus Torvalds /* this one isn't base aligned properly
548427438c6SAlex Chiang * so we'll make a new entry and split it up
549427438c6SAlex Chiang */
5501da177e4SLinus Torvalds temp_dword = (node->base | (size-1)) + 1;
5511da177e4SLinus Torvalds
5521da177e4SLinus Torvalds /* Short circuit if adjusted size is too small */
5531da177e4SLinus Torvalds if ((node->length - (temp_dword - node->base)) < size)
5541da177e4SLinus Torvalds continue;
5551da177e4SLinus Torvalds
5561da177e4SLinus Torvalds split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
5571da177e4SLinus Torvalds
5581da177e4SLinus Torvalds if (!split_node)
5591da177e4SLinus Torvalds return NULL;
5601da177e4SLinus Torvalds
5611da177e4SLinus Torvalds split_node->base = node->base;
5621da177e4SLinus Torvalds split_node->length = temp_dword - node->base;
5631da177e4SLinus Torvalds node->base = temp_dword;
5641da177e4SLinus Torvalds node->length -= split_node->length;
5651da177e4SLinus Torvalds
5661da177e4SLinus Torvalds /* Put it in the list */
5671da177e4SLinus Torvalds split_node->next = node->next;
5681da177e4SLinus Torvalds node->next = split_node;
5691da177e4SLinus Torvalds } /* End of non-aligned base */
5701da177e4SLinus Torvalds
5711da177e4SLinus Torvalds /* Don't need to check if too small since we already did */
5721da177e4SLinus Torvalds if (node->length > size) {
5731da177e4SLinus Torvalds /* this one is longer than we need
574427438c6SAlex Chiang * so we'll make a new entry and split it up
575427438c6SAlex Chiang */
5761da177e4SLinus Torvalds split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
5771da177e4SLinus Torvalds
5781da177e4SLinus Torvalds if (!split_node)
5791da177e4SLinus Torvalds return NULL;
5801da177e4SLinus Torvalds
5811da177e4SLinus Torvalds split_node->base = node->base + size;
5821da177e4SLinus Torvalds split_node->length = node->length - size;
5831da177e4SLinus Torvalds node->length = size;
5841da177e4SLinus Torvalds
5851da177e4SLinus Torvalds /* Put it in the list */
5861da177e4SLinus Torvalds split_node->next = node->next;
5871da177e4SLinus Torvalds node->next = split_node;
5881da177e4SLinus Torvalds } /* End of too big on top end */
5891da177e4SLinus Torvalds
5901da177e4SLinus Torvalds /* For IO make sure it's not in the ISA aliasing space */
5911da177e4SLinus Torvalds if (node->base & 0x300L)
5921da177e4SLinus Torvalds continue;
5931da177e4SLinus Torvalds
5941da177e4SLinus Torvalds /* If we got here, then it is the right size
595427438c6SAlex Chiang * Now take it out of the list and break
596427438c6SAlex Chiang */
5971da177e4SLinus Torvalds if (*head == node) {
5981da177e4SLinus Torvalds *head = node->next;
5991da177e4SLinus Torvalds } else {
6001da177e4SLinus Torvalds prevnode = *head;
6011da177e4SLinus Torvalds while (prevnode->next != node)
6021da177e4SLinus Torvalds prevnode = prevnode->next;
6031da177e4SLinus Torvalds
6041da177e4SLinus Torvalds prevnode->next = node->next;
6051da177e4SLinus Torvalds }
6061da177e4SLinus Torvalds node->next = NULL;
6071da177e4SLinus Torvalds break;
6081da177e4SLinus Torvalds }
6091da177e4SLinus Torvalds
6101da177e4SLinus Torvalds return node;
6111da177e4SLinus Torvalds }
6121da177e4SLinus Torvalds
6131da177e4SLinus Torvalds
6141da177e4SLinus Torvalds /**
61526e6c66eSRandy Dunlap * get_max_resource - get largest node which has at least the given size.
6161da177e4SLinus Torvalds * @head: the list to search the node in
6171da177e4SLinus Torvalds * @size: the minimum size of the node to find
6181da177e4SLinus Torvalds *
6191da177e4SLinus Torvalds * Description: Gets the largest node that is at least "size" big from the
6201da177e4SLinus Torvalds * list pointed to by head. It aligns the node on top and bottom
6211da177e4SLinus Torvalds * to "size" alignment before returning it.
6221da177e4SLinus Torvalds */
get_max_resource(struct pci_resource ** head,u32 size)6231da177e4SLinus Torvalds static struct pci_resource *get_max_resource(struct pci_resource **head, u32 size)
6241da177e4SLinus Torvalds {
6251da177e4SLinus Torvalds struct pci_resource *max;
6261da177e4SLinus Torvalds struct pci_resource *temp;
6271da177e4SLinus Torvalds struct pci_resource *split_node;
6281da177e4SLinus Torvalds u32 temp_dword;
6291da177e4SLinus Torvalds
6301da177e4SLinus Torvalds if (cpqhp_resource_sort_and_combine(head))
6311da177e4SLinus Torvalds return NULL;
6321da177e4SLinus Torvalds
6331da177e4SLinus Torvalds if (sort_by_max_size(head))
6341da177e4SLinus Torvalds return NULL;
6351da177e4SLinus Torvalds
6361da177e4SLinus Torvalds for (max = *head; max; max = max->next) {
6371da177e4SLinus Torvalds /* If not big enough we could probably just bail,
638427438c6SAlex Chiang * instead we'll continue to the next.
639427438c6SAlex Chiang */
6401da177e4SLinus Torvalds if (max->length < size)
6411da177e4SLinus Torvalds continue;
6421da177e4SLinus Torvalds
6431da177e4SLinus Torvalds if (max->base & (size - 1)) {
6441da177e4SLinus Torvalds /* this one isn't base aligned properly
645427438c6SAlex Chiang * so we'll make a new entry and split it up
646427438c6SAlex Chiang */
6471da177e4SLinus Torvalds temp_dword = (max->base | (size-1)) + 1;
6481da177e4SLinus Torvalds
6491da177e4SLinus Torvalds /* Short circuit if adjusted size is too small */
6501da177e4SLinus Torvalds if ((max->length - (temp_dword - max->base)) < size)
6511da177e4SLinus Torvalds continue;
6521da177e4SLinus Torvalds
6531da177e4SLinus Torvalds split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
6541da177e4SLinus Torvalds
6551da177e4SLinus Torvalds if (!split_node)
6561da177e4SLinus Torvalds return NULL;
6571da177e4SLinus Torvalds
6581da177e4SLinus Torvalds split_node->base = max->base;
6591da177e4SLinus Torvalds split_node->length = temp_dword - max->base;
6601da177e4SLinus Torvalds max->base = temp_dword;
6611da177e4SLinus Torvalds max->length -= split_node->length;
6621da177e4SLinus Torvalds
6631da177e4SLinus Torvalds split_node->next = max->next;
6641da177e4SLinus Torvalds max->next = split_node;
6651da177e4SLinus Torvalds }
6661da177e4SLinus Torvalds
6671da177e4SLinus Torvalds if ((max->base + max->length) & (size - 1)) {
6681da177e4SLinus Torvalds /* this one isn't end aligned properly at the top
669427438c6SAlex Chiang * so we'll make a new entry and split it up
670427438c6SAlex Chiang */
6711da177e4SLinus Torvalds split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
6721da177e4SLinus Torvalds
6731da177e4SLinus Torvalds if (!split_node)
6741da177e4SLinus Torvalds return NULL;
6751da177e4SLinus Torvalds temp_dword = ((max->base + max->length) & ~(size - 1));
6761da177e4SLinus Torvalds split_node->base = temp_dword;
6771da177e4SLinus Torvalds split_node->length = max->length + max->base
6781da177e4SLinus Torvalds - split_node->base;
6791da177e4SLinus Torvalds max->length -= split_node->length;
6801da177e4SLinus Torvalds
6811da177e4SLinus Torvalds split_node->next = max->next;
6821da177e4SLinus Torvalds max->next = split_node;
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds
6851da177e4SLinus Torvalds /* Make sure it didn't shrink too much when we aligned it */
6861da177e4SLinus Torvalds if (max->length < size)
6871da177e4SLinus Torvalds continue;
6881da177e4SLinus Torvalds
6891da177e4SLinus Torvalds /* Now take it out of the list */
6901da177e4SLinus Torvalds temp = *head;
6911da177e4SLinus Torvalds if (temp == max) {
6921da177e4SLinus Torvalds *head = max->next;
6931da177e4SLinus Torvalds } else {
694656f978fSQuentin Lambert while (temp && temp->next != max)
6951da177e4SLinus Torvalds temp = temp->next;
6961da177e4SLinus Torvalds
697cab9a128SRickard Strandqvist if (temp)
6981da177e4SLinus Torvalds temp->next = max->next;
6991da177e4SLinus Torvalds }
7001da177e4SLinus Torvalds
7011da177e4SLinus Torvalds max->next = NULL;
7021da177e4SLinus Torvalds break;
7031da177e4SLinus Torvalds }
7041da177e4SLinus Torvalds
7051da177e4SLinus Torvalds return max;
7061da177e4SLinus Torvalds }
7071da177e4SLinus Torvalds
7081da177e4SLinus Torvalds
7091da177e4SLinus Torvalds /**
71026e6c66eSRandy Dunlap * get_resource - find resource of given size and split up larger ones.
7111da177e4SLinus Torvalds * @head: the list to search for resources
7121da177e4SLinus Torvalds * @size: the size limit to use
7131da177e4SLinus Torvalds *
7141da177e4SLinus Torvalds * Description: This function sorts the resource list by size and then
7151da177e4SLinus Torvalds * returns the first node of "size" length. If it finds a node
7161da177e4SLinus Torvalds * larger than "size" it will split it up.
7171da177e4SLinus Torvalds *
7181da177e4SLinus Torvalds * size must be a power of two.
7191da177e4SLinus Torvalds */
get_resource(struct pci_resource ** head,u32 size)7201da177e4SLinus Torvalds static struct pci_resource *get_resource(struct pci_resource **head, u32 size)
7211da177e4SLinus Torvalds {
7221da177e4SLinus Torvalds struct pci_resource *prevnode;
7231da177e4SLinus Torvalds struct pci_resource *node;
7241da177e4SLinus Torvalds struct pci_resource *split_node;
7251da177e4SLinus Torvalds u32 temp_dword;
7261da177e4SLinus Torvalds
7271da177e4SLinus Torvalds if (cpqhp_resource_sort_and_combine(head))
7281da177e4SLinus Torvalds return NULL;
7291da177e4SLinus Torvalds
7301da177e4SLinus Torvalds if (sort_by_size(head))
7311da177e4SLinus Torvalds return NULL;
7321da177e4SLinus Torvalds
7331da177e4SLinus Torvalds for (node = *head; node; node = node->next) {
7341da177e4SLinus Torvalds dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",
73566bef8c0SHarvey Harrison __func__, size, node, node->base, node->length);
7361da177e4SLinus Torvalds if (node->length < size)
7371da177e4SLinus Torvalds continue;
7381da177e4SLinus Torvalds
7391da177e4SLinus Torvalds if (node->base & (size - 1)) {
74066bef8c0SHarvey Harrison dbg("%s: not aligned\n", __func__);
7411da177e4SLinus Torvalds /* this one isn't base aligned properly
742427438c6SAlex Chiang * so we'll make a new entry and split it up
743427438c6SAlex Chiang */
7441da177e4SLinus Torvalds temp_dword = (node->base | (size-1)) + 1;
7451da177e4SLinus Torvalds
7461da177e4SLinus Torvalds /* Short circuit if adjusted size is too small */
7471da177e4SLinus Torvalds if ((node->length - (temp_dword - node->base)) < size)
7481da177e4SLinus Torvalds continue;
7491da177e4SLinus Torvalds
7501da177e4SLinus Torvalds split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
7511da177e4SLinus Torvalds
7521da177e4SLinus Torvalds if (!split_node)
7531da177e4SLinus Torvalds return NULL;
7541da177e4SLinus Torvalds
7551da177e4SLinus Torvalds split_node->base = node->base;
7561da177e4SLinus Torvalds split_node->length = temp_dword - node->base;
7571da177e4SLinus Torvalds node->base = temp_dword;
7581da177e4SLinus Torvalds node->length -= split_node->length;
7591da177e4SLinus Torvalds
7601da177e4SLinus Torvalds split_node->next = node->next;
7611da177e4SLinus Torvalds node->next = split_node;
7621da177e4SLinus Torvalds } /* End of non-aligned base */
7631da177e4SLinus Torvalds
7641da177e4SLinus Torvalds /* Don't need to check if too small since we already did */
7651da177e4SLinus Torvalds if (node->length > size) {
76666bef8c0SHarvey Harrison dbg("%s: too big\n", __func__);
7671da177e4SLinus Torvalds /* this one is longer than we need
768427438c6SAlex Chiang * so we'll make a new entry and split it up
769427438c6SAlex Chiang */
7701da177e4SLinus Torvalds split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
7711da177e4SLinus Torvalds
7721da177e4SLinus Torvalds if (!split_node)
7731da177e4SLinus Torvalds return NULL;
7741da177e4SLinus Torvalds
7751da177e4SLinus Torvalds split_node->base = node->base + size;
7761da177e4SLinus Torvalds split_node->length = node->length - size;
7771da177e4SLinus Torvalds node->length = size;
7781da177e4SLinus Torvalds
7791da177e4SLinus Torvalds /* Put it in the list */
7801da177e4SLinus Torvalds split_node->next = node->next;
7811da177e4SLinus Torvalds node->next = split_node;
7821da177e4SLinus Torvalds } /* End of too big on top end */
7831da177e4SLinus Torvalds
78466bef8c0SHarvey Harrison dbg("%s: got one!!!\n", __func__);
7851da177e4SLinus Torvalds /* If we got here, then it is the right size
7861da177e4SLinus Torvalds * Now take it out of the list */
7871da177e4SLinus Torvalds if (*head == node) {
7881da177e4SLinus Torvalds *head = node->next;
7891da177e4SLinus Torvalds } else {
7901da177e4SLinus Torvalds prevnode = *head;
7911da177e4SLinus Torvalds while (prevnode->next != node)
7921da177e4SLinus Torvalds prevnode = prevnode->next;
7931da177e4SLinus Torvalds
7941da177e4SLinus Torvalds prevnode->next = node->next;
7951da177e4SLinus Torvalds }
7961da177e4SLinus Torvalds node->next = NULL;
7971da177e4SLinus Torvalds break;
7981da177e4SLinus Torvalds }
7991da177e4SLinus Torvalds return node;
8001da177e4SLinus Torvalds }
8011da177e4SLinus Torvalds
8021da177e4SLinus Torvalds
8031da177e4SLinus Torvalds /**
80426e6c66eSRandy Dunlap * cpqhp_resource_sort_and_combine - sort nodes by base addresses and clean up
8051da177e4SLinus Torvalds * @head: the list to sort and clean up
8061da177e4SLinus Torvalds *
8071da177e4SLinus Torvalds * Description: Sorts all of the nodes in the list in ascending order by
8081da177e4SLinus Torvalds * their base addresses. Also does garbage collection by
8091da177e4SLinus Torvalds * combining adjacent nodes.
8101da177e4SLinus Torvalds *
81126e6c66eSRandy Dunlap * Returns %0 if success.
8121da177e4SLinus Torvalds */
cpqhp_resource_sort_and_combine(struct pci_resource ** head)8131da177e4SLinus Torvalds int cpqhp_resource_sort_and_combine(struct pci_resource **head)
8141da177e4SLinus Torvalds {
8151da177e4SLinus Torvalds struct pci_resource *node1;
8161da177e4SLinus Torvalds struct pci_resource *node2;
8171da177e4SLinus Torvalds int out_of_order = 1;
8181da177e4SLinus Torvalds
81966bef8c0SHarvey Harrison dbg("%s: head = %p, *head = %p\n", __func__, head, *head);
8201da177e4SLinus Torvalds
8211da177e4SLinus Torvalds if (!(*head))
8221da177e4SLinus Torvalds return 1;
8231da177e4SLinus Torvalds
8241da177e4SLinus Torvalds dbg("*head->next = %p\n", (*head)->next);
8251da177e4SLinus Torvalds
8261da177e4SLinus Torvalds if (!(*head)->next)
8271da177e4SLinus Torvalds return 0; /* only one item on the list, already sorted! */
8281da177e4SLinus Torvalds
8291da177e4SLinus Torvalds dbg("*head->base = 0x%x\n", (*head)->base);
8301da177e4SLinus Torvalds dbg("*head->next->base = 0x%x\n", (*head)->next->base);
8311da177e4SLinus Torvalds while (out_of_order) {
8321da177e4SLinus Torvalds out_of_order = 0;
8331da177e4SLinus Torvalds
8341da177e4SLinus Torvalds /* Special case for swapping list head */
8351da177e4SLinus Torvalds if (((*head)->next) &&
8361da177e4SLinus Torvalds ((*head)->base > (*head)->next->base)) {
8371da177e4SLinus Torvalds node1 = *head;
8381da177e4SLinus Torvalds (*head) = (*head)->next;
8391da177e4SLinus Torvalds node1->next = (*head)->next;
8401da177e4SLinus Torvalds (*head)->next = node1;
8411da177e4SLinus Torvalds out_of_order++;
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds
8441da177e4SLinus Torvalds node1 = (*head);
8451da177e4SLinus Torvalds
8461da177e4SLinus Torvalds while (node1->next && node1->next->next) {
8471da177e4SLinus Torvalds if (node1->next->base > node1->next->next->base) {
8481da177e4SLinus Torvalds out_of_order++;
8491da177e4SLinus Torvalds node2 = node1->next;
8501da177e4SLinus Torvalds node1->next = node1->next->next;
8511da177e4SLinus Torvalds node1 = node1->next;
8521da177e4SLinus Torvalds node2->next = node1->next;
8531da177e4SLinus Torvalds node1->next = node2;
8541da177e4SLinus Torvalds } else
8551da177e4SLinus Torvalds node1 = node1->next;
8561da177e4SLinus Torvalds }
8571da177e4SLinus Torvalds } /* End of out_of_order loop */
8581da177e4SLinus Torvalds
8591da177e4SLinus Torvalds node1 = *head;
8601da177e4SLinus Torvalds
8611da177e4SLinus Torvalds while (node1 && node1->next) {
8621da177e4SLinus Torvalds if ((node1->base + node1->length) == node1->next->base) {
8631da177e4SLinus Torvalds /* Combine */
8641da177e4SLinus Torvalds dbg("8..\n");
8651da177e4SLinus Torvalds node1->length += node1->next->length;
8661da177e4SLinus Torvalds node2 = node1->next;
8671da177e4SLinus Torvalds node1->next = node1->next->next;
8681da177e4SLinus Torvalds kfree(node2);
8691da177e4SLinus Torvalds } else
8701da177e4SLinus Torvalds node1 = node1->next;
8711da177e4SLinus Torvalds }
8721da177e4SLinus Torvalds
8731da177e4SLinus Torvalds return 0;
8741da177e4SLinus Torvalds }
8751da177e4SLinus Torvalds
8761da177e4SLinus Torvalds
cpqhp_ctrl_intr(int IRQ,void * data)8777d12e780SDavid Howells irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data)
8781da177e4SLinus Torvalds {
8791da177e4SLinus Torvalds struct controller *ctrl = data;
8801da177e4SLinus Torvalds u8 schedule_flag = 0;
8811da177e4SLinus Torvalds u8 reset;
8821da177e4SLinus Torvalds u16 misc;
8831da177e4SLinus Torvalds u32 Diff;
8841da177e4SLinus Torvalds
8851da177e4SLinus Torvalds
8861da177e4SLinus Torvalds misc = readw(ctrl->hpc_reg + MISC);
887427438c6SAlex Chiang /*
8881da177e4SLinus Torvalds * Check to see if it was our interrupt
889427438c6SAlex Chiang */
890656f978fSQuentin Lambert if (!(misc & 0x000C))
8911da177e4SLinus Torvalds return IRQ_NONE;
8921da177e4SLinus Torvalds
8931da177e4SLinus Torvalds if (misc & 0x0004) {
894427438c6SAlex Chiang /*
8951da177e4SLinus Torvalds * Serial Output interrupt Pending
896427438c6SAlex Chiang */
8971da177e4SLinus Torvalds
8981da177e4SLinus Torvalds /* Clear the interrupt */
8991da177e4SLinus Torvalds misc |= 0x0004;
9001da177e4SLinus Torvalds writew(misc, ctrl->hpc_reg + MISC);
9011da177e4SLinus Torvalds
9021da177e4SLinus Torvalds /* Read to clear posted writes */
9031da177e4SLinus Torvalds misc = readw(ctrl->hpc_reg + MISC);
9041da177e4SLinus Torvalds
90566bef8c0SHarvey Harrison dbg("%s - waking up\n", __func__);
9061da177e4SLinus Torvalds wake_up_interruptible(&ctrl->queue);
9071da177e4SLinus Torvalds }
9081da177e4SLinus Torvalds
9091da177e4SLinus Torvalds if (misc & 0x0008) {
9101da177e4SLinus Torvalds /* General-interrupt-input interrupt Pending */
9111da177e4SLinus Torvalds Diff = readl(ctrl->hpc_reg + INT_INPUT_CLEAR) ^ ctrl->ctrl_int_comp;
9121da177e4SLinus Torvalds
9131da177e4SLinus Torvalds ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
9141da177e4SLinus Torvalds
9151da177e4SLinus Torvalds /* Clear the interrupt */
9161da177e4SLinus Torvalds writel(Diff, ctrl->hpc_reg + INT_INPUT_CLEAR);
9171da177e4SLinus Torvalds
9181da177e4SLinus Torvalds /* Read it back to clear any posted writes */
919af8b8b6cSBjorn Helgaas readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
9201da177e4SLinus Torvalds
9211da177e4SLinus Torvalds if (!Diff)
9221da177e4SLinus Torvalds /* Clear all interrupts */
9231da177e4SLinus Torvalds writel(0xFFFFFFFF, ctrl->hpc_reg + INT_INPUT_CLEAR);
9241da177e4SLinus Torvalds
9251da177e4SLinus Torvalds schedule_flag += handle_switch_change((u8)(Diff & 0xFFL), ctrl);
9261da177e4SLinus Torvalds schedule_flag += handle_presence_change((u16)((Diff & 0xFFFF0000L) >> 16), ctrl);
9271da177e4SLinus Torvalds schedule_flag += handle_power_fault((u8)((Diff & 0xFF00L) >> 8), ctrl);
9281da177e4SLinus Torvalds }
9291da177e4SLinus Torvalds
9301da177e4SLinus Torvalds reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE);
9311da177e4SLinus Torvalds if (reset & 0x40) {
9321da177e4SLinus Torvalds /* Bus reset has completed */
9331da177e4SLinus Torvalds reset &= 0xCF;
9341da177e4SLinus Torvalds writeb(reset, ctrl->hpc_reg + RESET_FREQ_MODE);
9351da177e4SLinus Torvalds reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE);
9361da177e4SLinus Torvalds wake_up_interruptible(&ctrl->queue);
9371da177e4SLinus Torvalds }
9381da177e4SLinus Torvalds
9391da177e4SLinus Torvalds if (schedule_flag) {
940fa007d8bSChristoph Hellwig wake_up_process(cpqhp_event_thread);
941fa007d8bSChristoph Hellwig dbg("Waking even thread");
9421da177e4SLinus Torvalds }
9431da177e4SLinus Torvalds return IRQ_HANDLED;
9441da177e4SLinus Torvalds }
9451da177e4SLinus Torvalds
9461da177e4SLinus Torvalds
9471da177e4SLinus Torvalds /**
9481da177e4SLinus Torvalds * cpqhp_slot_create - Creates a node and adds it to the proper bus.
94926e6c66eSRandy Dunlap * @busnumber: bus where new node is to be located
9501da177e4SLinus Torvalds *
95126e6c66eSRandy Dunlap * Returns pointer to the new node or %NULL if unsuccessful.
9521da177e4SLinus Torvalds */
cpqhp_slot_create(u8 busnumber)9531da177e4SLinus Torvalds struct pci_func *cpqhp_slot_create(u8 busnumber)
9541da177e4SLinus Torvalds {
9551da177e4SLinus Torvalds struct pci_func *new_slot;
9561da177e4SLinus Torvalds struct pci_func *next;
9571da177e4SLinus Torvalds
95873a985a1SMariusz Kozlowski new_slot = kzalloc(sizeof(*new_slot), GFP_KERNEL);
9591d3ecf13SAlex Chiang if (new_slot == NULL)
9601da177e4SLinus Torvalds return new_slot;
9611da177e4SLinus Torvalds
9621da177e4SLinus Torvalds new_slot->next = NULL;
9631da177e4SLinus Torvalds new_slot->configured = 1;
9641da177e4SLinus Torvalds
9651da177e4SLinus Torvalds if (cpqhp_slot_list[busnumber] == NULL) {
9661da177e4SLinus Torvalds cpqhp_slot_list[busnumber] = new_slot;
9671da177e4SLinus Torvalds } else {
9681da177e4SLinus Torvalds next = cpqhp_slot_list[busnumber];
9691da177e4SLinus Torvalds while (next->next != NULL)
9701da177e4SLinus Torvalds next = next->next;
9711da177e4SLinus Torvalds next->next = new_slot;
9721da177e4SLinus Torvalds }
9731da177e4SLinus Torvalds return new_slot;
9741da177e4SLinus Torvalds }
9751da177e4SLinus Torvalds
9761da177e4SLinus Torvalds
9771da177e4SLinus Torvalds /**
9781da177e4SLinus Torvalds * slot_remove - Removes a node from the linked list of slots.
9791da177e4SLinus Torvalds * @old_slot: slot to remove
9801da177e4SLinus Torvalds *
98126e6c66eSRandy Dunlap * Returns %0 if successful, !0 otherwise.
9821da177e4SLinus Torvalds */
slot_remove(struct pci_func * old_slot)9831da177e4SLinus Torvalds static int slot_remove(struct pci_func *old_slot)
9841da177e4SLinus Torvalds {
9851da177e4SLinus Torvalds struct pci_func *next;
9861da177e4SLinus Torvalds
9871da177e4SLinus Torvalds if (old_slot == NULL)
9881da177e4SLinus Torvalds return 1;
9891da177e4SLinus Torvalds
9901da177e4SLinus Torvalds next = cpqhp_slot_list[old_slot->bus];
9911d3ecf13SAlex Chiang if (next == NULL)
9921da177e4SLinus Torvalds return 1;
9931da177e4SLinus Torvalds
9941da177e4SLinus Torvalds if (next == old_slot) {
9951da177e4SLinus Torvalds cpqhp_slot_list[old_slot->bus] = old_slot->next;
9961da177e4SLinus Torvalds cpqhp_destroy_board_resources(old_slot);
9971da177e4SLinus Torvalds kfree(old_slot);
9981da177e4SLinus Torvalds return 0;
9991da177e4SLinus Torvalds }
10001da177e4SLinus Torvalds
10011d3ecf13SAlex Chiang while ((next->next != old_slot) && (next->next != NULL))
10021da177e4SLinus Torvalds next = next->next;
10031da177e4SLinus Torvalds
10041da177e4SLinus Torvalds if (next->next == old_slot) {
10051da177e4SLinus Torvalds next->next = old_slot->next;
10061da177e4SLinus Torvalds cpqhp_destroy_board_resources(old_slot);
10071da177e4SLinus Torvalds kfree(old_slot);
10081da177e4SLinus Torvalds return 0;
10091da177e4SLinus Torvalds } else
10101da177e4SLinus Torvalds return 2;
10111da177e4SLinus Torvalds }
10121da177e4SLinus Torvalds
10131da177e4SLinus Torvalds
10141da177e4SLinus Torvalds /**
10151da177e4SLinus Torvalds * bridge_slot_remove - Removes a node from the linked list of slots.
10161da177e4SLinus Torvalds * @bridge: bridge to remove
10171da177e4SLinus Torvalds *
101826e6c66eSRandy Dunlap * Returns %0 if successful, !0 otherwise.
10191da177e4SLinus Torvalds */
bridge_slot_remove(struct pci_func * bridge)10201da177e4SLinus Torvalds static int bridge_slot_remove(struct pci_func *bridge)
10211da177e4SLinus Torvalds {
10221da177e4SLinus Torvalds u8 subordinateBus, secondaryBus;
10231da177e4SLinus Torvalds u8 tempBus;
10241da177e4SLinus Torvalds struct pci_func *next;
10251da177e4SLinus Torvalds
10261da177e4SLinus Torvalds secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
10271da177e4SLinus Torvalds subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
10281da177e4SLinus Torvalds
10291da177e4SLinus Torvalds for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
10301da177e4SLinus Torvalds next = cpqhp_slot_list[tempBus];
10311da177e4SLinus Torvalds
10321d3ecf13SAlex Chiang while (!slot_remove(next))
10331da177e4SLinus Torvalds next = cpqhp_slot_list[tempBus];
10341da177e4SLinus Torvalds }
10351da177e4SLinus Torvalds
10361da177e4SLinus Torvalds next = cpqhp_slot_list[bridge->bus];
10371da177e4SLinus Torvalds
10381da177e4SLinus Torvalds if (next == NULL)
10391da177e4SLinus Torvalds return 1;
10401da177e4SLinus Torvalds
10411da177e4SLinus Torvalds if (next == bridge) {
10421da177e4SLinus Torvalds cpqhp_slot_list[bridge->bus] = bridge->next;
10431da177e4SLinus Torvalds goto out;
10441da177e4SLinus Torvalds }
10451da177e4SLinus Torvalds
10461da177e4SLinus Torvalds while ((next->next != bridge) && (next->next != NULL))
10471da177e4SLinus Torvalds next = next->next;
10481da177e4SLinus Torvalds
10491da177e4SLinus Torvalds if (next->next != bridge)
10501da177e4SLinus Torvalds return 2;
10511da177e4SLinus Torvalds next->next = bridge->next;
10521da177e4SLinus Torvalds out:
10531da177e4SLinus Torvalds kfree(bridge);
10541da177e4SLinus Torvalds return 0;
10551da177e4SLinus Torvalds }
10561da177e4SLinus Torvalds
10571da177e4SLinus Torvalds
10581da177e4SLinus Torvalds /**
10591da177e4SLinus Torvalds * cpqhp_slot_find - Looks for a node by bus, and device, multiple functions accessed
10601da177e4SLinus Torvalds * @bus: bus to find
10611da177e4SLinus Torvalds * @device: device to find
106226e6c66eSRandy Dunlap * @index: is %0 for first function found, %1 for the second...
10631da177e4SLinus Torvalds *
10641da177e4SLinus Torvalds * Returns pointer to the node if successful, %NULL otherwise.
10651da177e4SLinus Torvalds */
cpqhp_slot_find(u8 bus,u8 device,u8 index)10661da177e4SLinus Torvalds struct pci_func *cpqhp_slot_find(u8 bus, u8 device, u8 index)
10671da177e4SLinus Torvalds {
10681da177e4SLinus Torvalds int found = -1;
10691da177e4SLinus Torvalds struct pci_func *func;
10701da177e4SLinus Torvalds
10711da177e4SLinus Torvalds func = cpqhp_slot_list[bus];
10721da177e4SLinus Torvalds
10731da177e4SLinus Torvalds if ((func == NULL) || ((func->device == device) && (index == 0)))
10741da177e4SLinus Torvalds return func;
10751da177e4SLinus Torvalds
10761da177e4SLinus Torvalds if (func->device == device)
10771da177e4SLinus Torvalds found++;
10781da177e4SLinus Torvalds
10791da177e4SLinus Torvalds while (func->next != NULL) {
10801da177e4SLinus Torvalds func = func->next;
10811da177e4SLinus Torvalds
10821da177e4SLinus Torvalds if (func->device == device)
10831da177e4SLinus Torvalds found++;
10841da177e4SLinus Torvalds
10851da177e4SLinus Torvalds if (found == index)
10861da177e4SLinus Torvalds return func;
10871da177e4SLinus Torvalds }
10881da177e4SLinus Torvalds
10891da177e4SLinus Torvalds return NULL;
10901da177e4SLinus Torvalds }
10911da177e4SLinus Torvalds
10921da177e4SLinus Torvalds
10931da177e4SLinus Torvalds /* DJZ: I don't think is_bridge will work as is.
10941da177e4SLinus Torvalds * FIXME */
is_bridge(struct pci_func * func)10951da177e4SLinus Torvalds static int is_bridge(struct pci_func *func)
10961da177e4SLinus Torvalds {
10971da177e4SLinus Torvalds /* Check the header type */
10981da177e4SLinus Torvalds if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
10991da177e4SLinus Torvalds return 1;
11001da177e4SLinus Torvalds else
11011da177e4SLinus Torvalds return 0;
11021da177e4SLinus Torvalds }
11031da177e4SLinus Torvalds
11041da177e4SLinus Torvalds
11051da177e4SLinus Torvalds /**
110626e6c66eSRandy Dunlap * set_controller_speed - set the frequency and/or mode of a specific controller segment.
11071da177e4SLinus Torvalds * @ctrl: controller to change frequency/mode for.
11081da177e4SLinus Torvalds * @adapter_speed: the speed of the adapter we want to match.
11091da177e4SLinus Torvalds * @hp_slot: the slot number where the adapter is installed.
11101da177e4SLinus Torvalds *
111126e6c66eSRandy Dunlap * Returns %0 if we successfully change frequency and/or mode to match the
11121da177e4SLinus Torvalds * adapter speed.
11131da177e4SLinus Torvalds */
set_controller_speed(struct controller * ctrl,u8 adapter_speed,u8 hp_slot)11141da177e4SLinus Torvalds static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot)
11151da177e4SLinus Torvalds {
11161da177e4SLinus Torvalds struct slot *slot;
11173749c51aSMatthew Wilcox struct pci_bus *bus = ctrl->pci_bus;
11181da177e4SLinus Torvalds u8 reg;
11191da177e4SLinus Torvalds u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
11201da177e4SLinus Torvalds u16 reg16;
11211da177e4SLinus Torvalds u32 leds = readl(ctrl->hpc_reg + LED_CONTROL);
11221da177e4SLinus Torvalds
11233749c51aSMatthew Wilcox if (bus->cur_bus_speed == adapter_speed)
11241da177e4SLinus Torvalds return 0;
11251da177e4SLinus Torvalds
11261da177e4SLinus Torvalds /* We don't allow freq/mode changes if we find another adapter running
1127427438c6SAlex Chiang * in another slot on this controller
1128427438c6SAlex Chiang */
11291da177e4SLinus Torvalds for (slot = ctrl->slot; slot; slot = slot->next) {
11301da177e4SLinus Torvalds if (slot->device == (hp_slot + ctrl->slot_device_offset))
11311da177e4SLinus Torvalds continue;
1132a7da2161SLukas Wunner if (get_presence_status(ctrl, slot) == 0)
11331da177e4SLinus Torvalds continue;
11341da177e4SLinus Torvalds /* If another adapter is running on the same segment but at a
11351da177e4SLinus Torvalds * lower speed/mode, we allow the new adapter to function at
1136427438c6SAlex Chiang * this rate if supported
1137427438c6SAlex Chiang */
11383749c51aSMatthew Wilcox if (bus->cur_bus_speed < adapter_speed)
11391da177e4SLinus Torvalds return 0;
11401da177e4SLinus Torvalds
11411da177e4SLinus Torvalds return 1;
11421da177e4SLinus Torvalds }
11431da177e4SLinus Torvalds
11441da177e4SLinus Torvalds /* If the controller doesn't support freq/mode changes and the
1145427438c6SAlex Chiang * controller is running at a higher mode, we bail
1146427438c6SAlex Chiang */
11473749c51aSMatthew Wilcox if ((bus->cur_bus_speed > adapter_speed) && (!ctrl->pcix_speed_capability))
11481da177e4SLinus Torvalds return 1;
11491da177e4SLinus Torvalds
11501da177e4SLinus Torvalds /* But we allow the adapter to run at a lower rate if possible */
11513749c51aSMatthew Wilcox if ((bus->cur_bus_speed < adapter_speed) && (!ctrl->pcix_speed_capability))
11521da177e4SLinus Torvalds return 0;
11531da177e4SLinus Torvalds
11541da177e4SLinus Torvalds /* We try to set the max speed supported by both the adapter and
1155427438c6SAlex Chiang * controller
1156427438c6SAlex Chiang */
11573749c51aSMatthew Wilcox if (bus->max_bus_speed < adapter_speed) {
11583749c51aSMatthew Wilcox if (bus->cur_bus_speed == bus->max_bus_speed)
11591da177e4SLinus Torvalds return 0;
11603749c51aSMatthew Wilcox adapter_speed = bus->max_bus_speed;
11611da177e4SLinus Torvalds }
11621da177e4SLinus Torvalds
11631da177e4SLinus Torvalds writel(0x0L, ctrl->hpc_reg + LED_CONTROL);
11641da177e4SLinus Torvalds writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE);
11651da177e4SLinus Torvalds
11661da177e4SLinus Torvalds set_SOGO(ctrl);
11671da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
11681da177e4SLinus Torvalds
11691da177e4SLinus Torvalds if (adapter_speed != PCI_SPEED_133MHz_PCIX)
11701da177e4SLinus Torvalds reg = 0xF5;
11711da177e4SLinus Torvalds else
11721da177e4SLinus Torvalds reg = 0xF4;
11731da177e4SLinus Torvalds pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
11741da177e4SLinus Torvalds
11751da177e4SLinus Torvalds reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ);
11761da177e4SLinus Torvalds reg16 &= ~0x000F;
11771da177e4SLinus Torvalds switch (adapter_speed) {
11781da177e4SLinus Torvalds case(PCI_SPEED_133MHz_PCIX):
11791da177e4SLinus Torvalds reg = 0x75;
11801da177e4SLinus Torvalds reg16 |= 0xB;
11811da177e4SLinus Torvalds break;
11821da177e4SLinus Torvalds case(PCI_SPEED_100MHz_PCIX):
11831da177e4SLinus Torvalds reg = 0x74;
11841da177e4SLinus Torvalds reg16 |= 0xA;
11851da177e4SLinus Torvalds break;
11861da177e4SLinus Torvalds case(PCI_SPEED_66MHz_PCIX):
11871da177e4SLinus Torvalds reg = 0x73;
11881da177e4SLinus Torvalds reg16 |= 0x9;
11891da177e4SLinus Torvalds break;
11901da177e4SLinus Torvalds case(PCI_SPEED_66MHz):
11911da177e4SLinus Torvalds reg = 0x73;
11921da177e4SLinus Torvalds reg16 |= 0x1;
11931da177e4SLinus Torvalds break;
11941da177e4SLinus Torvalds default: /* 33MHz PCI 2.2 */
11951da177e4SLinus Torvalds reg = 0x71;
11961da177e4SLinus Torvalds break;
11971da177e4SLinus Torvalds
11981da177e4SLinus Torvalds }
11991da177e4SLinus Torvalds reg16 |= 0xB << 12;
12001da177e4SLinus Torvalds writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ);
12011da177e4SLinus Torvalds
12021da177e4SLinus Torvalds mdelay(5);
12031da177e4SLinus Torvalds
1204b2105b9fSKrzysztof Wilczyński /* Re-enable interrupts */
12051da177e4SLinus Torvalds writel(0, ctrl->hpc_reg + INT_MASK);
12061da177e4SLinus Torvalds
12071da177e4SLinus Torvalds pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
12081da177e4SLinus Torvalds
12091da177e4SLinus Torvalds /* Restart state machine */
12101da177e4SLinus Torvalds reg = ~0xF;
12111da177e4SLinus Torvalds pci_read_config_byte(ctrl->pci_dev, 0x43, ®);
12121da177e4SLinus Torvalds pci_write_config_byte(ctrl->pci_dev, 0x43, reg);
12131da177e4SLinus Torvalds
12141da177e4SLinus Torvalds /* Only if mode change...*/
12153749c51aSMatthew Wilcox if (((bus->cur_bus_speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
12163749c51aSMatthew Wilcox ((bus->cur_bus_speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
12171da177e4SLinus Torvalds set_SOGO(ctrl);
12181da177e4SLinus Torvalds
12191da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
12201da177e4SLinus Torvalds mdelay(1100);
12211da177e4SLinus Torvalds
12221da177e4SLinus Torvalds /* Restore LED/Slot state */
12231da177e4SLinus Torvalds writel(leds, ctrl->hpc_reg + LED_CONTROL);
12241da177e4SLinus Torvalds writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE);
12251da177e4SLinus Torvalds
12261da177e4SLinus Torvalds set_SOGO(ctrl);
12271da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
12281da177e4SLinus Torvalds
12293749c51aSMatthew Wilcox bus->cur_bus_speed = adapter_speed;
12301da177e4SLinus Torvalds slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
12311da177e4SLinus Torvalds
12321da177e4SLinus Torvalds info("Successfully changed frequency/mode for adapter in slot %d\n",
12331da177e4SLinus Torvalds slot->number);
12341da177e4SLinus Torvalds return 0;
12351da177e4SLinus Torvalds }
12361da177e4SLinus Torvalds
12371da177e4SLinus Torvalds /* the following routines constitute the bulk of the
1238427438c6SAlex Chiang * hotplug controller logic
12391da177e4SLinus Torvalds */
12401da177e4SLinus Torvalds
12411da177e4SLinus Torvalds
12421da177e4SLinus Torvalds /**
12431da177e4SLinus Torvalds * board_replaced - Called after a board has been replaced in the system.
124426e6c66eSRandy Dunlap * @func: PCI device/function information
124526e6c66eSRandy Dunlap * @ctrl: hotplug controller
12461da177e4SLinus Torvalds *
124726e6c66eSRandy Dunlap * This is only used if we don't have resources for hot add.
124826e6c66eSRandy Dunlap * Turns power on for the board.
124926e6c66eSRandy Dunlap * Checks to see if board is the same.
125026e6c66eSRandy Dunlap * If board is same, reconfigures it.
12511da177e4SLinus Torvalds * If board isn't same, turns it back off.
12521da177e4SLinus Torvalds */
board_replaced(struct pci_func * func,struct controller * ctrl)12531da177e4SLinus Torvalds static u32 board_replaced(struct pci_func *func, struct controller *ctrl)
12541da177e4SLinus Torvalds {
12553749c51aSMatthew Wilcox struct pci_bus *bus = ctrl->pci_bus;
12561da177e4SLinus Torvalds u8 hp_slot;
12571da177e4SLinus Torvalds u8 temp_byte;
12581da177e4SLinus Torvalds u8 adapter_speed;
12591da177e4SLinus Torvalds u32 rc = 0;
12601da177e4SLinus Torvalds
12611da177e4SLinus Torvalds hp_slot = func->device - ctrl->slot_device_offset;
12621da177e4SLinus Torvalds
1263427438c6SAlex Chiang /*
12641da177e4SLinus Torvalds * The switch is open.
1265427438c6SAlex Chiang */
12661d3ecf13SAlex Chiang if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot))
12671da177e4SLinus Torvalds rc = INTERLOCK_OPEN;
1268427438c6SAlex Chiang /*
12691da177e4SLinus Torvalds * The board is already on
1270427438c6SAlex Chiang */
12711d3ecf13SAlex Chiang else if (is_slot_enabled(ctrl, hp_slot))
12721da177e4SLinus Torvalds rc = CARD_FUNCTIONING;
12731d3ecf13SAlex Chiang else {
12746aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
12751da177e4SLinus Torvalds
12761da177e4SLinus Torvalds /* turn on board without attaching to the bus */
12771da177e4SLinus Torvalds enable_slot_power(ctrl, hp_slot);
12781da177e4SLinus Torvalds
12791da177e4SLinus Torvalds set_SOGO(ctrl);
12801da177e4SLinus Torvalds
12811da177e4SLinus Torvalds /* Wait for SOBS to be unset */
12821da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
12831da177e4SLinus Torvalds
12841da177e4SLinus Torvalds /* Change bits in slot power register to force another shift out
12851da177e4SLinus Torvalds * NOTE: this is to work around the timer bug */
12861da177e4SLinus Torvalds temp_byte = readb(ctrl->hpc_reg + SLOT_POWER);
12871da177e4SLinus Torvalds writeb(0x00, ctrl->hpc_reg + SLOT_POWER);
12881da177e4SLinus Torvalds writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER);
12891da177e4SLinus Torvalds
12901da177e4SLinus Torvalds set_SOGO(ctrl);
12911da177e4SLinus Torvalds
12921da177e4SLinus Torvalds /* Wait for SOBS to be unset */
12931da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
12941da177e4SLinus Torvalds
12951da177e4SLinus Torvalds adapter_speed = get_adapter_speed(ctrl, hp_slot);
12963749c51aSMatthew Wilcox if (bus->cur_bus_speed != adapter_speed)
12971da177e4SLinus Torvalds if (set_controller_speed(ctrl, adapter_speed, hp_slot))
12981da177e4SLinus Torvalds rc = WRONG_BUS_FREQUENCY;
12991da177e4SLinus Torvalds
13001da177e4SLinus Torvalds /* turn off board without attaching to the bus */
13011da177e4SLinus Torvalds disable_slot_power(ctrl, hp_slot);
13021da177e4SLinus Torvalds
13031da177e4SLinus Torvalds set_SOGO(ctrl);
13041da177e4SLinus Torvalds
13051da177e4SLinus Torvalds /* Wait for SOBS to be unset */
13061da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
13071da177e4SLinus Torvalds
13086aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
13091da177e4SLinus Torvalds
13101da177e4SLinus Torvalds if (rc)
13111da177e4SLinus Torvalds return rc;
13121da177e4SLinus Torvalds
13136aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
13141da177e4SLinus Torvalds
13151da177e4SLinus Torvalds slot_enable(ctrl, hp_slot);
13161da177e4SLinus Torvalds green_LED_blink(ctrl, hp_slot);
13171da177e4SLinus Torvalds
13181da177e4SLinus Torvalds amber_LED_off(ctrl, hp_slot);
13191da177e4SLinus Torvalds
13201da177e4SLinus Torvalds set_SOGO(ctrl);
13211da177e4SLinus Torvalds
13221da177e4SLinus Torvalds /* Wait for SOBS to be unset */
13231da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
13241da177e4SLinus Torvalds
13256aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
13261da177e4SLinus Torvalds
13271da177e4SLinus Torvalds /* Wait for ~1 second because of hot plug spec */
13281da177e4SLinus Torvalds long_delay(1*HZ);
13291da177e4SLinus Torvalds
13301da177e4SLinus Torvalds /* Check for a power fault */
13311da177e4SLinus Torvalds if (func->status == 0xFF) {
13321da177e4SLinus Torvalds /* power fault occurred, but it was benign */
13331da177e4SLinus Torvalds rc = POWER_FAILURE;
13341da177e4SLinus Torvalds func->status = 0;
13351da177e4SLinus Torvalds } else
13361da177e4SLinus Torvalds rc = cpqhp_valid_replace(ctrl, func);
13371da177e4SLinus Torvalds
13381da177e4SLinus Torvalds if (!rc) {
13391da177e4SLinus Torvalds /* It must be the same board */
13401da177e4SLinus Torvalds
13411da177e4SLinus Torvalds rc = cpqhp_configure_board(ctrl, func);
13421da177e4SLinus Torvalds
13431da177e4SLinus Torvalds /* If configuration fails, turn it off
13441da177e4SLinus Torvalds * Get slot won't work for devices behind
13451da177e4SLinus Torvalds * bridges, but in this case it will always be
13461da177e4SLinus Torvalds * called for the "base" bus/dev/func of an
1347427438c6SAlex Chiang * adapter.
1348427438c6SAlex Chiang */
13491da177e4SLinus Torvalds
13506aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
13511da177e4SLinus Torvalds
13521da177e4SLinus Torvalds amber_LED_on(ctrl, hp_slot);
13531da177e4SLinus Torvalds green_LED_off(ctrl, hp_slot);
13541da177e4SLinus Torvalds slot_disable(ctrl, hp_slot);
13551da177e4SLinus Torvalds
13561da177e4SLinus Torvalds set_SOGO(ctrl);
13571da177e4SLinus Torvalds
13581da177e4SLinus Torvalds /* Wait for SOBS to be unset */
13591da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
13601da177e4SLinus Torvalds
13616aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
13621da177e4SLinus Torvalds
13631da177e4SLinus Torvalds if (rc)
13641da177e4SLinus Torvalds return rc;
13651da177e4SLinus Torvalds else
13661da177e4SLinus Torvalds return 1;
13671da177e4SLinus Torvalds
13681da177e4SLinus Torvalds } else {
13691da177e4SLinus Torvalds /* Something is wrong
13701da177e4SLinus Torvalds
13711da177e4SLinus Torvalds * Get slot won't work for devices behind bridges, but
13721da177e4SLinus Torvalds * in this case it will always be called for the "base"
1373427438c6SAlex Chiang * bus/dev/func of an adapter.
1374427438c6SAlex Chiang */
13751da177e4SLinus Torvalds
13766aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
13771da177e4SLinus Torvalds
13781da177e4SLinus Torvalds amber_LED_on(ctrl, hp_slot);
13791da177e4SLinus Torvalds green_LED_off(ctrl, hp_slot);
13801da177e4SLinus Torvalds slot_disable(ctrl, hp_slot);
13811da177e4SLinus Torvalds
13821da177e4SLinus Torvalds set_SOGO(ctrl);
13831da177e4SLinus Torvalds
13841da177e4SLinus Torvalds /* Wait for SOBS to be unset */
13851da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
13861da177e4SLinus Torvalds
13876aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
13881da177e4SLinus Torvalds }
13891da177e4SLinus Torvalds
13901da177e4SLinus Torvalds }
13911da177e4SLinus Torvalds return rc;
13921da177e4SLinus Torvalds
13931da177e4SLinus Torvalds }
13941da177e4SLinus Torvalds
13951da177e4SLinus Torvalds
13961da177e4SLinus Torvalds /**
13971da177e4SLinus Torvalds * board_added - Called after a board has been added to the system.
139826e6c66eSRandy Dunlap * @func: PCI device/function info
139926e6c66eSRandy Dunlap * @ctrl: hotplug controller
14001da177e4SLinus Torvalds *
140126e6c66eSRandy Dunlap * Turns power on for the board.
140226e6c66eSRandy Dunlap * Configures board.
14031da177e4SLinus Torvalds */
board_added(struct pci_func * func,struct controller * ctrl)14041da177e4SLinus Torvalds static u32 board_added(struct pci_func *func, struct controller *ctrl)
14051da177e4SLinus Torvalds {
14061da177e4SLinus Torvalds u8 hp_slot;
14071da177e4SLinus Torvalds u8 temp_byte;
14081da177e4SLinus Torvalds u8 adapter_speed;
14091da177e4SLinus Torvalds int index;
14101da177e4SLinus Torvalds u32 temp_register = 0xFFFFFFFF;
14111da177e4SLinus Torvalds u32 rc = 0;
14121da177e4SLinus Torvalds struct pci_func *new_slot = NULL;
14133749c51aSMatthew Wilcox struct pci_bus *bus = ctrl->pci_bus;
14141da177e4SLinus Torvalds struct resource_lists res_lists;
14151da177e4SLinus Torvalds
14161da177e4SLinus Torvalds hp_slot = func->device - ctrl->slot_device_offset;
14171da177e4SLinus Torvalds dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n",
141866bef8c0SHarvey Harrison __func__, func->device, ctrl->slot_device_offset, hp_slot);
14191da177e4SLinus Torvalds
14206aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
14211da177e4SLinus Torvalds
14221da177e4SLinus Torvalds /* turn on board without attaching to the bus */
14231da177e4SLinus Torvalds enable_slot_power(ctrl, hp_slot);
14241da177e4SLinus Torvalds
14251da177e4SLinus Torvalds set_SOGO(ctrl);
14261da177e4SLinus Torvalds
14271da177e4SLinus Torvalds /* Wait for SOBS to be unset */
14281da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
14291da177e4SLinus Torvalds
14301da177e4SLinus Torvalds /* Change bits in slot power register to force another shift out
1431427438c6SAlex Chiang * NOTE: this is to work around the timer bug
1432427438c6SAlex Chiang */
14331da177e4SLinus Torvalds temp_byte = readb(ctrl->hpc_reg + SLOT_POWER);
14341da177e4SLinus Torvalds writeb(0x00, ctrl->hpc_reg + SLOT_POWER);
14351da177e4SLinus Torvalds writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER);
14361da177e4SLinus Torvalds
14371da177e4SLinus Torvalds set_SOGO(ctrl);
14381da177e4SLinus Torvalds
14391da177e4SLinus Torvalds /* Wait for SOBS to be unset */
14401da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
14411da177e4SLinus Torvalds
14421da177e4SLinus Torvalds adapter_speed = get_adapter_speed(ctrl, hp_slot);
14433749c51aSMatthew Wilcox if (bus->cur_bus_speed != adapter_speed)
14441da177e4SLinus Torvalds if (set_controller_speed(ctrl, adapter_speed, hp_slot))
14451da177e4SLinus Torvalds rc = WRONG_BUS_FREQUENCY;
14461da177e4SLinus Torvalds
14471da177e4SLinus Torvalds /* turn off board without attaching to the bus */
14481da177e4SLinus Torvalds disable_slot_power(ctrl, hp_slot);
14491da177e4SLinus Torvalds
14501da177e4SLinus Torvalds set_SOGO(ctrl);
14511da177e4SLinus Torvalds
14521da177e4SLinus Torvalds /* Wait for SOBS to be unset */
14531da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
14541da177e4SLinus Torvalds
14556aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
14561da177e4SLinus Torvalds
14571da177e4SLinus Torvalds if (rc)
14581da177e4SLinus Torvalds return rc;
14591da177e4SLinus Torvalds
1460af8b8b6cSBjorn Helgaas cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
14611da177e4SLinus Torvalds
14621da177e4SLinus Torvalds /* turn on board and blink green LED */
14631da177e4SLinus Torvalds
146466bef8c0SHarvey Harrison dbg("%s: before down\n", __func__);
14656aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
146666bef8c0SHarvey Harrison dbg("%s: after down\n", __func__);
14671da177e4SLinus Torvalds
146866bef8c0SHarvey Harrison dbg("%s: before slot_enable\n", __func__);
14691da177e4SLinus Torvalds slot_enable(ctrl, hp_slot);
14701da177e4SLinus Torvalds
147166bef8c0SHarvey Harrison dbg("%s: before green_LED_blink\n", __func__);
14721da177e4SLinus Torvalds green_LED_blink(ctrl, hp_slot);
14731da177e4SLinus Torvalds
147466bef8c0SHarvey Harrison dbg("%s: before amber_LED_blink\n", __func__);
14751da177e4SLinus Torvalds amber_LED_off(ctrl, hp_slot);
14761da177e4SLinus Torvalds
147766bef8c0SHarvey Harrison dbg("%s: before set_SOGO\n", __func__);
14781da177e4SLinus Torvalds set_SOGO(ctrl);
14791da177e4SLinus Torvalds
14801da177e4SLinus Torvalds /* Wait for SOBS to be unset */
148166bef8c0SHarvey Harrison dbg("%s: before wait_for_ctrl_irq\n", __func__);
14821da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
148366bef8c0SHarvey Harrison dbg("%s: after wait_for_ctrl_irq\n", __func__);
14841da177e4SLinus Torvalds
148566bef8c0SHarvey Harrison dbg("%s: before up\n", __func__);
14866aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
148766bef8c0SHarvey Harrison dbg("%s: after up\n", __func__);
14881da177e4SLinus Torvalds
14891da177e4SLinus Torvalds /* Wait for ~1 second because of hot plug spec */
149066bef8c0SHarvey Harrison dbg("%s: before long_delay\n", __func__);
14911da177e4SLinus Torvalds long_delay(1*HZ);
149266bef8c0SHarvey Harrison dbg("%s: after long_delay\n", __func__);
14931da177e4SLinus Torvalds
149466bef8c0SHarvey Harrison dbg("%s: func status = %x\n", __func__, func->status);
14951da177e4SLinus Torvalds /* Check for a power fault */
14961da177e4SLinus Torvalds if (func->status == 0xFF) {
14971da177e4SLinus Torvalds /* power fault occurred, but it was benign */
14981da177e4SLinus Torvalds temp_register = 0xFFFFFFFF;
149966bef8c0SHarvey Harrison dbg("%s: temp register set to %x by power fault\n", __func__, temp_register);
15001da177e4SLinus Torvalds rc = POWER_FAILURE;
15011da177e4SLinus Torvalds func->status = 0;
15021da177e4SLinus Torvalds } else {
15031da177e4SLinus Torvalds /* Get vendor/device ID u32 */
15041da177e4SLinus Torvalds ctrl->pci_bus->number = func->bus;
15051da177e4SLinus Torvalds rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register);
150666bef8c0SHarvey Harrison dbg("%s: pci_read_config_dword returns %d\n", __func__, rc);
150766bef8c0SHarvey Harrison dbg("%s: temp_register is %x\n", __func__, temp_register);
15081da177e4SLinus Torvalds
15091da177e4SLinus Torvalds if (rc != 0) {
15101da177e4SLinus Torvalds /* Something's wrong here */
15111da177e4SLinus Torvalds temp_register = 0xFFFFFFFF;
151266bef8c0SHarvey Harrison dbg("%s: temp register set to %x by error\n", __func__, temp_register);
15131da177e4SLinus Torvalds }
15141da177e4SLinus Torvalds /* Preset return code. It will be changed later if things go okay. */
15151da177e4SLinus Torvalds rc = NO_ADAPTER_PRESENT;
15161da177e4SLinus Torvalds }
15171da177e4SLinus Torvalds
15181da177e4SLinus Torvalds /* All F's is an empty slot or an invalid board */
15191d3ecf13SAlex Chiang if (temp_register != 0xFFFFFFFF) {
15201da177e4SLinus Torvalds res_lists.io_head = ctrl->io_head;
15211da177e4SLinus Torvalds res_lists.mem_head = ctrl->mem_head;
15221da177e4SLinus Torvalds res_lists.p_mem_head = ctrl->p_mem_head;
15231da177e4SLinus Torvalds res_lists.bus_head = ctrl->bus_head;
15241da177e4SLinus Torvalds res_lists.irqs = NULL;
15251da177e4SLinus Torvalds
15261da177e4SLinus Torvalds rc = configure_new_device(ctrl, func, 0, &res_lists);
15271da177e4SLinus Torvalds
152866bef8c0SHarvey Harrison dbg("%s: back from configure_new_device\n", __func__);
15291da177e4SLinus Torvalds ctrl->io_head = res_lists.io_head;
15301da177e4SLinus Torvalds ctrl->mem_head = res_lists.mem_head;
15311da177e4SLinus Torvalds ctrl->p_mem_head = res_lists.p_mem_head;
15321da177e4SLinus Torvalds ctrl->bus_head = res_lists.bus_head;
15331da177e4SLinus Torvalds
15341da177e4SLinus Torvalds cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
15351da177e4SLinus Torvalds cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
15361da177e4SLinus Torvalds cpqhp_resource_sort_and_combine(&(ctrl->io_head));
15371da177e4SLinus Torvalds cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
15381da177e4SLinus Torvalds
15391da177e4SLinus Torvalds if (rc) {
15406aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
15411da177e4SLinus Torvalds
15421da177e4SLinus Torvalds amber_LED_on(ctrl, hp_slot);
15431da177e4SLinus Torvalds green_LED_off(ctrl, hp_slot);
15441da177e4SLinus Torvalds slot_disable(ctrl, hp_slot);
15451da177e4SLinus Torvalds
15461da177e4SLinus Torvalds set_SOGO(ctrl);
15471da177e4SLinus Torvalds
15481da177e4SLinus Torvalds /* Wait for SOBS to be unset */
15491da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
15501da177e4SLinus Torvalds
15516aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
15521da177e4SLinus Torvalds return rc;
15531da177e4SLinus Torvalds } else {
15541da177e4SLinus Torvalds cpqhp_save_slot_config(ctrl, func);
15551da177e4SLinus Torvalds }
15561da177e4SLinus Torvalds
15571da177e4SLinus Torvalds
15581da177e4SLinus Torvalds func->status = 0;
15591da177e4SLinus Torvalds func->switch_save = 0x10;
15601da177e4SLinus Torvalds func->is_a_board = 0x01;
15611da177e4SLinus Torvalds
15621da177e4SLinus Torvalds /* next, we will instantiate the linux pci_dev structures (with
15631da177e4SLinus Torvalds * appropriate driver notification, if already present) */
156466bef8c0SHarvey Harrison dbg("%s: configure linux pci_dev structure\n", __func__);
15651da177e4SLinus Torvalds index = 0;
15661da177e4SLinus Torvalds do {
15671da177e4SLinus Torvalds new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++);
15681d3ecf13SAlex Chiang if (new_slot && !new_slot->pci_dev)
15691da177e4SLinus Torvalds cpqhp_configure_device(ctrl, new_slot);
15701da177e4SLinus Torvalds } while (new_slot);
15711da177e4SLinus Torvalds
15726aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
15731da177e4SLinus Torvalds
15741da177e4SLinus Torvalds green_LED_on(ctrl, hp_slot);
15751da177e4SLinus Torvalds
15761da177e4SLinus Torvalds set_SOGO(ctrl);
15771da177e4SLinus Torvalds
15781da177e4SLinus Torvalds /* Wait for SOBS to be unset */
15791da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
15801da177e4SLinus Torvalds
15816aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
15821da177e4SLinus Torvalds } else {
15836aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
15841da177e4SLinus Torvalds
15851da177e4SLinus Torvalds amber_LED_on(ctrl, hp_slot);
15861da177e4SLinus Torvalds green_LED_off(ctrl, hp_slot);
15871da177e4SLinus Torvalds slot_disable(ctrl, hp_slot);
15881da177e4SLinus Torvalds
15891da177e4SLinus Torvalds set_SOGO(ctrl);
15901da177e4SLinus Torvalds
15911da177e4SLinus Torvalds /* Wait for SOBS to be unset */
15921da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
15931da177e4SLinus Torvalds
15946aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
15951da177e4SLinus Torvalds
15961da177e4SLinus Torvalds return rc;
15971da177e4SLinus Torvalds }
15981da177e4SLinus Torvalds return 0;
15991da177e4SLinus Torvalds }
16001da177e4SLinus Torvalds
16011da177e4SLinus Torvalds
16021da177e4SLinus Torvalds /**
160326e6c66eSRandy Dunlap * remove_board - Turns off slot and LEDs
160426e6c66eSRandy Dunlap * @func: PCI device/function info
160526e6c66eSRandy Dunlap * @replace_flag: whether replacing or adding a new device
160626e6c66eSRandy Dunlap * @ctrl: target controller
16071da177e4SLinus Torvalds */
remove_board(struct pci_func * func,u32 replace_flag,struct controller * ctrl)16081da177e4SLinus Torvalds static u32 remove_board(struct pci_func *func, u32 replace_flag, struct controller *ctrl)
16091da177e4SLinus Torvalds {
16101da177e4SLinus Torvalds int index;
16111da177e4SLinus Torvalds u8 skip = 0;
16121da177e4SLinus Torvalds u8 device;
16131da177e4SLinus Torvalds u8 hp_slot;
16141da177e4SLinus Torvalds u8 temp_byte;
16151da177e4SLinus Torvalds struct resource_lists res_lists;
16161da177e4SLinus Torvalds struct pci_func *temp_func;
16171da177e4SLinus Torvalds
16181da177e4SLinus Torvalds if (cpqhp_unconfigure_device(func))
16191da177e4SLinus Torvalds return 1;
16201da177e4SLinus Torvalds
16211da177e4SLinus Torvalds device = func->device;
16221da177e4SLinus Torvalds
16231da177e4SLinus Torvalds hp_slot = func->device - ctrl->slot_device_offset;
162466bef8c0SHarvey Harrison dbg("In %s, hp_slot = %d\n", __func__, hp_slot);
16251da177e4SLinus Torvalds
16261da177e4SLinus Torvalds /* When we get here, it is safe to change base address registers.
16271da177e4SLinus Torvalds * We will attempt to save the base address register lengths */
16281da177e4SLinus Torvalds if (replace_flag || !ctrl->add_support)
1629af8b8b6cSBjorn Helgaas cpqhp_save_base_addr_length(ctrl, func);
16301da177e4SLinus Torvalds else if (!func->bus_head && !func->mem_head &&
16311da177e4SLinus Torvalds !func->p_mem_head && !func->io_head) {
16321da177e4SLinus Torvalds /* Here we check to see if we've saved any of the board's
16331da177e4SLinus Torvalds * resources already. If so, we'll skip the attempt to
16341da177e4SLinus Torvalds * determine what's being used. */
16351da177e4SLinus Torvalds index = 0;
16361da177e4SLinus Torvalds temp_func = cpqhp_slot_find(func->bus, func->device, index++);
16371da177e4SLinus Torvalds while (temp_func) {
16381da177e4SLinus Torvalds if (temp_func->bus_head || temp_func->mem_head
16391da177e4SLinus Torvalds || temp_func->p_mem_head || temp_func->io_head) {
16401da177e4SLinus Torvalds skip = 1;
16411da177e4SLinus Torvalds break;
16421da177e4SLinus Torvalds }
16431da177e4SLinus Torvalds temp_func = cpqhp_slot_find(temp_func->bus, temp_func->device, index++);
16441da177e4SLinus Torvalds }
16451da177e4SLinus Torvalds
16461da177e4SLinus Torvalds if (!skip)
1647af8b8b6cSBjorn Helgaas cpqhp_save_used_resources(ctrl, func);
16481da177e4SLinus Torvalds }
16491da177e4SLinus Torvalds /* Change status to shutdown */
16501da177e4SLinus Torvalds if (func->is_a_board)
16511da177e4SLinus Torvalds func->status = 0x01;
16521da177e4SLinus Torvalds func->configured = 0;
16531da177e4SLinus Torvalds
16546aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
16551da177e4SLinus Torvalds
16561da177e4SLinus Torvalds green_LED_off(ctrl, hp_slot);
16571da177e4SLinus Torvalds slot_disable(ctrl, hp_slot);
16581da177e4SLinus Torvalds
16591da177e4SLinus Torvalds set_SOGO(ctrl);
16601da177e4SLinus Torvalds
16611da177e4SLinus Torvalds /* turn off SERR for slot */
16621da177e4SLinus Torvalds temp_byte = readb(ctrl->hpc_reg + SLOT_SERR);
16631da177e4SLinus Torvalds temp_byte &= ~(0x01 << hp_slot);
16641da177e4SLinus Torvalds writeb(temp_byte, ctrl->hpc_reg + SLOT_SERR);
16651da177e4SLinus Torvalds
16661da177e4SLinus Torvalds /* Wait for SOBS to be unset */
16671da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
16681da177e4SLinus Torvalds
16696aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
16701da177e4SLinus Torvalds
16711da177e4SLinus Torvalds if (!replace_flag && ctrl->add_support) {
16721da177e4SLinus Torvalds while (func) {
16731da177e4SLinus Torvalds res_lists.io_head = ctrl->io_head;
16741da177e4SLinus Torvalds res_lists.mem_head = ctrl->mem_head;
16751da177e4SLinus Torvalds res_lists.p_mem_head = ctrl->p_mem_head;
16761da177e4SLinus Torvalds res_lists.bus_head = ctrl->bus_head;
16771da177e4SLinus Torvalds
16781da177e4SLinus Torvalds cpqhp_return_board_resources(func, &res_lists);
16791da177e4SLinus Torvalds
16801da177e4SLinus Torvalds ctrl->io_head = res_lists.io_head;
16811da177e4SLinus Torvalds ctrl->mem_head = res_lists.mem_head;
16821da177e4SLinus Torvalds ctrl->p_mem_head = res_lists.p_mem_head;
16831da177e4SLinus Torvalds ctrl->bus_head = res_lists.bus_head;
16841da177e4SLinus Torvalds
16851da177e4SLinus Torvalds cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
16861da177e4SLinus Torvalds cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
16871da177e4SLinus Torvalds cpqhp_resource_sort_and_combine(&(ctrl->io_head));
16881da177e4SLinus Torvalds cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
16891da177e4SLinus Torvalds
16901da177e4SLinus Torvalds if (is_bridge(func)) {
16911da177e4SLinus Torvalds bridge_slot_remove(func);
16921da177e4SLinus Torvalds } else
16931da177e4SLinus Torvalds slot_remove(func);
16941da177e4SLinus Torvalds
16951da177e4SLinus Torvalds func = cpqhp_slot_find(ctrl->bus, device, 0);
16961da177e4SLinus Torvalds }
16971da177e4SLinus Torvalds
16981da177e4SLinus Torvalds /* Setup slot structure with entry for empty slot */
16991da177e4SLinus Torvalds func = cpqhp_slot_create(ctrl->bus);
17001da177e4SLinus Torvalds
17011da177e4SLinus Torvalds if (func == NULL)
17021da177e4SLinus Torvalds return 1;
17031da177e4SLinus Torvalds
17041da177e4SLinus Torvalds func->bus = ctrl->bus;
17051da177e4SLinus Torvalds func->device = device;
17061da177e4SLinus Torvalds func->function = 0;
17071da177e4SLinus Torvalds func->configured = 0;
17081da177e4SLinus Torvalds func->switch_save = 0x10;
17091da177e4SLinus Torvalds func->is_a_board = 0;
17101da177e4SLinus Torvalds func->p_task_event = NULL;
17111da177e4SLinus Torvalds }
17121da177e4SLinus Torvalds
17131da177e4SLinus Torvalds return 0;
17141da177e4SLinus Torvalds }
17151da177e4SLinus Torvalds
pushbutton_helper_thread(struct timer_list * t)171634d773f6SKees Cook static void pushbutton_helper_thread(struct timer_list *t)
17171da177e4SLinus Torvalds {
171834d773f6SKees Cook pushbutton_pending = t;
171934d773f6SKees Cook
1720fa007d8bSChristoph Hellwig wake_up_process(cpqhp_event_thread);
17211da177e4SLinus Torvalds }
17221da177e4SLinus Torvalds
17231da177e4SLinus Torvalds
17241da177e4SLinus Torvalds /* this is the main worker thread */
event_thread(void * data)17251da177e4SLinus Torvalds static int event_thread(void *data)
17261da177e4SLinus Torvalds {
17271da177e4SLinus Torvalds struct controller *ctrl;
17281da177e4SLinus Torvalds
17291da177e4SLinus Torvalds while (1) {
17301da177e4SLinus Torvalds dbg("!!!!event_thread sleeping\n");
1731fa007d8bSChristoph Hellwig set_current_state(TASK_INTERRUPTIBLE);
1732fa007d8bSChristoph Hellwig schedule();
1733fa007d8bSChristoph Hellwig
1734fa007d8bSChristoph Hellwig if (kthread_should_stop())
1735fa007d8bSChristoph Hellwig break;
17361da177e4SLinus Torvalds /* Do stuff here */
17371da177e4SLinus Torvalds if (pushbutton_pending)
17381da177e4SLinus Torvalds cpqhp_pushbutton_thread(pushbutton_pending);
17391da177e4SLinus Torvalds else
17401da177e4SLinus Torvalds for (ctrl = cpqhp_ctrl_list; ctrl; ctrl = ctrl->next)
17411da177e4SLinus Torvalds interrupt_event_handler(ctrl);
17421da177e4SLinus Torvalds }
17431da177e4SLinus Torvalds dbg("event_thread signals exit\n");
17441da177e4SLinus Torvalds return 0;
17451da177e4SLinus Torvalds }
17461da177e4SLinus Torvalds
cpqhp_event_start_thread(void)17471da177e4SLinus Torvalds int cpqhp_event_start_thread(void)
17481da177e4SLinus Torvalds {
1749fa007d8bSChristoph Hellwig cpqhp_event_thread = kthread_run(event_thread, NULL, "phpd_event");
1750fa007d8bSChristoph Hellwig if (IS_ERR(cpqhp_event_thread)) {
17511da177e4SLinus Torvalds err("Can't start up our event thread\n");
1752fa007d8bSChristoph Hellwig return PTR_ERR(cpqhp_event_thread);
17531da177e4SLinus Torvalds }
1754fa007d8bSChristoph Hellwig
17551da177e4SLinus Torvalds return 0;
17561da177e4SLinus Torvalds }
17571da177e4SLinus Torvalds
17581da177e4SLinus Torvalds
cpqhp_event_stop_thread(void)17591da177e4SLinus Torvalds void cpqhp_event_stop_thread(void)
17601da177e4SLinus Torvalds {
1761fa007d8bSChristoph Hellwig kthread_stop(cpqhp_event_thread);
17621da177e4SLinus Torvalds }
17631da177e4SLinus Torvalds
17641da177e4SLinus Torvalds
interrupt_event_handler(struct controller * ctrl)17651da177e4SLinus Torvalds static void interrupt_event_handler(struct controller *ctrl)
17661da177e4SLinus Torvalds {
1767af8b8b6cSBjorn Helgaas int loop;
17681da177e4SLinus Torvalds int change = 1;
17691da177e4SLinus Torvalds struct pci_func *func;
17701da177e4SLinus Torvalds u8 hp_slot;
17711da177e4SLinus Torvalds struct slot *p_slot;
17721da177e4SLinus Torvalds
17731da177e4SLinus Torvalds while (change) {
17741da177e4SLinus Torvalds change = 0;
17751da177e4SLinus Torvalds
17761da177e4SLinus Torvalds for (loop = 0; loop < 10; loop++) {
17771da177e4SLinus Torvalds /* dbg("loop %d\n", loop); */
17781da177e4SLinus Torvalds if (ctrl->event_queue[loop].event_type != 0) {
17791da177e4SLinus Torvalds hp_slot = ctrl->event_queue[loop].hp_slot;
17801da177e4SLinus Torvalds
17811da177e4SLinus Torvalds func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0);
17821da177e4SLinus Torvalds if (!func)
17831da177e4SLinus Torvalds return;
17841da177e4SLinus Torvalds
17851da177e4SLinus Torvalds p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
17861da177e4SLinus Torvalds if (!p_slot)
17871da177e4SLinus Torvalds return;
17881da177e4SLinus Torvalds
17891da177e4SLinus Torvalds dbg("hp_slot %d, func %p, p_slot %p\n",
17901da177e4SLinus Torvalds hp_slot, func, p_slot);
17911da177e4SLinus Torvalds
17921da177e4SLinus Torvalds if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
17931da177e4SLinus Torvalds dbg("button pressed\n");
17941da177e4SLinus Torvalds } else if (ctrl->event_queue[loop].event_type ==
17951da177e4SLinus Torvalds INT_BUTTON_CANCEL) {
17961da177e4SLinus Torvalds dbg("button cancel\n");
1797*8fa7292fSThomas Gleixner timer_delete(&p_slot->task_event);
17981da177e4SLinus Torvalds
17996aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
18001da177e4SLinus Torvalds
18011da177e4SLinus Torvalds if (p_slot->state == BLINKINGOFF_STATE) {
18021da177e4SLinus Torvalds /* slot is on */
18031da177e4SLinus Torvalds dbg("turn on green LED\n");
18041da177e4SLinus Torvalds green_LED_on(ctrl, hp_slot);
18051da177e4SLinus Torvalds } else if (p_slot->state == BLINKINGON_STATE) {
18061da177e4SLinus Torvalds /* slot is off */
18071da177e4SLinus Torvalds dbg("turn off green LED\n");
18081da177e4SLinus Torvalds green_LED_off(ctrl, hp_slot);
18091da177e4SLinus Torvalds }
18101da177e4SLinus Torvalds
18111da177e4SLinus Torvalds info(msg_button_cancel, p_slot->number);
18121da177e4SLinus Torvalds
18131da177e4SLinus Torvalds p_slot->state = STATIC_STATE;
18141da177e4SLinus Torvalds
18151da177e4SLinus Torvalds amber_LED_off(ctrl, hp_slot);
18161da177e4SLinus Torvalds
18171da177e4SLinus Torvalds set_SOGO(ctrl);
18181da177e4SLinus Torvalds
18191da177e4SLinus Torvalds /* Wait for SOBS to be unset */
18201da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
18211da177e4SLinus Torvalds
18226aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
18231da177e4SLinus Torvalds }
18241da177e4SLinus Torvalds /*** button Released (No action on press...) */
18251da177e4SLinus Torvalds else if (ctrl->event_queue[loop].event_type == INT_BUTTON_RELEASE) {
18261da177e4SLinus Torvalds dbg("button release\n");
18271da177e4SLinus Torvalds
18281da177e4SLinus Torvalds if (is_slot_enabled(ctrl, hp_slot)) {
18291da177e4SLinus Torvalds dbg("slot is on\n");
18301da177e4SLinus Torvalds p_slot->state = BLINKINGOFF_STATE;
18311da177e4SLinus Torvalds info(msg_button_off, p_slot->number);
18321da177e4SLinus Torvalds } else {
18331da177e4SLinus Torvalds dbg("slot is off\n");
18341da177e4SLinus Torvalds p_slot->state = BLINKINGON_STATE;
18351da177e4SLinus Torvalds info(msg_button_on, p_slot->number);
18361da177e4SLinus Torvalds }
18376aa4cdd0SIngo Molnar mutex_lock(&ctrl->crit_sect);
18381da177e4SLinus Torvalds
18391da177e4SLinus Torvalds dbg("blink green LED and turn off amber\n");
18401da177e4SLinus Torvalds
18411da177e4SLinus Torvalds amber_LED_off(ctrl, hp_slot);
18421da177e4SLinus Torvalds green_LED_blink(ctrl, hp_slot);
18431da177e4SLinus Torvalds
18441da177e4SLinus Torvalds set_SOGO(ctrl);
18451da177e4SLinus Torvalds
18461da177e4SLinus Torvalds /* Wait for SOBS to be unset */
18471da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
18481da177e4SLinus Torvalds
18496aa4cdd0SIngo Molnar mutex_unlock(&ctrl->crit_sect);
185034d773f6SKees Cook timer_setup(&p_slot->task_event,
185134d773f6SKees Cook pushbutton_helper_thread,
185234d773f6SKees Cook 0);
18531da177e4SLinus Torvalds p_slot->hp_slot = hp_slot;
18541da177e4SLinus Torvalds p_slot->ctrl = ctrl;
18551da177e4SLinus Torvalds /* p_slot->physical_slot = physical_slot; */
18561da177e4SLinus Torvalds p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */
18571da177e4SLinus Torvalds
18581da177e4SLinus Torvalds dbg("add_timer p_slot = %p\n", p_slot);
18591da177e4SLinus Torvalds add_timer(&p_slot->task_event);
18601da177e4SLinus Torvalds }
18611da177e4SLinus Torvalds /***********POWER FAULT */
18621da177e4SLinus Torvalds else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
18631da177e4SLinus Torvalds dbg("power fault\n");
18641da177e4SLinus Torvalds }
18651da177e4SLinus Torvalds
18661da177e4SLinus Torvalds ctrl->event_queue[loop].event_type = 0;
18671da177e4SLinus Torvalds
18681da177e4SLinus Torvalds change = 1;
18691da177e4SLinus Torvalds }
18701da177e4SLinus Torvalds } /* End of FOR loop */
18711da177e4SLinus Torvalds }
18721da177e4SLinus Torvalds }
18731da177e4SLinus Torvalds
18741da177e4SLinus Torvalds
18751da177e4SLinus Torvalds /**
187626e6c66eSRandy Dunlap * cpqhp_pushbutton_thread - handle pushbutton events
1877347269c1SKrzysztof Wilczyński * @t: pointer to struct timer_list which holds all timer-related callbacks
18781da177e4SLinus Torvalds *
187926e6c66eSRandy Dunlap * Scheduled procedure to handle blocking stuff for the pushbuttons.
18801da177e4SLinus Torvalds * Handles all pending events and exits.
18811da177e4SLinus Torvalds */
cpqhp_pushbutton_thread(struct timer_list * t)188234d773f6SKees Cook void cpqhp_pushbutton_thread(struct timer_list *t)
18831da177e4SLinus Torvalds {
18841da177e4SLinus Torvalds u8 hp_slot;
18851da177e4SLinus Torvalds struct pci_func *func;
188634d773f6SKees Cook struct slot *p_slot = from_timer(p_slot, t, task_event);
18871da177e4SLinus Torvalds struct controller *ctrl = (struct controller *) p_slot->ctrl;
18881da177e4SLinus Torvalds
188934d773f6SKees Cook pushbutton_pending = NULL;
18901da177e4SLinus Torvalds hp_slot = p_slot->hp_slot;
18911da177e4SLinus Torvalds
18921da177e4SLinus Torvalds if (is_slot_enabled(ctrl, hp_slot)) {
18931da177e4SLinus Torvalds p_slot->state = POWEROFF_STATE;
18941da177e4SLinus Torvalds /* power Down board */
18951da177e4SLinus Torvalds func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0);
18961da177e4SLinus Torvalds dbg("In power_down_board, func = %p, ctrl = %p\n", func, ctrl);
18971da177e4SLinus Torvalds if (!func) {
189866bef8c0SHarvey Harrison dbg("Error! func NULL in %s\n", __func__);
18991da177e4SLinus Torvalds return;
19001da177e4SLinus Torvalds }
19011da177e4SLinus Torvalds
19021da177e4SLinus Torvalds if (cpqhp_process_SS(ctrl, func) != 0) {
19031da177e4SLinus Torvalds amber_LED_on(ctrl, hp_slot);
19041da177e4SLinus Torvalds green_LED_on(ctrl, hp_slot);
19051da177e4SLinus Torvalds
19061da177e4SLinus Torvalds set_SOGO(ctrl);
19071da177e4SLinus Torvalds
19081da177e4SLinus Torvalds /* Wait for SOBS to be unset */
19091da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
19101da177e4SLinus Torvalds }
19111da177e4SLinus Torvalds
19121da177e4SLinus Torvalds p_slot->state = STATIC_STATE;
19131da177e4SLinus Torvalds } else {
19141da177e4SLinus Torvalds p_slot->state = POWERON_STATE;
19151da177e4SLinus Torvalds /* slot is off */
19161da177e4SLinus Torvalds
19171da177e4SLinus Torvalds func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0);
19181da177e4SLinus Torvalds dbg("In add_board, func = %p, ctrl = %p\n", func, ctrl);
19191da177e4SLinus Torvalds if (!func) {
192066bef8c0SHarvey Harrison dbg("Error! func NULL in %s\n", __func__);
19211da177e4SLinus Torvalds return;
19221da177e4SLinus Torvalds }
19231da177e4SLinus Torvalds
1924b8d9cb2aSJulia Lawall if (ctrl != NULL) {
19251da177e4SLinus Torvalds if (cpqhp_process_SI(ctrl, func) != 0) {
19261da177e4SLinus Torvalds amber_LED_on(ctrl, hp_slot);
19271da177e4SLinus Torvalds green_LED_off(ctrl, hp_slot);
19281da177e4SLinus Torvalds
19291da177e4SLinus Torvalds set_SOGO(ctrl);
19301da177e4SLinus Torvalds
19311da177e4SLinus Torvalds /* Wait for SOBS to be unset */
19321da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
19331da177e4SLinus Torvalds }
19341da177e4SLinus Torvalds }
19351da177e4SLinus Torvalds
19361da177e4SLinus Torvalds p_slot->state = STATIC_STATE;
19371da177e4SLinus Torvalds }
19381da177e4SLinus Torvalds }
19391da177e4SLinus Torvalds
19401da177e4SLinus Torvalds
cpqhp_process_SI(struct controller * ctrl,struct pci_func * func)19411da177e4SLinus Torvalds int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
19421da177e4SLinus Torvalds {
19431da177e4SLinus Torvalds u8 device, hp_slot;
19441da177e4SLinus Torvalds u16 temp_word;
19451da177e4SLinus Torvalds u32 tempdword;
19461da177e4SLinus Torvalds int rc;
19471da177e4SLinus Torvalds struct slot *p_slot;
19481da177e4SLinus Torvalds
19491da177e4SLinus Torvalds tempdword = 0;
19501da177e4SLinus Torvalds
19511da177e4SLinus Torvalds device = func->device;
19521da177e4SLinus Torvalds hp_slot = device - ctrl->slot_device_offset;
19531da177e4SLinus Torvalds p_slot = cpqhp_find_slot(ctrl, device);
19541da177e4SLinus Torvalds
19551da177e4SLinus Torvalds /* Check to see if the interlock is closed */
19561da177e4SLinus Torvalds tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
19571da177e4SLinus Torvalds
1958656f978fSQuentin Lambert if (tempdword & (0x01 << hp_slot))
19591da177e4SLinus Torvalds return 1;
19601da177e4SLinus Torvalds
19611da177e4SLinus Torvalds if (func->is_a_board) {
19621da177e4SLinus Torvalds rc = board_replaced(func, ctrl);
19631da177e4SLinus Torvalds } else {
19641da177e4SLinus Torvalds /* add board */
19651da177e4SLinus Torvalds slot_remove(func);
19661da177e4SLinus Torvalds
19671da177e4SLinus Torvalds func = cpqhp_slot_create(ctrl->bus);
19681da177e4SLinus Torvalds if (func == NULL)
19691da177e4SLinus Torvalds return 1;
19701da177e4SLinus Torvalds
19711da177e4SLinus Torvalds func->bus = ctrl->bus;
19721da177e4SLinus Torvalds func->device = device;
19731da177e4SLinus Torvalds func->function = 0;
19741da177e4SLinus Torvalds func->configured = 0;
19751da177e4SLinus Torvalds func->is_a_board = 1;
19761da177e4SLinus Torvalds
19771da177e4SLinus Torvalds /* We have to save the presence info for these slots */
19781da177e4SLinus Torvalds temp_word = ctrl->ctrl_int_comp >> 16;
19791da177e4SLinus Torvalds func->presence_save = (temp_word >> hp_slot) & 0x01;
19801da177e4SLinus Torvalds func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
19811da177e4SLinus Torvalds
19821da177e4SLinus Torvalds if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
19831da177e4SLinus Torvalds func->switch_save = 0;
19841da177e4SLinus Torvalds } else {
19851da177e4SLinus Torvalds func->switch_save = 0x10;
19861da177e4SLinus Torvalds }
19871da177e4SLinus Torvalds
19881da177e4SLinus Torvalds rc = board_added(func, ctrl);
19891da177e4SLinus Torvalds if (rc) {
19901da177e4SLinus Torvalds if (is_bridge(func)) {
19911da177e4SLinus Torvalds bridge_slot_remove(func);
19921da177e4SLinus Torvalds } else
19931da177e4SLinus Torvalds slot_remove(func);
19941da177e4SLinus Torvalds
19951da177e4SLinus Torvalds /* Setup slot structure with entry for empty slot */
19961da177e4SLinus Torvalds func = cpqhp_slot_create(ctrl->bus);
19971da177e4SLinus Torvalds
19981da177e4SLinus Torvalds if (func == NULL)
19991da177e4SLinus Torvalds return 1;
20001da177e4SLinus Torvalds
20011da177e4SLinus Torvalds func->bus = ctrl->bus;
20021da177e4SLinus Torvalds func->device = device;
20031da177e4SLinus Torvalds func->function = 0;
20041da177e4SLinus Torvalds func->configured = 0;
20051da177e4SLinus Torvalds func->is_a_board = 0;
20061da177e4SLinus Torvalds
20071da177e4SLinus Torvalds /* We have to save the presence info for these slots */
20081da177e4SLinus Torvalds temp_word = ctrl->ctrl_int_comp >> 16;
20091da177e4SLinus Torvalds func->presence_save = (temp_word >> hp_slot) & 0x01;
20101da177e4SLinus Torvalds func->presence_save |=
20111da177e4SLinus Torvalds (temp_word >> (hp_slot + 7)) & 0x02;
20121da177e4SLinus Torvalds
20131da177e4SLinus Torvalds if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
20141da177e4SLinus Torvalds func->switch_save = 0;
20151da177e4SLinus Torvalds } else {
20161da177e4SLinus Torvalds func->switch_save = 0x10;
20171da177e4SLinus Torvalds }
20181da177e4SLinus Torvalds }
20191da177e4SLinus Torvalds }
20201da177e4SLinus Torvalds
2021656f978fSQuentin Lambert if (rc)
202266bef8c0SHarvey Harrison dbg("%s: rc = %d\n", __func__, rc);
20231da177e4SLinus Torvalds
20241da177e4SLinus Torvalds return rc;
20251da177e4SLinus Torvalds }
20261da177e4SLinus Torvalds
20271da177e4SLinus Torvalds
cpqhp_process_SS(struct controller * ctrl,struct pci_func * func)20281da177e4SLinus Torvalds int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
20291da177e4SLinus Torvalds {
20301da177e4SLinus Torvalds u8 device, class_code, header_type, BCR;
20311da177e4SLinus Torvalds u8 index = 0;
20321da177e4SLinus Torvalds u8 replace_flag;
20331da177e4SLinus Torvalds u32 rc = 0;
20341da177e4SLinus Torvalds unsigned int devfn;
20351da177e4SLinus Torvalds struct slot *p_slot;
20361da177e4SLinus Torvalds struct pci_bus *pci_bus = ctrl->pci_bus;
20371da177e4SLinus Torvalds
20381da177e4SLinus Torvalds device = func->device;
20391da177e4SLinus Torvalds func = cpqhp_slot_find(ctrl->bus, device, index++);
20401da177e4SLinus Torvalds p_slot = cpqhp_find_slot(ctrl, device);
20411da177e4SLinus Torvalds
20421da177e4SLinus Torvalds /* Make sure there are no video controllers here */
20431da177e4SLinus Torvalds while (func && !rc) {
20441da177e4SLinus Torvalds pci_bus->number = func->bus;
20451da177e4SLinus Torvalds devfn = PCI_DEVFN(func->device, func->function);
20461da177e4SLinus Torvalds
20471da177e4SLinus Torvalds /* Check the Class Code */
20481da177e4SLinus Torvalds rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code);
20491da177e4SLinus Torvalds if (rc)
20501da177e4SLinus Torvalds return rc;
20511da177e4SLinus Torvalds
20521da177e4SLinus Torvalds if (class_code == PCI_BASE_CLASS_DISPLAY) {
20531da177e4SLinus Torvalds /* Display/Video adapter (not supported) */
20541da177e4SLinus Torvalds rc = REMOVE_NOT_SUPPORTED;
20551da177e4SLinus Torvalds } else {
20561da177e4SLinus Torvalds /* See if it's a bridge */
20571da177e4SLinus Torvalds rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
20581da177e4SLinus Torvalds if (rc)
20591da177e4SLinus Torvalds return rc;
20601da177e4SLinus Torvalds
20611da177e4SLinus Torvalds /* If it's a bridge, check the VGA Enable bit */
206283c08814SIlpo Järvinen if ((header_type & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
20631da177e4SLinus Torvalds rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR);
20641da177e4SLinus Torvalds if (rc)
20651da177e4SLinus Torvalds return rc;
20661da177e4SLinus Torvalds
20671da177e4SLinus Torvalds /* If the VGA Enable bit is set, remove isn't
20681da177e4SLinus Torvalds * supported */
20691d3ecf13SAlex Chiang if (BCR & PCI_BRIDGE_CTL_VGA)
20701da177e4SLinus Torvalds rc = REMOVE_NOT_SUPPORTED;
20711da177e4SLinus Torvalds }
20721da177e4SLinus Torvalds }
20731da177e4SLinus Torvalds
20741da177e4SLinus Torvalds func = cpqhp_slot_find(ctrl->bus, device, index++);
20751da177e4SLinus Torvalds }
20761da177e4SLinus Torvalds
20771da177e4SLinus Torvalds func = cpqhp_slot_find(ctrl->bus, device, 0);
20781da177e4SLinus Torvalds if ((func != NULL) && !rc) {
20791da177e4SLinus Torvalds /* FIXME: Replace flag should be passed into process_SS */
20801da177e4SLinus Torvalds replace_flag = !(ctrl->add_support);
20811da177e4SLinus Torvalds rc = remove_board(func, replace_flag, ctrl);
20821da177e4SLinus Torvalds } else if (!rc) {
20831da177e4SLinus Torvalds rc = 1;
20841da177e4SLinus Torvalds }
20851da177e4SLinus Torvalds
20861da177e4SLinus Torvalds return rc;
20871da177e4SLinus Torvalds }
20881da177e4SLinus Torvalds
20891da177e4SLinus Torvalds /**
209026e6c66eSRandy Dunlap * switch_leds - switch the leds, go from one site to the other.
20911da177e4SLinus Torvalds * @ctrl: controller to use
20921da177e4SLinus Torvalds * @num_of_slots: number of slots to use
209326e6c66eSRandy Dunlap * @work_LED: LED control value
20941da177e4SLinus Torvalds * @direction: 1 to start from the left side, 0 to start right.
20951da177e4SLinus Torvalds */
switch_leds(struct controller * ctrl,const int num_of_slots,u32 * work_LED,const int direction)20961da177e4SLinus Torvalds static void switch_leds(struct controller *ctrl, const int num_of_slots,
20971da177e4SLinus Torvalds u32 *work_LED, const int direction)
20981da177e4SLinus Torvalds {
20991da177e4SLinus Torvalds int loop;
21001da177e4SLinus Torvalds
21011da177e4SLinus Torvalds for (loop = 0; loop < num_of_slots; loop++) {
21021da177e4SLinus Torvalds if (direction)
21031da177e4SLinus Torvalds *work_LED = *work_LED >> 1;
21041da177e4SLinus Torvalds else
21051da177e4SLinus Torvalds *work_LED = *work_LED << 1;
21061da177e4SLinus Torvalds writel(*work_LED, ctrl->hpc_reg + LED_CONTROL);
21071da177e4SLinus Torvalds
21081da177e4SLinus Torvalds set_SOGO(ctrl);
21091da177e4SLinus Torvalds
21101da177e4SLinus Torvalds /* Wait for SOGO interrupt */
21111da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
21121da177e4SLinus Torvalds
21131da177e4SLinus Torvalds /* Get ready for next iteration */
21141da177e4SLinus Torvalds long_delay((2*HZ)/10);
21151da177e4SLinus Torvalds }
21161da177e4SLinus Torvalds }
21171da177e4SLinus Torvalds
21181da177e4SLinus Torvalds /**
211926e6c66eSRandy Dunlap * cpqhp_hardware_test - runs hardware tests
212026e6c66eSRandy Dunlap * @ctrl: target controller
212126e6c66eSRandy Dunlap * @test_num: the number written to the "test" file in sysfs.
21221da177e4SLinus Torvalds *
21231da177e4SLinus Torvalds * For hot plug ctrl folks to play with.
21241da177e4SLinus Torvalds */
cpqhp_hardware_test(struct controller * ctrl,int test_num)21251da177e4SLinus Torvalds int cpqhp_hardware_test(struct controller *ctrl, int test_num)
21261da177e4SLinus Torvalds {
21271da177e4SLinus Torvalds u32 save_LED;
21281da177e4SLinus Torvalds u32 work_LED;
21291da177e4SLinus Torvalds int loop;
21301da177e4SLinus Torvalds int num_of_slots;
21311da177e4SLinus Torvalds
21321da177e4SLinus Torvalds num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0f;
21331da177e4SLinus Torvalds
21341da177e4SLinus Torvalds switch (test_num) {
21351da177e4SLinus Torvalds case 1:
21361da177e4SLinus Torvalds /* Do stuff here! */
21371da177e4SLinus Torvalds
21381da177e4SLinus Torvalds /* Do that funky LED thing */
21391da177e4SLinus Torvalds /* so we can restore them later */
21401da177e4SLinus Torvalds save_LED = readl(ctrl->hpc_reg + LED_CONTROL);
21411da177e4SLinus Torvalds work_LED = 0x01010101;
21421da177e4SLinus Torvalds switch_leds(ctrl, num_of_slots, &work_LED, 0);
21431da177e4SLinus Torvalds switch_leds(ctrl, num_of_slots, &work_LED, 1);
21441da177e4SLinus Torvalds switch_leds(ctrl, num_of_slots, &work_LED, 0);
21451da177e4SLinus Torvalds switch_leds(ctrl, num_of_slots, &work_LED, 1);
21461da177e4SLinus Torvalds
21471da177e4SLinus Torvalds work_LED = 0x01010000;
21481da177e4SLinus Torvalds writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
21491da177e4SLinus Torvalds switch_leds(ctrl, num_of_slots, &work_LED, 0);
21501da177e4SLinus Torvalds switch_leds(ctrl, num_of_slots, &work_LED, 1);
21511da177e4SLinus Torvalds work_LED = 0x00000101;
21521da177e4SLinus Torvalds writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
21531da177e4SLinus Torvalds switch_leds(ctrl, num_of_slots, &work_LED, 0);
21541da177e4SLinus Torvalds switch_leds(ctrl, num_of_slots, &work_LED, 1);
21551da177e4SLinus Torvalds
21561da177e4SLinus Torvalds work_LED = 0x01010000;
21571da177e4SLinus Torvalds writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
21581da177e4SLinus Torvalds for (loop = 0; loop < num_of_slots; loop++) {
21591da177e4SLinus Torvalds set_SOGO(ctrl);
21601da177e4SLinus Torvalds
21611da177e4SLinus Torvalds /* Wait for SOGO interrupt */
21621da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
21631da177e4SLinus Torvalds
21641da177e4SLinus Torvalds /* Get ready for next iteration */
21651da177e4SLinus Torvalds long_delay((3*HZ)/10);
21661da177e4SLinus Torvalds work_LED = work_LED >> 16;
21671da177e4SLinus Torvalds writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
21681da177e4SLinus Torvalds
21691da177e4SLinus Torvalds set_SOGO(ctrl);
21701da177e4SLinus Torvalds
21711da177e4SLinus Torvalds /* Wait for SOGO interrupt */
21721da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
21731da177e4SLinus Torvalds
21741da177e4SLinus Torvalds /* Get ready for next iteration */
21751da177e4SLinus Torvalds long_delay((3*HZ)/10);
21761da177e4SLinus Torvalds work_LED = work_LED << 16;
21771da177e4SLinus Torvalds writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
21781da177e4SLinus Torvalds work_LED = work_LED << 1;
21791da177e4SLinus Torvalds writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
21801da177e4SLinus Torvalds }
21811da177e4SLinus Torvalds
21821da177e4SLinus Torvalds /* put it back the way it was */
21831da177e4SLinus Torvalds writel(save_LED, ctrl->hpc_reg + LED_CONTROL);
21841da177e4SLinus Torvalds
21851da177e4SLinus Torvalds set_SOGO(ctrl);
21861da177e4SLinus Torvalds
21871da177e4SLinus Torvalds /* Wait for SOBS to be unset */
21881da177e4SLinus Torvalds wait_for_ctrl_irq(ctrl);
21891da177e4SLinus Torvalds break;
21901da177e4SLinus Torvalds case 2:
21911da177e4SLinus Torvalds /* Do other stuff here! */
21921da177e4SLinus Torvalds break;
21931da177e4SLinus Torvalds case 3:
21941da177e4SLinus Torvalds /* and more... */
21951da177e4SLinus Torvalds break;
21961da177e4SLinus Torvalds }
21971da177e4SLinus Torvalds return 0;
21981da177e4SLinus Torvalds }
21991da177e4SLinus Torvalds
22001da177e4SLinus Torvalds
22011da177e4SLinus Torvalds /**
22021da177e4SLinus Torvalds * configure_new_device - Configures the PCI header information of one board.
22031da177e4SLinus Torvalds * @ctrl: pointer to controller structure
22041da177e4SLinus Torvalds * @func: pointer to function structure
22051da177e4SLinus Torvalds * @behind_bridge: 1 if this is a recursive call, 0 if not
22061da177e4SLinus Torvalds * @resources: pointer to set of resource lists
22071da177e4SLinus Torvalds *
220826e6c66eSRandy Dunlap * Returns 0 if success.
22091da177e4SLinus Torvalds */
configure_new_device(struct controller * ctrl,struct pci_func * func,u8 behind_bridge,struct resource_lists * resources)22101da177e4SLinus Torvalds static u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
22111da177e4SLinus Torvalds u8 behind_bridge, struct resource_lists *resources)
22121da177e4SLinus Torvalds {
22131da177e4SLinus Torvalds u8 temp_byte, function, max_functions, stop_it;
22141da177e4SLinus Torvalds int rc;
22151da177e4SLinus Torvalds u32 ID;
22161da177e4SLinus Torvalds struct pci_func *new_slot;
22171da177e4SLinus Torvalds int index;
22181da177e4SLinus Torvalds
22191da177e4SLinus Torvalds new_slot = func;
22201da177e4SLinus Torvalds
222166bef8c0SHarvey Harrison dbg("%s\n", __func__);
22221da177e4SLinus Torvalds /* Check for Multi-function device */
22231da177e4SLinus Torvalds ctrl->pci_bus->number = func->bus;
22241da177e4SLinus Torvalds rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
22251da177e4SLinus Torvalds if (rc) {
222666bef8c0SHarvey Harrison dbg("%s: rc = %d\n", __func__, rc);
22271da177e4SLinus Torvalds return rc;
22281da177e4SLinus Torvalds }
22291da177e4SLinus Torvalds
22301da177e4SLinus Torvalds if (temp_byte & 0x80) /* Multi-function device */
22311da177e4SLinus Torvalds max_functions = 8;
22321da177e4SLinus Torvalds else
22331da177e4SLinus Torvalds max_functions = 1;
22341da177e4SLinus Torvalds
22351da177e4SLinus Torvalds function = 0;
22361da177e4SLinus Torvalds
22371da177e4SLinus Torvalds do {
22381da177e4SLinus Torvalds rc = configure_new_function(ctrl, new_slot, behind_bridge, resources);
22391da177e4SLinus Torvalds
22401da177e4SLinus Torvalds if (rc) {
22411da177e4SLinus Torvalds dbg("configure_new_function failed %d\n", rc);
22421da177e4SLinus Torvalds index = 0;
22431da177e4SLinus Torvalds
22441da177e4SLinus Torvalds while (new_slot) {
22451da177e4SLinus Torvalds new_slot = cpqhp_slot_find(new_slot->bus, new_slot->device, index++);
22461da177e4SLinus Torvalds
22471da177e4SLinus Torvalds if (new_slot)
22481da177e4SLinus Torvalds cpqhp_return_board_resources(new_slot, resources);
22491da177e4SLinus Torvalds }
22501da177e4SLinus Torvalds
22511da177e4SLinus Torvalds return rc;
22521da177e4SLinus Torvalds }
22531da177e4SLinus Torvalds
22541da177e4SLinus Torvalds function++;
22551da177e4SLinus Torvalds
22561da177e4SLinus Torvalds stop_it = 0;
22571da177e4SLinus Torvalds
22581da177e4SLinus Torvalds /* The following loop skips to the next present function
22591da177e4SLinus Torvalds * and creates a board structure */
22601da177e4SLinus Torvalds
22611da177e4SLinus Torvalds while ((function < max_functions) && (!stop_it)) {
22621da177e4SLinus Torvalds pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
22631da177e4SLinus Torvalds
2264a18a025cSNaveen Naidu if (PCI_POSSIBLE_ERROR(ID)) {
22651da177e4SLinus Torvalds function++;
22661d3ecf13SAlex Chiang } else {
22671da177e4SLinus Torvalds /* Setup slot structure. */
22681da177e4SLinus Torvalds new_slot = cpqhp_slot_create(func->bus);
22691da177e4SLinus Torvalds
22701da177e4SLinus Torvalds if (new_slot == NULL)
22711da177e4SLinus Torvalds return 1;
22721da177e4SLinus Torvalds
22731da177e4SLinus Torvalds new_slot->bus = func->bus;
22741da177e4SLinus Torvalds new_slot->device = func->device;
22751da177e4SLinus Torvalds new_slot->function = function;
22761da177e4SLinus Torvalds new_slot->is_a_board = 1;
22771da177e4SLinus Torvalds new_slot->status = 0;
22781da177e4SLinus Torvalds
22791da177e4SLinus Torvalds stop_it++;
22801da177e4SLinus Torvalds }
22811da177e4SLinus Torvalds }
22821da177e4SLinus Torvalds
22831da177e4SLinus Torvalds } while (function < max_functions);
22841da177e4SLinus Torvalds dbg("returning from configure_new_device\n");
22851da177e4SLinus Torvalds
22861da177e4SLinus Torvalds return 0;
22871da177e4SLinus Torvalds }
22881da177e4SLinus Torvalds
22891da177e4SLinus Torvalds
22901da177e4SLinus Torvalds /*
22911d3ecf13SAlex Chiang * Configuration logic that involves the hotplug data structures and
22921d3ecf13SAlex Chiang * their bookkeeping
22931da177e4SLinus Torvalds */
22941da177e4SLinus Torvalds
22951da177e4SLinus Torvalds
22961da177e4SLinus Torvalds /**
22971da177e4SLinus Torvalds * configure_new_function - Configures the PCI header information of one device
22981da177e4SLinus Torvalds * @ctrl: pointer to controller structure
22991da177e4SLinus Torvalds * @func: pointer to function structure
23001da177e4SLinus Torvalds * @behind_bridge: 1 if this is a recursive call, 0 if not
23011da177e4SLinus Torvalds * @resources: pointer to set of resource lists
23021da177e4SLinus Torvalds *
23031da177e4SLinus Torvalds * Calls itself recursively for bridged devices.
230426e6c66eSRandy Dunlap * Returns 0 if success.
23051da177e4SLinus Torvalds */
configure_new_function(struct controller * ctrl,struct pci_func * func,u8 behind_bridge,struct resource_lists * resources)23061da177e4SLinus Torvalds static int configure_new_function(struct controller *ctrl, struct pci_func *func,
23071da177e4SLinus Torvalds u8 behind_bridge,
23081da177e4SLinus Torvalds struct resource_lists *resources)
23091da177e4SLinus Torvalds {
23101da177e4SLinus Torvalds int cloop;
23111da177e4SLinus Torvalds u8 IRQ = 0;
23121da177e4SLinus Torvalds u8 temp_byte;
23131da177e4SLinus Torvalds u8 device;
23141da177e4SLinus Torvalds u8 class_code;
23151da177e4SLinus Torvalds u16 command;
23161da177e4SLinus Torvalds u16 temp_word;
23171da177e4SLinus Torvalds u32 temp_dword;
23181da177e4SLinus Torvalds u32 rc;
23191da177e4SLinus Torvalds u32 temp_register;
23201da177e4SLinus Torvalds u32 base;
23211da177e4SLinus Torvalds u32 ID;
23221da177e4SLinus Torvalds unsigned int devfn;
23231da177e4SLinus Torvalds struct pci_resource *mem_node;
23241da177e4SLinus Torvalds struct pci_resource *p_mem_node;
23251da177e4SLinus Torvalds struct pci_resource *io_node;
23261da177e4SLinus Torvalds struct pci_resource *bus_node;
23271da177e4SLinus Torvalds struct pci_resource *hold_mem_node;
23281da177e4SLinus Torvalds struct pci_resource *hold_p_mem_node;
23291da177e4SLinus Torvalds struct pci_resource *hold_IO_node;
23301da177e4SLinus Torvalds struct pci_resource *hold_bus_node;
23311da177e4SLinus Torvalds struct irq_mapping irqs;
23321da177e4SLinus Torvalds struct pci_func *new_slot;
23331da177e4SLinus Torvalds struct pci_bus *pci_bus;
23341da177e4SLinus Torvalds struct resource_lists temp_resources;
23351da177e4SLinus Torvalds
23361da177e4SLinus Torvalds pci_bus = ctrl->pci_bus;
23371da177e4SLinus Torvalds pci_bus->number = func->bus;
23381da177e4SLinus Torvalds devfn = PCI_DEVFN(func->device, func->function);
23391da177e4SLinus Torvalds
23401da177e4SLinus Torvalds /* Check for Bridge */
23411da177e4SLinus Torvalds rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte);
23421da177e4SLinus Torvalds if (rc)
23431da177e4SLinus Torvalds return rc;
23441da177e4SLinus Torvalds
234583c08814SIlpo Järvinen if ((temp_byte & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_BRIDGE) {
23461da177e4SLinus Torvalds /* set Primary bus */
23471da177e4SLinus Torvalds dbg("set Primary bus = %d\n", func->bus);
23481da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
23491da177e4SLinus Torvalds if (rc)
23501da177e4SLinus Torvalds return rc;
23511da177e4SLinus Torvalds
2352f7625980SBjorn Helgaas /* find range of buses to use */
23531da177e4SLinus Torvalds dbg("find ranges of buses to use\n");
23541da177e4SLinus Torvalds bus_node = get_max_resource(&(resources->bus_head), 1);
23551da177e4SLinus Torvalds
2356f7625980SBjorn Helgaas /* If we don't have any buses to allocate, we can't continue */
23571da177e4SLinus Torvalds if (!bus_node)
23581da177e4SLinus Torvalds return -ENOMEM;
23591da177e4SLinus Torvalds
23601da177e4SLinus Torvalds /* set Secondary bus */
23611da177e4SLinus Torvalds temp_byte = bus_node->base;
23621da177e4SLinus Torvalds dbg("set Secondary bus = %d\n", bus_node->base);
23631da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte);
23641da177e4SLinus Torvalds if (rc)
23651da177e4SLinus Torvalds return rc;
23661da177e4SLinus Torvalds
23671da177e4SLinus Torvalds /* set subordinate bus */
23681da177e4SLinus Torvalds temp_byte = bus_node->base + bus_node->length - 1;
23691da177e4SLinus Torvalds dbg("set subordinate bus = %d\n", bus_node->base + bus_node->length - 1);
23701da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
23711da177e4SLinus Torvalds if (rc)
23721da177e4SLinus Torvalds return rc;
23731da177e4SLinus Torvalds
23741da177e4SLinus Torvalds /* set subordinate Latency Timer and base Latency Timer */
23751da177e4SLinus Torvalds temp_byte = 0x40;
23761da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
23771da177e4SLinus Torvalds if (rc)
23781da177e4SLinus Torvalds return rc;
23791da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
23801da177e4SLinus Torvalds if (rc)
23811da177e4SLinus Torvalds return rc;
23821da177e4SLinus Torvalds
23831da177e4SLinus Torvalds /* set Cache Line size */
23841da177e4SLinus Torvalds temp_byte = 0x08;
23851da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
23861da177e4SLinus Torvalds if (rc)
23871da177e4SLinus Torvalds return rc;
23881da177e4SLinus Torvalds
23891da177e4SLinus Torvalds /* Setup the IO, memory, and prefetchable windows */
23901da177e4SLinus Torvalds io_node = get_max_resource(&(resources->io_head), 0x1000);
23911da177e4SLinus Torvalds if (!io_node)
23921da177e4SLinus Torvalds return -ENOMEM;
23931da177e4SLinus Torvalds mem_node = get_max_resource(&(resources->mem_head), 0x100000);
23941da177e4SLinus Torvalds if (!mem_node)
23951da177e4SLinus Torvalds return -ENOMEM;
23961da177e4SLinus Torvalds p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000);
23971da177e4SLinus Torvalds if (!p_mem_node)
23981da177e4SLinus Torvalds return -ENOMEM;
23991da177e4SLinus Torvalds dbg("Setup the IO, memory, and prefetchable windows\n");
24001da177e4SLinus Torvalds dbg("io_node\n");
24011da177e4SLinus Torvalds dbg("(base, len, next) (%x, %x, %p)\n", io_node->base,
24021da177e4SLinus Torvalds io_node->length, io_node->next);
24031da177e4SLinus Torvalds dbg("mem_node\n");
24041da177e4SLinus Torvalds dbg("(base, len, next) (%x, %x, %p)\n", mem_node->base,
24051da177e4SLinus Torvalds mem_node->length, mem_node->next);
24061da177e4SLinus Torvalds dbg("p_mem_node\n");
24071da177e4SLinus Torvalds dbg("(base, len, next) (%x, %x, %p)\n", p_mem_node->base,
24081da177e4SLinus Torvalds p_mem_node->length, p_mem_node->next);
24091da177e4SLinus Torvalds
24101da177e4SLinus Torvalds /* set up the IRQ info */
24111da177e4SLinus Torvalds if (!resources->irqs) {
24121da177e4SLinus Torvalds irqs.barber_pole = 0;
24131da177e4SLinus Torvalds irqs.interrupt[0] = 0;
24141da177e4SLinus Torvalds irqs.interrupt[1] = 0;
24151da177e4SLinus Torvalds irqs.interrupt[2] = 0;
24161da177e4SLinus Torvalds irqs.interrupt[3] = 0;
24171da177e4SLinus Torvalds irqs.valid_INT = 0;
24181da177e4SLinus Torvalds } else {
24191da177e4SLinus Torvalds irqs.barber_pole = resources->irqs->barber_pole;
24201da177e4SLinus Torvalds irqs.interrupt[0] = resources->irqs->interrupt[0];
24211da177e4SLinus Torvalds irqs.interrupt[1] = resources->irqs->interrupt[1];
24221da177e4SLinus Torvalds irqs.interrupt[2] = resources->irqs->interrupt[2];
24231da177e4SLinus Torvalds irqs.interrupt[3] = resources->irqs->interrupt[3];
24241da177e4SLinus Torvalds irqs.valid_INT = resources->irqs->valid_INT;
24251da177e4SLinus Torvalds }
24261da177e4SLinus Torvalds
24271da177e4SLinus Torvalds /* set up resource lists that are now aligned on top and bottom
24281da177e4SLinus Torvalds * for anything behind the bridge. */
24291da177e4SLinus Torvalds temp_resources.bus_head = bus_node;
24301da177e4SLinus Torvalds temp_resources.io_head = io_node;
24311da177e4SLinus Torvalds temp_resources.mem_head = mem_node;
24321da177e4SLinus Torvalds temp_resources.p_mem_head = p_mem_node;
24331da177e4SLinus Torvalds temp_resources.irqs = &irqs;
24341da177e4SLinus Torvalds
24351da177e4SLinus Torvalds /* Make copies of the nodes we are going to pass down so that
2436427438c6SAlex Chiang * if there is a problem,we can just use these to free resources
2437427438c6SAlex Chiang */
24381da177e4SLinus Torvalds hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL);
24391da177e4SLinus Torvalds hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL);
24401da177e4SLinus Torvalds hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL);
24411da177e4SLinus Torvalds hold_p_mem_node = kmalloc(sizeof(*hold_p_mem_node), GFP_KERNEL);
24421da177e4SLinus Torvalds
24431da177e4SLinus Torvalds if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
24441da177e4SLinus Torvalds kfree(hold_bus_node);
24451da177e4SLinus Torvalds kfree(hold_IO_node);
24461da177e4SLinus Torvalds kfree(hold_mem_node);
24471da177e4SLinus Torvalds kfree(hold_p_mem_node);
24481da177e4SLinus Torvalds
24491da177e4SLinus Torvalds return 1;
24501da177e4SLinus Torvalds }
24511da177e4SLinus Torvalds
24521da177e4SLinus Torvalds memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource));
24531da177e4SLinus Torvalds
24541da177e4SLinus Torvalds bus_node->base += 1;
24551da177e4SLinus Torvalds bus_node->length -= 1;
24561da177e4SLinus Torvalds bus_node->next = NULL;
24571da177e4SLinus Torvalds
24581da177e4SLinus Torvalds /* If we have IO resources copy them and fill in the bridge's
24591da177e4SLinus Torvalds * IO range registers */
24601da177e4SLinus Torvalds memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
24611da177e4SLinus Torvalds io_node->next = NULL;
24621da177e4SLinus Torvalds
24631da177e4SLinus Torvalds /* set IO base and Limit registers */
24641da177e4SLinus Torvalds temp_byte = io_node->base >> 8;
24651da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte);
24661da177e4SLinus Torvalds
24671da177e4SLinus Torvalds temp_byte = (io_node->base + io_node->length - 1) >> 8;
24681da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
24691da177e4SLinus Torvalds
24703ecd9d01SSasha Levin /* Copy the memory resources and fill in the bridge's memory
24713ecd9d01SSasha Levin * range registers.
24723ecd9d01SSasha Levin */
24731da177e4SLinus Torvalds memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
24741da177e4SLinus Torvalds mem_node->next = NULL;
24751da177e4SLinus Torvalds
24761da177e4SLinus Torvalds /* set Mem base and Limit registers */
24771da177e4SLinus Torvalds temp_word = mem_node->base >> 16;
24781da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
24791da177e4SLinus Torvalds
24801da177e4SLinus Torvalds temp_word = (mem_node->base + mem_node->length - 1) >> 16;
24811da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
24821da177e4SLinus Torvalds
24831da177e4SLinus Torvalds memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource));
24841da177e4SLinus Torvalds p_mem_node->next = NULL;
24851da177e4SLinus Torvalds
24861da177e4SLinus Torvalds /* set Pre Mem base and Limit registers */
24871da177e4SLinus Torvalds temp_word = p_mem_node->base >> 16;
24881da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
24891da177e4SLinus Torvalds
24901da177e4SLinus Torvalds temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16;
24911da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
24921da177e4SLinus Torvalds
2493427438c6SAlex Chiang /* Adjust this to compensate for extra adjustment in first loop
2494427438c6SAlex Chiang */
24951da177e4SLinus Torvalds irqs.barber_pole--;
24961da177e4SLinus Torvalds
24971da177e4SLinus Torvalds rc = 0;
24981da177e4SLinus Torvalds
24991da177e4SLinus Torvalds /* Here we actually find the devices and configure them */
25001da177e4SLinus Torvalds for (device = 0; (device <= 0x1F) && !rc; device++) {
25011da177e4SLinus Torvalds irqs.barber_pole = (irqs.barber_pole + 1) & 0x03;
25021da177e4SLinus Torvalds
25031da177e4SLinus Torvalds ID = 0xFFFFFFFF;
25041da177e4SLinus Torvalds pci_bus->number = hold_bus_node->base;
25051da177e4SLinus Torvalds pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), 0x00, &ID);
25061da177e4SLinus Torvalds pci_bus->number = func->bus;
25071da177e4SLinus Torvalds
2508a18a025cSNaveen Naidu if (!PCI_POSSIBLE_ERROR(ID)) { /* device present */
25091da177e4SLinus Torvalds /* Setup slot structure. */
25101da177e4SLinus Torvalds new_slot = cpqhp_slot_create(hold_bus_node->base);
25111da177e4SLinus Torvalds
25121da177e4SLinus Torvalds if (new_slot == NULL) {
25131da177e4SLinus Torvalds rc = -ENOMEM;
25141da177e4SLinus Torvalds continue;
25151da177e4SLinus Torvalds }
25161da177e4SLinus Torvalds
25171da177e4SLinus Torvalds new_slot->bus = hold_bus_node->base;
25181da177e4SLinus Torvalds new_slot->device = device;
25191da177e4SLinus Torvalds new_slot->function = 0;
25201da177e4SLinus Torvalds new_slot->is_a_board = 1;
25211da177e4SLinus Torvalds new_slot->status = 0;
25221da177e4SLinus Torvalds
25231da177e4SLinus Torvalds rc = configure_new_device(ctrl, new_slot, 1, &temp_resources);
25241da177e4SLinus Torvalds dbg("configure_new_device rc=0x%x\n", rc);
25251da177e4SLinus Torvalds } /* End of IF (device in slot?) */
25261da177e4SLinus Torvalds } /* End of FOR loop */
25271da177e4SLinus Torvalds
25281da177e4SLinus Torvalds if (rc)
25291da177e4SLinus Torvalds goto free_and_out;
25301da177e4SLinus Torvalds /* save the interrupt routing information */
25311da177e4SLinus Torvalds if (resources->irqs) {
25321da177e4SLinus Torvalds resources->irqs->interrupt[0] = irqs.interrupt[0];
25331da177e4SLinus Torvalds resources->irqs->interrupt[1] = irqs.interrupt[1];
25341da177e4SLinus Torvalds resources->irqs->interrupt[2] = irqs.interrupt[2];
25351da177e4SLinus Torvalds resources->irqs->interrupt[3] = irqs.interrupt[3];
25361da177e4SLinus Torvalds resources->irqs->valid_INT = irqs.valid_INT;
25371da177e4SLinus Torvalds } else if (!behind_bridge) {
25381da177e4SLinus Torvalds /* We need to hook up the interrupts here */
25391da177e4SLinus Torvalds for (cloop = 0; cloop < 4; cloop++) {
25401da177e4SLinus Torvalds if (irqs.valid_INT & (0x01 << cloop)) {
25411da177e4SLinus Torvalds rc = cpqhp_set_irq(func->bus, func->device,
254298d3333aSBjorn Helgaas cloop + 1, irqs.interrupt[cloop]);
25431da177e4SLinus Torvalds if (rc)
25441da177e4SLinus Torvalds goto free_and_out;
25451da177e4SLinus Torvalds }
25461da177e4SLinus Torvalds } /* end of for loop */
25471da177e4SLinus Torvalds }
25481da177e4SLinus Torvalds /* Return unused bus resources
25491da177e4SLinus Torvalds * First use the temporary node to store information for
25501da177e4SLinus Torvalds * the board */
25513ecd9d01SSasha Levin if (bus_node && temp_resources.bus_head) {
25521da177e4SLinus Torvalds hold_bus_node->length = bus_node->base - hold_bus_node->base;
25531da177e4SLinus Torvalds
25541da177e4SLinus Torvalds hold_bus_node->next = func->bus_head;
25551da177e4SLinus Torvalds func->bus_head = hold_bus_node;
25561da177e4SLinus Torvalds
25571da177e4SLinus Torvalds temp_byte = temp_resources.bus_head->base - 1;
25581da177e4SLinus Torvalds
25591da177e4SLinus Torvalds /* set subordinate bus */
25601da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
25611da177e4SLinus Torvalds
25621da177e4SLinus Torvalds if (temp_resources.bus_head->length == 0) {
25631da177e4SLinus Torvalds kfree(temp_resources.bus_head);
25641da177e4SLinus Torvalds temp_resources.bus_head = NULL;
25651da177e4SLinus Torvalds } else {
25661da177e4SLinus Torvalds return_resource(&(resources->bus_head), temp_resources.bus_head);
25671da177e4SLinus Torvalds }
25681da177e4SLinus Torvalds }
25691da177e4SLinus Torvalds
25701da177e4SLinus Torvalds /* If we have IO space available and there is some left,
25711da177e4SLinus Torvalds * return the unused portion */
25721da177e4SLinus Torvalds if (hold_IO_node && temp_resources.io_head) {
25731da177e4SLinus Torvalds io_node = do_pre_bridge_resource_split(&(temp_resources.io_head),
25741da177e4SLinus Torvalds &hold_IO_node, 0x1000);
25751da177e4SLinus Torvalds
25761da177e4SLinus Torvalds /* Check if we were able to split something off */
25771da177e4SLinus Torvalds if (io_node) {
25781da177e4SLinus Torvalds hold_IO_node->base = io_node->base + io_node->length;
25791da177e4SLinus Torvalds
25801da177e4SLinus Torvalds temp_byte = (hold_IO_node->base) >> 8;
25811da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_IO_BASE, temp_byte);
25821da177e4SLinus Torvalds
25831da177e4SLinus Torvalds return_resource(&(resources->io_head), io_node);
25841da177e4SLinus Torvalds }
25851da177e4SLinus Torvalds
25861da177e4SLinus Torvalds io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000);
25871da177e4SLinus Torvalds
25881da177e4SLinus Torvalds /* Check if we were able to split something off */
25891da177e4SLinus Torvalds if (io_node) {
25901da177e4SLinus Torvalds /* First use the temporary node to store
25911da177e4SLinus Torvalds * information for the board */
25921da177e4SLinus Torvalds hold_IO_node->length = io_node->base - hold_IO_node->base;
25931da177e4SLinus Torvalds
25941da177e4SLinus Torvalds /* If we used any, add it to the board's list */
25951da177e4SLinus Torvalds if (hold_IO_node->length) {
25961da177e4SLinus Torvalds hold_IO_node->next = func->io_head;
25971da177e4SLinus Torvalds func->io_head = hold_IO_node;
25981da177e4SLinus Torvalds
25991da177e4SLinus Torvalds temp_byte = (io_node->base - 1) >> 8;
26001da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
26011da177e4SLinus Torvalds
26021da177e4SLinus Torvalds return_resource(&(resources->io_head), io_node);
26031da177e4SLinus Torvalds } else {
26041da177e4SLinus Torvalds /* it doesn't need any IO */
26051da177e4SLinus Torvalds temp_word = 0x0000;
26061da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_IO_LIMIT, temp_word);
26071da177e4SLinus Torvalds
26081da177e4SLinus Torvalds return_resource(&(resources->io_head), io_node);
26091da177e4SLinus Torvalds kfree(hold_IO_node);
26101da177e4SLinus Torvalds }
26111da177e4SLinus Torvalds } else {
26121da177e4SLinus Torvalds /* it used most of the range */
26131da177e4SLinus Torvalds hold_IO_node->next = func->io_head;
26141da177e4SLinus Torvalds func->io_head = hold_IO_node;
26151da177e4SLinus Torvalds }
26161da177e4SLinus Torvalds } else if (hold_IO_node) {
26171da177e4SLinus Torvalds /* it used the whole range */
26181da177e4SLinus Torvalds hold_IO_node->next = func->io_head;
26191da177e4SLinus Torvalds func->io_head = hold_IO_node;
26201da177e4SLinus Torvalds }
26211da177e4SLinus Torvalds /* If we have memory space available and there is some left,
26221da177e4SLinus Torvalds * return the unused portion */
26231da177e4SLinus Torvalds if (hold_mem_node && temp_resources.mem_head) {
26241da177e4SLinus Torvalds mem_node = do_pre_bridge_resource_split(&(temp_resources. mem_head),
26251da177e4SLinus Torvalds &hold_mem_node, 0x100000);
26261da177e4SLinus Torvalds
26271da177e4SLinus Torvalds /* Check if we were able to split something off */
26281da177e4SLinus Torvalds if (mem_node) {
26291da177e4SLinus Torvalds hold_mem_node->base = mem_node->base + mem_node->length;
26301da177e4SLinus Torvalds
26311da177e4SLinus Torvalds temp_word = (hold_mem_node->base) >> 16;
26321da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
26331da177e4SLinus Torvalds
26341da177e4SLinus Torvalds return_resource(&(resources->mem_head), mem_node);
26351da177e4SLinus Torvalds }
26361da177e4SLinus Torvalds
26371da177e4SLinus Torvalds mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000);
26381da177e4SLinus Torvalds
26391da177e4SLinus Torvalds /* Check if we were able to split something off */
26401da177e4SLinus Torvalds if (mem_node) {
26411da177e4SLinus Torvalds /* First use the temporary node to store
26421da177e4SLinus Torvalds * information for the board */
26431da177e4SLinus Torvalds hold_mem_node->length = mem_node->base - hold_mem_node->base;
26441da177e4SLinus Torvalds
26451da177e4SLinus Torvalds if (hold_mem_node->length) {
26461da177e4SLinus Torvalds hold_mem_node->next = func->mem_head;
26471da177e4SLinus Torvalds func->mem_head = hold_mem_node;
26481da177e4SLinus Torvalds
26491da177e4SLinus Torvalds /* configure end address */
26501da177e4SLinus Torvalds temp_word = (mem_node->base - 1) >> 16;
26511da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
26521da177e4SLinus Torvalds
26531da177e4SLinus Torvalds /* Return unused resources to the pool */
26541da177e4SLinus Torvalds return_resource(&(resources->mem_head), mem_node);
26551da177e4SLinus Torvalds } else {
26561da177e4SLinus Torvalds /* it doesn't need any Mem */
26571da177e4SLinus Torvalds temp_word = 0x0000;
26581da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
26591da177e4SLinus Torvalds
26601da177e4SLinus Torvalds return_resource(&(resources->mem_head), mem_node);
26611da177e4SLinus Torvalds kfree(hold_mem_node);
26621da177e4SLinus Torvalds }
26631da177e4SLinus Torvalds } else {
26641da177e4SLinus Torvalds /* it used most of the range */
26651da177e4SLinus Torvalds hold_mem_node->next = func->mem_head;
26661da177e4SLinus Torvalds func->mem_head = hold_mem_node;
26671da177e4SLinus Torvalds }
26681da177e4SLinus Torvalds } else if (hold_mem_node) {
26691da177e4SLinus Torvalds /* it used the whole range */
26701da177e4SLinus Torvalds hold_mem_node->next = func->mem_head;
26711da177e4SLinus Torvalds func->mem_head = hold_mem_node;
26721da177e4SLinus Torvalds }
26731da177e4SLinus Torvalds /* If we have prefetchable memory space available and there
26741da177e4SLinus Torvalds * is some left at the end, return the unused portion */
26753ecd9d01SSasha Levin if (temp_resources.p_mem_head) {
26761da177e4SLinus Torvalds p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
26771da177e4SLinus Torvalds &hold_p_mem_node, 0x100000);
26781da177e4SLinus Torvalds
26791da177e4SLinus Torvalds /* Check if we were able to split something off */
26801da177e4SLinus Torvalds if (p_mem_node) {
26811da177e4SLinus Torvalds hold_p_mem_node->base = p_mem_node->base + p_mem_node->length;
26821da177e4SLinus Torvalds
26831da177e4SLinus Torvalds temp_word = (hold_p_mem_node->base) >> 16;
26841da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
26851da177e4SLinus Torvalds
26861da177e4SLinus Torvalds return_resource(&(resources->p_mem_head), p_mem_node);
26871da177e4SLinus Torvalds }
26881da177e4SLinus Torvalds
26891da177e4SLinus Torvalds p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000);
26901da177e4SLinus Torvalds
26911da177e4SLinus Torvalds /* Check if we were able to split something off */
26921da177e4SLinus Torvalds if (p_mem_node) {
26931da177e4SLinus Torvalds /* First use the temporary node to store
26941da177e4SLinus Torvalds * information for the board */
26951da177e4SLinus Torvalds hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base;
26961da177e4SLinus Torvalds
26971da177e4SLinus Torvalds /* If we used any, add it to the board's list */
26981da177e4SLinus Torvalds if (hold_p_mem_node->length) {
26991da177e4SLinus Torvalds hold_p_mem_node->next = func->p_mem_head;
27001da177e4SLinus Torvalds func->p_mem_head = hold_p_mem_node;
27011da177e4SLinus Torvalds
27021da177e4SLinus Torvalds temp_word = (p_mem_node->base - 1) >> 16;
27031da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
27041da177e4SLinus Torvalds
27051da177e4SLinus Torvalds return_resource(&(resources->p_mem_head), p_mem_node);
27061da177e4SLinus Torvalds } else {
27071da177e4SLinus Torvalds /* it doesn't need any PMem */
27081da177e4SLinus Torvalds temp_word = 0x0000;
27091da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
27101da177e4SLinus Torvalds
27111da177e4SLinus Torvalds return_resource(&(resources->p_mem_head), p_mem_node);
27121da177e4SLinus Torvalds kfree(hold_p_mem_node);
27131da177e4SLinus Torvalds }
27141da177e4SLinus Torvalds } else {
27151da177e4SLinus Torvalds /* it used the most of the range */
27161da177e4SLinus Torvalds hold_p_mem_node->next = func->p_mem_head;
27171da177e4SLinus Torvalds func->p_mem_head = hold_p_mem_node;
27181da177e4SLinus Torvalds }
27191da177e4SLinus Torvalds } else if (hold_p_mem_node) {
27201da177e4SLinus Torvalds /* it used the whole range */
27211da177e4SLinus Torvalds hold_p_mem_node->next = func->p_mem_head;
27221da177e4SLinus Torvalds func->p_mem_head = hold_p_mem_node;
27231da177e4SLinus Torvalds }
27241da177e4SLinus Torvalds /* We should be configuring an IRQ and the bridge's base address
27251da177e4SLinus Torvalds * registers if it needs them. Although we have never seen such
27261da177e4SLinus Torvalds * a device */
27271da177e4SLinus Torvalds
27281da177e4SLinus Torvalds /* enable card */
27291da177e4SLinus Torvalds command = 0x0157; /* = PCI_COMMAND_IO |
27301da177e4SLinus Torvalds * PCI_COMMAND_MEMORY |
27311da177e4SLinus Torvalds * PCI_COMMAND_MASTER |
27321da177e4SLinus Torvalds * PCI_COMMAND_INVALIDATE |
27331da177e4SLinus Torvalds * PCI_COMMAND_PARITY |
27341da177e4SLinus Torvalds * PCI_COMMAND_SERR */
27351da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
27361da177e4SLinus Torvalds
27371da177e4SLinus Torvalds /* set Bridge Control Register */
27381da177e4SLinus Torvalds command = 0x07; /* = PCI_BRIDGE_CTL_PARITY |
27391da177e4SLinus Torvalds * PCI_BRIDGE_CTL_SERR |
27401da177e4SLinus Torvalds * PCI_BRIDGE_CTL_NO_ISA */
27411da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
274283c08814SIlpo Järvinen } else if ((temp_byte & PCI_HEADER_TYPE_MASK) == PCI_HEADER_TYPE_NORMAL) {
27431da177e4SLinus Torvalds /* Standard device */
27441da177e4SLinus Torvalds rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code);
27451da177e4SLinus Torvalds
27461da177e4SLinus Torvalds if (class_code == PCI_BASE_CLASS_DISPLAY) {
27471da177e4SLinus Torvalds /* Display (video) adapter (not supported) */
27481da177e4SLinus Torvalds return DEVICE_TYPE_NOT_SUPPORTED;
27491da177e4SLinus Torvalds }
27501da177e4SLinus Torvalds /* Figure out IO and memory needs */
27511da177e4SLinus Torvalds for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
27521da177e4SLinus Torvalds temp_register = 0xFFFFFFFF;
27531da177e4SLinus Torvalds
27541da177e4SLinus Torvalds dbg("CND: bus=%d, devfn=%d, offset=%d\n", pci_bus->number, devfn, cloop);
27551da177e4SLinus Torvalds rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
27561da177e4SLinus Torvalds
27571da177e4SLinus Torvalds rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register);
27581da177e4SLinus Torvalds dbg("CND: base = 0x%x\n", temp_register);
27591da177e4SLinus Torvalds
27601da177e4SLinus Torvalds if (temp_register) { /* If this register is implemented */
27611da177e4SLinus Torvalds if ((temp_register & 0x03L) == 0x01) {
27621da177e4SLinus Torvalds /* Map IO */
27631da177e4SLinus Torvalds
27641da177e4SLinus Torvalds /* set base = amount of IO space */
27651da177e4SLinus Torvalds base = temp_register & 0xFFFFFFFC;
27661da177e4SLinus Torvalds base = ~base + 1;
27671da177e4SLinus Torvalds
27681da177e4SLinus Torvalds dbg("CND: length = 0x%x\n", base);
27691da177e4SLinus Torvalds io_node = get_io_resource(&(resources->io_head), base);
2770205adda7SShawn Lin if (!io_node)
2771205adda7SShawn Lin return -ENOMEM;
27721da177e4SLinus Torvalds dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n",
27731da177e4SLinus Torvalds io_node->base, io_node->length, io_node->next);
27741da177e4SLinus Torvalds dbg("func (%p) io_head (%p)\n", func, func->io_head);
27751da177e4SLinus Torvalds
27761da177e4SLinus Torvalds /* allocate the resource to the board */
27771da177e4SLinus Torvalds base = io_node->base;
27781da177e4SLinus Torvalds io_node->next = func->io_head;
27791da177e4SLinus Torvalds func->io_head = io_node;
27801da177e4SLinus Torvalds } else if ((temp_register & 0x0BL) == 0x08) {
27811da177e4SLinus Torvalds /* Map prefetchable memory */
27821da177e4SLinus Torvalds base = temp_register & 0xFFFFFFF0;
27831da177e4SLinus Torvalds base = ~base + 1;
27841da177e4SLinus Torvalds
27851da177e4SLinus Torvalds dbg("CND: length = 0x%x\n", base);
27861da177e4SLinus Torvalds p_mem_node = get_resource(&(resources->p_mem_head), base);
27871da177e4SLinus Torvalds
27881da177e4SLinus Torvalds /* allocate the resource to the board */
27891da177e4SLinus Torvalds if (p_mem_node) {
27901da177e4SLinus Torvalds base = p_mem_node->base;
27911da177e4SLinus Torvalds
27921da177e4SLinus Torvalds p_mem_node->next = func->p_mem_head;
27931da177e4SLinus Torvalds func->p_mem_head = p_mem_node;
27941da177e4SLinus Torvalds } else
27951da177e4SLinus Torvalds return -ENOMEM;
27961da177e4SLinus Torvalds } else if ((temp_register & 0x0BL) == 0x00) {
27971da177e4SLinus Torvalds /* Map memory */
27981da177e4SLinus Torvalds base = temp_register & 0xFFFFFFF0;
27991da177e4SLinus Torvalds base = ~base + 1;
28001da177e4SLinus Torvalds
28011da177e4SLinus Torvalds dbg("CND: length = 0x%x\n", base);
28021da177e4SLinus Torvalds mem_node = get_resource(&(resources->mem_head), base);
28031da177e4SLinus Torvalds
28041da177e4SLinus Torvalds /* allocate the resource to the board */
28051da177e4SLinus Torvalds if (mem_node) {
28061da177e4SLinus Torvalds base = mem_node->base;
28071da177e4SLinus Torvalds
28081da177e4SLinus Torvalds mem_node->next = func->mem_head;
28091da177e4SLinus Torvalds func->mem_head = mem_node;
28101da177e4SLinus Torvalds } else
28111da177e4SLinus Torvalds return -ENOMEM;
28121da177e4SLinus Torvalds } else {
2813b161dabcSAlan Cox /* Reserved bits or requesting space below 1M */
28141da177e4SLinus Torvalds return NOT_ENOUGH_RESOURCES;
28151da177e4SLinus Torvalds }
28161da177e4SLinus Torvalds
28171da177e4SLinus Torvalds rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
28181da177e4SLinus Torvalds
28191da177e4SLinus Torvalds /* Check for 64-bit base */
28201da177e4SLinus Torvalds if ((temp_register & 0x07L) == 0x04) {
28211da177e4SLinus Torvalds cloop += 4;
28221da177e4SLinus Torvalds
28231da177e4SLinus Torvalds /* Upper 32 bits of address always zero
28241da177e4SLinus Torvalds * on today's systems */
28251da177e4SLinus Torvalds /* FIXME this is probably not true on
28261da177e4SLinus Torvalds * Alpha and ia64??? */
28271da177e4SLinus Torvalds base = 0;
28281da177e4SLinus Torvalds rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
28291da177e4SLinus Torvalds }
28301da177e4SLinus Torvalds }
28311da177e4SLinus Torvalds } /* End of base register loop */
28321da177e4SLinus Torvalds if (cpqhp_legacy_mode) {
28331da177e4SLinus Torvalds /* Figure out which interrupt pin this function uses */
28341da177e4SLinus Torvalds rc = pci_bus_read_config_byte(pci_bus, devfn,
28351da177e4SLinus Torvalds PCI_INTERRUPT_PIN, &temp_byte);
28361da177e4SLinus Torvalds
28371da177e4SLinus Torvalds /* If this function needs an interrupt and we are behind
28381da177e4SLinus Torvalds * a bridge and the pin is tied to something that's
2839f7625980SBjorn Helgaas * already mapped, set this one the same */
28401da177e4SLinus Torvalds if (temp_byte && resources->irqs &&
28411da177e4SLinus Torvalds (resources->irqs->valid_INT &
28421da177e4SLinus Torvalds (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
28431da177e4SLinus Torvalds /* We have to share with something already set up */
28441da177e4SLinus Torvalds IRQ = resources->irqs->interrupt[(temp_byte +
28451da177e4SLinus Torvalds resources->irqs->barber_pole - 1) & 0x03];
28461da177e4SLinus Torvalds } else {
28471da177e4SLinus Torvalds /* Program IRQ based on card type */
28481da177e4SLinus Torvalds rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code);
28491da177e4SLinus Torvalds
28501d3ecf13SAlex Chiang if (class_code == PCI_BASE_CLASS_STORAGE)
28511da177e4SLinus Torvalds IRQ = cpqhp_disk_irq;
28521d3ecf13SAlex Chiang else
28531da177e4SLinus Torvalds IRQ = cpqhp_nic_irq;
28541da177e4SLinus Torvalds }
28551da177e4SLinus Torvalds
28561da177e4SLinus Torvalds /* IRQ Line */
28571da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ);
28581da177e4SLinus Torvalds }
28591da177e4SLinus Torvalds
28601da177e4SLinus Torvalds if (!behind_bridge) {
286198d3333aSBjorn Helgaas rc = cpqhp_set_irq(func->bus, func->device, temp_byte, IRQ);
28621da177e4SLinus Torvalds if (rc)
28631da177e4SLinus Torvalds return 1;
28641da177e4SLinus Torvalds } else {
28651da177e4SLinus Torvalds /* TBD - this code may also belong in the other clause
28661da177e4SLinus Torvalds * of this If statement */
28671da177e4SLinus Torvalds resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ;
28681da177e4SLinus Torvalds resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03;
28691da177e4SLinus Torvalds }
28701da177e4SLinus Torvalds
28711da177e4SLinus Torvalds /* Latency Timer */
28721da177e4SLinus Torvalds temp_byte = 0x40;
28731da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn,
28741da177e4SLinus Torvalds PCI_LATENCY_TIMER, temp_byte);
28751da177e4SLinus Torvalds
28761da177e4SLinus Torvalds /* Cache Line size */
28771da177e4SLinus Torvalds temp_byte = 0x08;
28781da177e4SLinus Torvalds rc = pci_bus_write_config_byte(pci_bus, devfn,
28791da177e4SLinus Torvalds PCI_CACHE_LINE_SIZE, temp_byte);
28801da177e4SLinus Torvalds
28811da177e4SLinus Torvalds /* disable ROM base Address */
28821da177e4SLinus Torvalds temp_dword = 0x00L;
28831da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn,
28841da177e4SLinus Torvalds PCI_ROM_ADDRESS, temp_dword);
28851da177e4SLinus Torvalds
28861da177e4SLinus Torvalds /* enable card */
28871da177e4SLinus Torvalds temp_word = 0x0157; /* = PCI_COMMAND_IO |
28881da177e4SLinus Torvalds * PCI_COMMAND_MEMORY |
28891da177e4SLinus Torvalds * PCI_COMMAND_MASTER |
28901da177e4SLinus Torvalds * PCI_COMMAND_INVALIDATE |
28911da177e4SLinus Torvalds * PCI_COMMAND_PARITY |
28921da177e4SLinus Torvalds * PCI_COMMAND_SERR */
28931da177e4SLinus Torvalds rc = pci_bus_write_config_word(pci_bus, devfn,
28941da177e4SLinus Torvalds PCI_COMMAND, temp_word);
28951da177e4SLinus Torvalds } else { /* End of Not-A-Bridge else */
28961da177e4SLinus Torvalds /* It's some strange type of PCI adapter (Cardbus?) */
28971da177e4SLinus Torvalds return DEVICE_TYPE_NOT_SUPPORTED;
28981da177e4SLinus Torvalds }
28991da177e4SLinus Torvalds
29001da177e4SLinus Torvalds func->configured = 1;
29011da177e4SLinus Torvalds
29021da177e4SLinus Torvalds return 0;
29031da177e4SLinus Torvalds free_and_out:
29041da177e4SLinus Torvalds cpqhp_destroy_resource_list(&temp_resources);
29051da177e4SLinus Torvalds
29061da177e4SLinus Torvalds return_resource(&(resources->bus_head), hold_bus_node);
29071da177e4SLinus Torvalds return_resource(&(resources->io_head), hold_IO_node);
29081da177e4SLinus Torvalds return_resource(&(resources->mem_head), hold_mem_node);
29091da177e4SLinus Torvalds return_resource(&(resources->p_mem_head), hold_p_mem_node);
29101da177e4SLinus Torvalds return rc;
29111da177e4SLinus Torvalds }
2912