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, &reg);
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