162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Compaq Hot Plug Controller Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 1995,2001 Compaq Computer Corporation
662306a36Sopenharmony_ci * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
762306a36Sopenharmony_ci * Copyright (C) 2001 IBM Corp.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * All rights reserved.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Send feedback to <greg@kroah.com>
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/types.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/workqueue.h>
2062306a36Sopenharmony_ci#include <linux/interrupt.h>
2162306a36Sopenharmony_ci#include <linux/delay.h>
2262306a36Sopenharmony_ci#include <linux/wait.h>
2362306a36Sopenharmony_ci#include <linux/pci.h>
2462306a36Sopenharmony_ci#include <linux/pci_hotplug.h>
2562306a36Sopenharmony_ci#include <linux/kthread.h>
2662306a36Sopenharmony_ci#include "cpqphp.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
2962306a36Sopenharmony_ci			u8 behind_bridge, struct resource_lists *resources);
3062306a36Sopenharmony_cistatic int configure_new_function(struct controller *ctrl, struct pci_func *func,
3162306a36Sopenharmony_ci			u8 behind_bridge, struct resource_lists *resources);
3262306a36Sopenharmony_cistatic void interrupt_event_handler(struct controller *ctrl);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic struct task_struct *cpqhp_event_thread;
3662306a36Sopenharmony_cistatic struct timer_list *pushbutton_pending;	/* = NULL */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* delay is in jiffies to wait for */
3962306a36Sopenharmony_cistatic void long_delay(int delay)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	/*
4262306a36Sopenharmony_ci	 * XXX(hch): if someone is bored please convert all callers
4362306a36Sopenharmony_ci	 * to call msleep_interruptible directly.  They really want
4462306a36Sopenharmony_ci	 * to specify timeouts in natural units and spend a lot of
4562306a36Sopenharmony_ci	 * effort converting them to jiffies..
4662306a36Sopenharmony_ci	 */
4762306a36Sopenharmony_ci	msleep_interruptible(jiffies_to_msecs(delay));
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* FIXME: The following line needs to be somewhere else... */
5262306a36Sopenharmony_ci#define WRONG_BUS_FREQUENCY 0x07
5362306a36Sopenharmony_cistatic u8 handle_switch_change(u8 change, struct controller *ctrl)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	int hp_slot;
5662306a36Sopenharmony_ci	u8 rc = 0;
5762306a36Sopenharmony_ci	u16 temp_word;
5862306a36Sopenharmony_ci	struct pci_func *func;
5962306a36Sopenharmony_ci	struct event_info *taskInfo;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (!change)
6262306a36Sopenharmony_ci		return 0;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* Switch Change */
6562306a36Sopenharmony_ci	dbg("cpqsbd:  Switch interrupt received.\n");
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	for (hp_slot = 0; hp_slot < 6; hp_slot++) {
6862306a36Sopenharmony_ci		if (change & (0x1L << hp_slot)) {
6962306a36Sopenharmony_ci			/*
7062306a36Sopenharmony_ci			 * this one changed.
7162306a36Sopenharmony_ci			 */
7262306a36Sopenharmony_ci			func = cpqhp_slot_find(ctrl->bus,
7362306a36Sopenharmony_ci				(hp_slot + ctrl->slot_device_offset), 0);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci			/* this is the structure that tells the worker thread
7662306a36Sopenharmony_ci			 * what to do
7762306a36Sopenharmony_ci			 */
7862306a36Sopenharmony_ci			taskInfo = &(ctrl->event_queue[ctrl->next_event]);
7962306a36Sopenharmony_ci			ctrl->next_event = (ctrl->next_event + 1) % 10;
8062306a36Sopenharmony_ci			taskInfo->hp_slot = hp_slot;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci			rc++;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci			temp_word = ctrl->ctrl_int_comp >> 16;
8562306a36Sopenharmony_ci			func->presence_save = (temp_word >> hp_slot) & 0x01;
8662306a36Sopenharmony_ci			func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci			if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
8962306a36Sopenharmony_ci				/*
9062306a36Sopenharmony_ci				 * Switch opened
9162306a36Sopenharmony_ci				 */
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci				func->switch_save = 0;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci				taskInfo->event_type = INT_SWITCH_OPEN;
9662306a36Sopenharmony_ci			} else {
9762306a36Sopenharmony_ci				/*
9862306a36Sopenharmony_ci				 * Switch closed
9962306a36Sopenharmony_ci				 */
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci				func->switch_save = 0x10;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci				taskInfo->event_type = INT_SWITCH_CLOSE;
10462306a36Sopenharmony_ci			}
10562306a36Sopenharmony_ci		}
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return rc;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/**
11262306a36Sopenharmony_ci * cpqhp_find_slot - find the struct slot of given device
11362306a36Sopenharmony_ci * @ctrl: scan lots of this controller
11462306a36Sopenharmony_ci * @device: the device id to find
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_cistatic struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct slot *slot = ctrl->slot;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	while (slot && (slot->device != device))
12162306a36Sopenharmony_ci		slot = slot->next;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return slot;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic u8 handle_presence_change(u16 change, struct controller *ctrl)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	int hp_slot;
13062306a36Sopenharmony_ci	u8 rc = 0;
13162306a36Sopenharmony_ci	u8 temp_byte;
13262306a36Sopenharmony_ci	u16 temp_word;
13362306a36Sopenharmony_ci	struct pci_func *func;
13462306a36Sopenharmony_ci	struct event_info *taskInfo;
13562306a36Sopenharmony_ci	struct slot *p_slot;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (!change)
13862306a36Sopenharmony_ci		return 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 * Presence Change
14262306a36Sopenharmony_ci	 */
14362306a36Sopenharmony_ci	dbg("cpqsbd:  Presence/Notify input change.\n");
14462306a36Sopenharmony_ci	dbg("         Changed bits are 0x%4.4x\n", change);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	for (hp_slot = 0; hp_slot < 6; hp_slot++) {
14762306a36Sopenharmony_ci		if (change & (0x0101 << hp_slot)) {
14862306a36Sopenharmony_ci			/*
14962306a36Sopenharmony_ci			 * this one changed.
15062306a36Sopenharmony_ci			 */
15162306a36Sopenharmony_ci			func = cpqhp_slot_find(ctrl->bus,
15262306a36Sopenharmony_ci				(hp_slot + ctrl->slot_device_offset), 0);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci			taskInfo = &(ctrl->event_queue[ctrl->next_event]);
15562306a36Sopenharmony_ci			ctrl->next_event = (ctrl->next_event + 1) % 10;
15662306a36Sopenharmony_ci			taskInfo->hp_slot = hp_slot;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci			rc++;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci			p_slot = cpqhp_find_slot(ctrl, hp_slot + (readb(ctrl->hpc_reg + SLOT_MASK) >> 4));
16162306a36Sopenharmony_ci			if (!p_slot)
16262306a36Sopenharmony_ci				return 0;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci			/* If the switch closed, must be a button
16562306a36Sopenharmony_ci			 * If not in button mode, nevermind
16662306a36Sopenharmony_ci			 */
16762306a36Sopenharmony_ci			if (func->switch_save && (ctrl->push_button == 1)) {
16862306a36Sopenharmony_ci				temp_word = ctrl->ctrl_int_comp >> 16;
16962306a36Sopenharmony_ci				temp_byte = (temp_word >> hp_slot) & 0x01;
17062306a36Sopenharmony_ci				temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci				if (temp_byte != func->presence_save) {
17362306a36Sopenharmony_ci					/*
17462306a36Sopenharmony_ci					 * button Pressed (doesn't do anything)
17562306a36Sopenharmony_ci					 */
17662306a36Sopenharmony_ci					dbg("hp_slot %d button pressed\n", hp_slot);
17762306a36Sopenharmony_ci					taskInfo->event_type = INT_BUTTON_PRESS;
17862306a36Sopenharmony_ci				} else {
17962306a36Sopenharmony_ci					/*
18062306a36Sopenharmony_ci					 * button Released - TAKE ACTION!!!!
18162306a36Sopenharmony_ci					 */
18262306a36Sopenharmony_ci					dbg("hp_slot %d button released\n", hp_slot);
18362306a36Sopenharmony_ci					taskInfo->event_type = INT_BUTTON_RELEASE;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci					/* Cancel if we are still blinking */
18662306a36Sopenharmony_ci					if ((p_slot->state == BLINKINGON_STATE)
18762306a36Sopenharmony_ci					    || (p_slot->state == BLINKINGOFF_STATE)) {
18862306a36Sopenharmony_ci						taskInfo->event_type = INT_BUTTON_CANCEL;
18962306a36Sopenharmony_ci						dbg("hp_slot %d button cancel\n", hp_slot);
19062306a36Sopenharmony_ci					} else if ((p_slot->state == POWERON_STATE)
19162306a36Sopenharmony_ci						   || (p_slot->state == POWEROFF_STATE)) {
19262306a36Sopenharmony_ci						/* info(msg_button_ignore, p_slot->number); */
19362306a36Sopenharmony_ci						taskInfo->event_type = INT_BUTTON_IGNORE;
19462306a36Sopenharmony_ci						dbg("hp_slot %d button ignore\n", hp_slot);
19562306a36Sopenharmony_ci					}
19662306a36Sopenharmony_ci				}
19762306a36Sopenharmony_ci			} else {
19862306a36Sopenharmony_ci				/* Switch is open, assume a presence change
19962306a36Sopenharmony_ci				 * Save the presence state
20062306a36Sopenharmony_ci				 */
20162306a36Sopenharmony_ci				temp_word = ctrl->ctrl_int_comp >> 16;
20262306a36Sopenharmony_ci				func->presence_save = (temp_word >> hp_slot) & 0x01;
20362306a36Sopenharmony_ci				func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci				if ((!(ctrl->ctrl_int_comp & (0x010000 << hp_slot))) ||
20662306a36Sopenharmony_ci				    (!(ctrl->ctrl_int_comp & (0x01000000 << hp_slot)))) {
20762306a36Sopenharmony_ci					/* Present */
20862306a36Sopenharmony_ci					taskInfo->event_type = INT_PRESENCE_ON;
20962306a36Sopenharmony_ci				} else {
21062306a36Sopenharmony_ci					/* Not Present */
21162306a36Sopenharmony_ci					taskInfo->event_type = INT_PRESENCE_OFF;
21262306a36Sopenharmony_ci				}
21362306a36Sopenharmony_ci			}
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return rc;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic u8 handle_power_fault(u8 change, struct controller *ctrl)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	int hp_slot;
22462306a36Sopenharmony_ci	u8 rc = 0;
22562306a36Sopenharmony_ci	struct pci_func *func;
22662306a36Sopenharmony_ci	struct event_info *taskInfo;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (!change)
22962306a36Sopenharmony_ci		return 0;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/*
23262306a36Sopenharmony_ci	 * power fault
23362306a36Sopenharmony_ci	 */
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	info("power fault interrupt\n");
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	for (hp_slot = 0; hp_slot < 6; hp_slot++) {
23862306a36Sopenharmony_ci		if (change & (0x01 << hp_slot)) {
23962306a36Sopenharmony_ci			/*
24062306a36Sopenharmony_ci			 * this one changed.
24162306a36Sopenharmony_ci			 */
24262306a36Sopenharmony_ci			func = cpqhp_slot_find(ctrl->bus,
24362306a36Sopenharmony_ci				(hp_slot + ctrl->slot_device_offset), 0);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci			taskInfo = &(ctrl->event_queue[ctrl->next_event]);
24662306a36Sopenharmony_ci			ctrl->next_event = (ctrl->next_event + 1) % 10;
24762306a36Sopenharmony_ci			taskInfo->hp_slot = hp_slot;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci			rc++;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci			if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) {
25262306a36Sopenharmony_ci				/*
25362306a36Sopenharmony_ci				 * power fault Cleared
25462306a36Sopenharmony_ci				 */
25562306a36Sopenharmony_ci				func->status = 0x00;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci				taskInfo->event_type = INT_POWER_FAULT_CLEAR;
25862306a36Sopenharmony_ci			} else {
25962306a36Sopenharmony_ci				/*
26062306a36Sopenharmony_ci				 * power fault
26162306a36Sopenharmony_ci				 */
26262306a36Sopenharmony_ci				taskInfo->event_type = INT_POWER_FAULT;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci				if (ctrl->rev < 4) {
26562306a36Sopenharmony_ci					amber_LED_on(ctrl, hp_slot);
26662306a36Sopenharmony_ci					green_LED_off(ctrl, hp_slot);
26762306a36Sopenharmony_ci					set_SOGO(ctrl);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci					/* this is a fatal condition, we want
27062306a36Sopenharmony_ci					 * to crash the machine to protect from
27162306a36Sopenharmony_ci					 * data corruption. simulated_NMI
27262306a36Sopenharmony_ci					 * shouldn't ever return */
27362306a36Sopenharmony_ci					/* FIXME
27462306a36Sopenharmony_ci					simulated_NMI(hp_slot, ctrl); */
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci					/* The following code causes a software
27762306a36Sopenharmony_ci					 * crash just in case simulated_NMI did
27862306a36Sopenharmony_ci					 * return */
27962306a36Sopenharmony_ci					/*FIXME
28062306a36Sopenharmony_ci					panic(msg_power_fault); */
28162306a36Sopenharmony_ci				} else {
28262306a36Sopenharmony_ci					/* set power fault status for this board */
28362306a36Sopenharmony_ci					func->status = 0xFF;
28462306a36Sopenharmony_ci					info("power fault bit %x set\n", hp_slot);
28562306a36Sopenharmony_ci				}
28662306a36Sopenharmony_ci			}
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	return rc;
29162306a36Sopenharmony_ci}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/**
29562306a36Sopenharmony_ci * sort_by_size - sort nodes on the list by their length, smallest first.
29662306a36Sopenharmony_ci * @head: list to sort
29762306a36Sopenharmony_ci */
29862306a36Sopenharmony_cistatic int sort_by_size(struct pci_resource **head)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct pci_resource *current_res;
30162306a36Sopenharmony_ci	struct pci_resource *next_res;
30262306a36Sopenharmony_ci	int out_of_order = 1;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (!(*head))
30562306a36Sopenharmony_ci		return 1;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (!((*head)->next))
30862306a36Sopenharmony_ci		return 0;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	while (out_of_order) {
31162306a36Sopenharmony_ci		out_of_order = 0;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		/* Special case for swapping list head */
31462306a36Sopenharmony_ci		if (((*head)->next) &&
31562306a36Sopenharmony_ci		    ((*head)->length > (*head)->next->length)) {
31662306a36Sopenharmony_ci			out_of_order++;
31762306a36Sopenharmony_ci			current_res = *head;
31862306a36Sopenharmony_ci			*head = (*head)->next;
31962306a36Sopenharmony_ci			current_res->next = (*head)->next;
32062306a36Sopenharmony_ci			(*head)->next = current_res;
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		current_res = *head;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci		while (current_res->next && current_res->next->next) {
32662306a36Sopenharmony_ci			if (current_res->next->length > current_res->next->next->length) {
32762306a36Sopenharmony_ci				out_of_order++;
32862306a36Sopenharmony_ci				next_res = current_res->next;
32962306a36Sopenharmony_ci				current_res->next = current_res->next->next;
33062306a36Sopenharmony_ci				current_res = current_res->next;
33162306a36Sopenharmony_ci				next_res->next = current_res->next;
33262306a36Sopenharmony_ci				current_res->next = next_res;
33362306a36Sopenharmony_ci			} else
33462306a36Sopenharmony_ci				current_res = current_res->next;
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci	}  /* End of out_of_order loop */
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return 0;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci/**
34362306a36Sopenharmony_ci * sort_by_max_size - sort nodes on the list by their length, largest first.
34462306a36Sopenharmony_ci * @head: list to sort
34562306a36Sopenharmony_ci */
34662306a36Sopenharmony_cistatic int sort_by_max_size(struct pci_resource **head)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	struct pci_resource *current_res;
34962306a36Sopenharmony_ci	struct pci_resource *next_res;
35062306a36Sopenharmony_ci	int out_of_order = 1;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	if (!(*head))
35362306a36Sopenharmony_ci		return 1;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (!((*head)->next))
35662306a36Sopenharmony_ci		return 0;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	while (out_of_order) {
35962306a36Sopenharmony_ci		out_of_order = 0;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		/* Special case for swapping list head */
36262306a36Sopenharmony_ci		if (((*head)->next) &&
36362306a36Sopenharmony_ci		    ((*head)->length < (*head)->next->length)) {
36462306a36Sopenharmony_ci			out_of_order++;
36562306a36Sopenharmony_ci			current_res = *head;
36662306a36Sopenharmony_ci			*head = (*head)->next;
36762306a36Sopenharmony_ci			current_res->next = (*head)->next;
36862306a36Sopenharmony_ci			(*head)->next = current_res;
36962306a36Sopenharmony_ci		}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci		current_res = *head;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		while (current_res->next && current_res->next->next) {
37462306a36Sopenharmony_ci			if (current_res->next->length < current_res->next->next->length) {
37562306a36Sopenharmony_ci				out_of_order++;
37662306a36Sopenharmony_ci				next_res = current_res->next;
37762306a36Sopenharmony_ci				current_res->next = current_res->next->next;
37862306a36Sopenharmony_ci				current_res = current_res->next;
37962306a36Sopenharmony_ci				next_res->next = current_res->next;
38062306a36Sopenharmony_ci				current_res->next = next_res;
38162306a36Sopenharmony_ci			} else
38262306a36Sopenharmony_ci				current_res = current_res->next;
38362306a36Sopenharmony_ci		}
38462306a36Sopenharmony_ci	}  /* End of out_of_order loop */
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return 0;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci/**
39162306a36Sopenharmony_ci * do_pre_bridge_resource_split - find node of resources that are unused
39262306a36Sopenharmony_ci * @head: new list head
39362306a36Sopenharmony_ci * @orig_head: original list head
39462306a36Sopenharmony_ci * @alignment: max node size (?)
39562306a36Sopenharmony_ci */
39662306a36Sopenharmony_cistatic struct pci_resource *do_pre_bridge_resource_split(struct pci_resource **head,
39762306a36Sopenharmony_ci				struct pci_resource **orig_head, u32 alignment)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct pci_resource *prevnode = NULL;
40062306a36Sopenharmony_ci	struct pci_resource *node;
40162306a36Sopenharmony_ci	struct pci_resource *split_node;
40262306a36Sopenharmony_ci	u32 rc;
40362306a36Sopenharmony_ci	u32 temp_dword;
40462306a36Sopenharmony_ci	dbg("do_pre_bridge_resource_split\n");
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (!(*head) || !(*orig_head))
40762306a36Sopenharmony_ci		return NULL;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	rc = cpqhp_resource_sort_and_combine(head);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (rc)
41262306a36Sopenharmony_ci		return NULL;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if ((*head)->base != (*orig_head)->base)
41562306a36Sopenharmony_ci		return NULL;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if ((*head)->length == (*orig_head)->length)
41862306a36Sopenharmony_ci		return NULL;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* If we got here, there the bridge requires some of the resource, but
42262306a36Sopenharmony_ci	 * we may be able to split some off of the front
42362306a36Sopenharmony_ci	 */
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	node = *head;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (node->length & (alignment - 1)) {
42862306a36Sopenharmony_ci		/* this one isn't an aligned length, so we'll make a new entry
42962306a36Sopenharmony_ci		 * and split it up.
43062306a36Sopenharmony_ci		 */
43162306a36Sopenharmony_ci		split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci		if (!split_node)
43462306a36Sopenharmony_ci			return NULL;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		temp_dword = (node->length | (alignment-1)) + 1 - alignment;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		split_node->base = node->base;
43962306a36Sopenharmony_ci		split_node->length = temp_dword;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		node->length -= temp_dword;
44262306a36Sopenharmony_ci		node->base += split_node->length;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		/* Put it in the list */
44562306a36Sopenharmony_ci		*head = split_node;
44662306a36Sopenharmony_ci		split_node->next = node;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (node->length < alignment)
45062306a36Sopenharmony_ci		return NULL;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	/* Now unlink it */
45362306a36Sopenharmony_ci	if (*head == node) {
45462306a36Sopenharmony_ci		*head = node->next;
45562306a36Sopenharmony_ci	} else {
45662306a36Sopenharmony_ci		prevnode = *head;
45762306a36Sopenharmony_ci		while (prevnode->next != node)
45862306a36Sopenharmony_ci			prevnode = prevnode->next;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		prevnode->next = node->next;
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci	node->next = NULL;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return node;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci/**
46962306a36Sopenharmony_ci * do_bridge_resource_split - find one node of resources that aren't in use
47062306a36Sopenharmony_ci * @head: list head
47162306a36Sopenharmony_ci * @alignment: max node size (?)
47262306a36Sopenharmony_ci */
47362306a36Sopenharmony_cistatic struct pci_resource *do_bridge_resource_split(struct pci_resource **head, u32 alignment)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	struct pci_resource *prevnode = NULL;
47662306a36Sopenharmony_ci	struct pci_resource *node;
47762306a36Sopenharmony_ci	u32 rc;
47862306a36Sopenharmony_ci	u32 temp_dword;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	rc = cpqhp_resource_sort_and_combine(head);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (rc)
48362306a36Sopenharmony_ci		return NULL;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	node = *head;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	while (node->next) {
48862306a36Sopenharmony_ci		prevnode = node;
48962306a36Sopenharmony_ci		node = node->next;
49062306a36Sopenharmony_ci		kfree(prevnode);
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (node->length < alignment)
49462306a36Sopenharmony_ci		goto error;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (node->base & (alignment - 1)) {
49762306a36Sopenharmony_ci		/* Short circuit if adjusted size is too small */
49862306a36Sopenharmony_ci		temp_dword = (node->base | (alignment-1)) + 1;
49962306a36Sopenharmony_ci		if ((node->length - (temp_dword - node->base)) < alignment)
50062306a36Sopenharmony_ci			goto error;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci		node->length -= (temp_dword - node->base);
50362306a36Sopenharmony_ci		node->base = temp_dword;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (node->length & (alignment - 1))
50762306a36Sopenharmony_ci		/* There's stuff in use after this node */
50862306a36Sopenharmony_ci		goto error;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return node;
51162306a36Sopenharmony_cierror:
51262306a36Sopenharmony_ci	kfree(node);
51362306a36Sopenharmony_ci	return NULL;
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci/**
51862306a36Sopenharmony_ci * get_io_resource - find first node of given size not in ISA aliasing window.
51962306a36Sopenharmony_ci * @head: list to search
52062306a36Sopenharmony_ci * @size: size of node to find, must be a power of two.
52162306a36Sopenharmony_ci *
52262306a36Sopenharmony_ci * Description: This function sorts the resource list by size and then
52362306a36Sopenharmony_ci * returns the first node of "size" length that is not in the ISA aliasing
52462306a36Sopenharmony_ci * window.  If it finds a node larger than "size" it will split it up.
52562306a36Sopenharmony_ci */
52662306a36Sopenharmony_cistatic struct pci_resource *get_io_resource(struct pci_resource **head, u32 size)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	struct pci_resource *prevnode;
52962306a36Sopenharmony_ci	struct pci_resource *node;
53062306a36Sopenharmony_ci	struct pci_resource *split_node;
53162306a36Sopenharmony_ci	u32 temp_dword;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (!(*head))
53462306a36Sopenharmony_ci		return NULL;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (cpqhp_resource_sort_and_combine(head))
53762306a36Sopenharmony_ci		return NULL;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (sort_by_size(head))
54062306a36Sopenharmony_ci		return NULL;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	for (node = *head; node; node = node->next) {
54362306a36Sopenharmony_ci		if (node->length < size)
54462306a36Sopenharmony_ci			continue;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		if (node->base & (size - 1)) {
54762306a36Sopenharmony_ci			/* this one isn't base aligned properly
54862306a36Sopenharmony_ci			 * so we'll make a new entry and split it up
54962306a36Sopenharmony_ci			 */
55062306a36Sopenharmony_ci			temp_dword = (node->base | (size-1)) + 1;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci			/* Short circuit if adjusted size is too small */
55362306a36Sopenharmony_ci			if ((node->length - (temp_dword - node->base)) < size)
55462306a36Sopenharmony_ci				continue;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci			if (!split_node)
55962306a36Sopenharmony_ci				return NULL;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci			split_node->base = node->base;
56262306a36Sopenharmony_ci			split_node->length = temp_dword - node->base;
56362306a36Sopenharmony_ci			node->base = temp_dword;
56462306a36Sopenharmony_ci			node->length -= split_node->length;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci			/* Put it in the list */
56762306a36Sopenharmony_ci			split_node->next = node->next;
56862306a36Sopenharmony_ci			node->next = split_node;
56962306a36Sopenharmony_ci		} /* End of non-aligned base */
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		/* Don't need to check if too small since we already did */
57262306a36Sopenharmony_ci		if (node->length > size) {
57362306a36Sopenharmony_ci			/* this one is longer than we need
57462306a36Sopenharmony_ci			 * so we'll make a new entry and split it up
57562306a36Sopenharmony_ci			 */
57662306a36Sopenharmony_ci			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci			if (!split_node)
57962306a36Sopenharmony_ci				return NULL;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci			split_node->base = node->base + size;
58262306a36Sopenharmony_ci			split_node->length = node->length - size;
58362306a36Sopenharmony_ci			node->length = size;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci			/* Put it in the list */
58662306a36Sopenharmony_ci			split_node->next = node->next;
58762306a36Sopenharmony_ci			node->next = split_node;
58862306a36Sopenharmony_ci		}  /* End of too big on top end */
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci		/* For IO make sure it's not in the ISA aliasing space */
59162306a36Sopenharmony_ci		if (node->base & 0x300L)
59262306a36Sopenharmony_ci			continue;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		/* If we got here, then it is the right size
59562306a36Sopenharmony_ci		 * Now take it out of the list and break
59662306a36Sopenharmony_ci		 */
59762306a36Sopenharmony_ci		if (*head == node) {
59862306a36Sopenharmony_ci			*head = node->next;
59962306a36Sopenharmony_ci		} else {
60062306a36Sopenharmony_ci			prevnode = *head;
60162306a36Sopenharmony_ci			while (prevnode->next != node)
60262306a36Sopenharmony_ci				prevnode = prevnode->next;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci			prevnode->next = node->next;
60562306a36Sopenharmony_ci		}
60662306a36Sopenharmony_ci		node->next = NULL;
60762306a36Sopenharmony_ci		break;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return node;
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci/**
61562306a36Sopenharmony_ci * get_max_resource - get largest node which has at least the given size.
61662306a36Sopenharmony_ci * @head: the list to search the node in
61762306a36Sopenharmony_ci * @size: the minimum size of the node to find
61862306a36Sopenharmony_ci *
61962306a36Sopenharmony_ci * Description: Gets the largest node that is at least "size" big from the
62062306a36Sopenharmony_ci * list pointed to by head.  It aligns the node on top and bottom
62162306a36Sopenharmony_ci * to "size" alignment before returning it.
62262306a36Sopenharmony_ci */
62362306a36Sopenharmony_cistatic struct pci_resource *get_max_resource(struct pci_resource **head, u32 size)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	struct pci_resource *max;
62662306a36Sopenharmony_ci	struct pci_resource *temp;
62762306a36Sopenharmony_ci	struct pci_resource *split_node;
62862306a36Sopenharmony_ci	u32 temp_dword;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (cpqhp_resource_sort_and_combine(head))
63162306a36Sopenharmony_ci		return NULL;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (sort_by_max_size(head))
63462306a36Sopenharmony_ci		return NULL;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	for (max = *head; max; max = max->next) {
63762306a36Sopenharmony_ci		/* If not big enough we could probably just bail,
63862306a36Sopenharmony_ci		 * instead we'll continue to the next.
63962306a36Sopenharmony_ci		 */
64062306a36Sopenharmony_ci		if (max->length < size)
64162306a36Sopenharmony_ci			continue;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci		if (max->base & (size - 1)) {
64462306a36Sopenharmony_ci			/* this one isn't base aligned properly
64562306a36Sopenharmony_ci			 * so we'll make a new entry and split it up
64662306a36Sopenharmony_ci			 */
64762306a36Sopenharmony_ci			temp_dword = (max->base | (size-1)) + 1;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci			/* Short circuit if adjusted size is too small */
65062306a36Sopenharmony_ci			if ((max->length - (temp_dword - max->base)) < size)
65162306a36Sopenharmony_ci				continue;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci			if (!split_node)
65662306a36Sopenharmony_ci				return NULL;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci			split_node->base = max->base;
65962306a36Sopenharmony_ci			split_node->length = temp_dword - max->base;
66062306a36Sopenharmony_ci			max->base = temp_dword;
66162306a36Sopenharmony_ci			max->length -= split_node->length;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci			split_node->next = max->next;
66462306a36Sopenharmony_ci			max->next = split_node;
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		if ((max->base + max->length) & (size - 1)) {
66862306a36Sopenharmony_ci			/* this one isn't end aligned properly at the top
66962306a36Sopenharmony_ci			 * so we'll make a new entry and split it up
67062306a36Sopenharmony_ci			 */
67162306a36Sopenharmony_ci			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci			if (!split_node)
67462306a36Sopenharmony_ci				return NULL;
67562306a36Sopenharmony_ci			temp_dword = ((max->base + max->length) & ~(size - 1));
67662306a36Sopenharmony_ci			split_node->base = temp_dword;
67762306a36Sopenharmony_ci			split_node->length = max->length + max->base
67862306a36Sopenharmony_ci					     - split_node->base;
67962306a36Sopenharmony_ci			max->length -= split_node->length;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci			split_node->next = max->next;
68262306a36Sopenharmony_ci			max->next = split_node;
68362306a36Sopenharmony_ci		}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci		/* Make sure it didn't shrink too much when we aligned it */
68662306a36Sopenharmony_ci		if (max->length < size)
68762306a36Sopenharmony_ci			continue;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		/* Now take it out of the list */
69062306a36Sopenharmony_ci		temp = *head;
69162306a36Sopenharmony_ci		if (temp == max) {
69262306a36Sopenharmony_ci			*head = max->next;
69362306a36Sopenharmony_ci		} else {
69462306a36Sopenharmony_ci			while (temp && temp->next != max)
69562306a36Sopenharmony_ci				temp = temp->next;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci			if (temp)
69862306a36Sopenharmony_ci				temp->next = max->next;
69962306a36Sopenharmony_ci		}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci		max->next = NULL;
70262306a36Sopenharmony_ci		break;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	return max;
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci/**
71062306a36Sopenharmony_ci * get_resource - find resource of given size and split up larger ones.
71162306a36Sopenharmony_ci * @head: the list to search for resources
71262306a36Sopenharmony_ci * @size: the size limit to use
71362306a36Sopenharmony_ci *
71462306a36Sopenharmony_ci * Description: This function sorts the resource list by size and then
71562306a36Sopenharmony_ci * returns the first node of "size" length.  If it finds a node
71662306a36Sopenharmony_ci * larger than "size" it will split it up.
71762306a36Sopenharmony_ci *
71862306a36Sopenharmony_ci * size must be a power of two.
71962306a36Sopenharmony_ci */
72062306a36Sopenharmony_cistatic struct pci_resource *get_resource(struct pci_resource **head, u32 size)
72162306a36Sopenharmony_ci{
72262306a36Sopenharmony_ci	struct pci_resource *prevnode;
72362306a36Sopenharmony_ci	struct pci_resource *node;
72462306a36Sopenharmony_ci	struct pci_resource *split_node;
72562306a36Sopenharmony_ci	u32 temp_dword;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (cpqhp_resource_sort_and_combine(head))
72862306a36Sopenharmony_ci		return NULL;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (sort_by_size(head))
73162306a36Sopenharmony_ci		return NULL;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	for (node = *head; node; node = node->next) {
73462306a36Sopenharmony_ci		dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",
73562306a36Sopenharmony_ci		    __func__, size, node, node->base, node->length);
73662306a36Sopenharmony_ci		if (node->length < size)
73762306a36Sopenharmony_ci			continue;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		if (node->base & (size - 1)) {
74062306a36Sopenharmony_ci			dbg("%s: not aligned\n", __func__);
74162306a36Sopenharmony_ci			/* this one isn't base aligned properly
74262306a36Sopenharmony_ci			 * so we'll make a new entry and split it up
74362306a36Sopenharmony_ci			 */
74462306a36Sopenharmony_ci			temp_dword = (node->base | (size-1)) + 1;
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci			/* Short circuit if adjusted size is too small */
74762306a36Sopenharmony_ci			if ((node->length - (temp_dword - node->base)) < size)
74862306a36Sopenharmony_ci				continue;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci			if (!split_node)
75362306a36Sopenharmony_ci				return NULL;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci			split_node->base = node->base;
75662306a36Sopenharmony_ci			split_node->length = temp_dword - node->base;
75762306a36Sopenharmony_ci			node->base = temp_dword;
75862306a36Sopenharmony_ci			node->length -= split_node->length;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci			split_node->next = node->next;
76162306a36Sopenharmony_ci			node->next = split_node;
76262306a36Sopenharmony_ci		} /* End of non-aligned base */
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		/* Don't need to check if too small since we already did */
76562306a36Sopenharmony_ci		if (node->length > size) {
76662306a36Sopenharmony_ci			dbg("%s: too big\n", __func__);
76762306a36Sopenharmony_ci			/* this one is longer than we need
76862306a36Sopenharmony_ci			 * so we'll make a new entry and split it up
76962306a36Sopenharmony_ci			 */
77062306a36Sopenharmony_ci			split_node = kmalloc(sizeof(*split_node), GFP_KERNEL);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci			if (!split_node)
77362306a36Sopenharmony_ci				return NULL;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci			split_node->base = node->base + size;
77662306a36Sopenharmony_ci			split_node->length = node->length - size;
77762306a36Sopenharmony_ci			node->length = size;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci			/* Put it in the list */
78062306a36Sopenharmony_ci			split_node->next = node->next;
78162306a36Sopenharmony_ci			node->next = split_node;
78262306a36Sopenharmony_ci		}  /* End of too big on top end */
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		dbg("%s: got one!!!\n", __func__);
78562306a36Sopenharmony_ci		/* If we got here, then it is the right size
78662306a36Sopenharmony_ci		 * Now take it out of the list */
78762306a36Sopenharmony_ci		if (*head == node) {
78862306a36Sopenharmony_ci			*head = node->next;
78962306a36Sopenharmony_ci		} else {
79062306a36Sopenharmony_ci			prevnode = *head;
79162306a36Sopenharmony_ci			while (prevnode->next != node)
79262306a36Sopenharmony_ci				prevnode = prevnode->next;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci			prevnode->next = node->next;
79562306a36Sopenharmony_ci		}
79662306a36Sopenharmony_ci		node->next = NULL;
79762306a36Sopenharmony_ci		break;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci	return node;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci/**
80462306a36Sopenharmony_ci * cpqhp_resource_sort_and_combine - sort nodes by base addresses and clean up
80562306a36Sopenharmony_ci * @head: the list to sort and clean up
80662306a36Sopenharmony_ci *
80762306a36Sopenharmony_ci * Description: Sorts all of the nodes in the list in ascending order by
80862306a36Sopenharmony_ci * their base addresses.  Also does garbage collection by
80962306a36Sopenharmony_ci * combining adjacent nodes.
81062306a36Sopenharmony_ci *
81162306a36Sopenharmony_ci * Returns %0 if success.
81262306a36Sopenharmony_ci */
81362306a36Sopenharmony_ciint cpqhp_resource_sort_and_combine(struct pci_resource **head)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	struct pci_resource *node1;
81662306a36Sopenharmony_ci	struct pci_resource *node2;
81762306a36Sopenharmony_ci	int out_of_order = 1;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	dbg("%s: head = %p, *head = %p\n", __func__, head, *head);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	if (!(*head))
82262306a36Sopenharmony_ci		return 1;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	dbg("*head->next = %p\n", (*head)->next);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (!(*head)->next)
82762306a36Sopenharmony_ci		return 0;	/* only one item on the list, already sorted! */
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	dbg("*head->base = 0x%x\n", (*head)->base);
83062306a36Sopenharmony_ci	dbg("*head->next->base = 0x%x\n", (*head)->next->base);
83162306a36Sopenharmony_ci	while (out_of_order) {
83262306a36Sopenharmony_ci		out_of_order = 0;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci		/* Special case for swapping list head */
83562306a36Sopenharmony_ci		if (((*head)->next) &&
83662306a36Sopenharmony_ci		    ((*head)->base > (*head)->next->base)) {
83762306a36Sopenharmony_ci			node1 = *head;
83862306a36Sopenharmony_ci			(*head) = (*head)->next;
83962306a36Sopenharmony_ci			node1->next = (*head)->next;
84062306a36Sopenharmony_ci			(*head)->next = node1;
84162306a36Sopenharmony_ci			out_of_order++;
84262306a36Sopenharmony_ci		}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		node1 = (*head);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci		while (node1->next && node1->next->next) {
84762306a36Sopenharmony_ci			if (node1->next->base > node1->next->next->base) {
84862306a36Sopenharmony_ci				out_of_order++;
84962306a36Sopenharmony_ci				node2 = node1->next;
85062306a36Sopenharmony_ci				node1->next = node1->next->next;
85162306a36Sopenharmony_ci				node1 = node1->next;
85262306a36Sopenharmony_ci				node2->next = node1->next;
85362306a36Sopenharmony_ci				node1->next = node2;
85462306a36Sopenharmony_ci			} else
85562306a36Sopenharmony_ci				node1 = node1->next;
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci	}  /* End of out_of_order loop */
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	node1 = *head;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	while (node1 && node1->next) {
86262306a36Sopenharmony_ci		if ((node1->base + node1->length) == node1->next->base) {
86362306a36Sopenharmony_ci			/* Combine */
86462306a36Sopenharmony_ci			dbg("8..\n");
86562306a36Sopenharmony_ci			node1->length += node1->next->length;
86662306a36Sopenharmony_ci			node2 = node1->next;
86762306a36Sopenharmony_ci			node1->next = node1->next->next;
86862306a36Sopenharmony_ci			kfree(node2);
86962306a36Sopenharmony_ci		} else
87062306a36Sopenharmony_ci			node1 = node1->next;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	return 0;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ciirqreturn_t cpqhp_ctrl_intr(int IRQ, void *data)
87862306a36Sopenharmony_ci{
87962306a36Sopenharmony_ci	struct controller *ctrl = data;
88062306a36Sopenharmony_ci	u8 schedule_flag = 0;
88162306a36Sopenharmony_ci	u8 reset;
88262306a36Sopenharmony_ci	u16 misc;
88362306a36Sopenharmony_ci	u32 Diff;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	misc = readw(ctrl->hpc_reg + MISC);
88762306a36Sopenharmony_ci	/*
88862306a36Sopenharmony_ci	 * Check to see if it was our interrupt
88962306a36Sopenharmony_ci	 */
89062306a36Sopenharmony_ci	if (!(misc & 0x000C))
89162306a36Sopenharmony_ci		return IRQ_NONE;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	if (misc & 0x0004) {
89462306a36Sopenharmony_ci		/*
89562306a36Sopenharmony_ci		 * Serial Output interrupt Pending
89662306a36Sopenharmony_ci		 */
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_ci		/* Clear the interrupt */
89962306a36Sopenharmony_ci		misc |= 0x0004;
90062306a36Sopenharmony_ci		writew(misc, ctrl->hpc_reg + MISC);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci		/* Read to clear posted writes */
90362306a36Sopenharmony_ci		misc = readw(ctrl->hpc_reg + MISC);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci		dbg("%s - waking up\n", __func__);
90662306a36Sopenharmony_ci		wake_up_interruptible(&ctrl->queue);
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	if (misc & 0x0008) {
91062306a36Sopenharmony_ci		/* General-interrupt-input interrupt Pending */
91162306a36Sopenharmony_ci		Diff = readl(ctrl->hpc_reg + INT_INPUT_CLEAR) ^ ctrl->ctrl_int_comp;
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci		ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		/* Clear the interrupt */
91662306a36Sopenharmony_ci		writel(Diff, ctrl->hpc_reg + INT_INPUT_CLEAR);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci		/* Read it back to clear any posted writes */
91962306a36Sopenharmony_ci		readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci		if (!Diff)
92262306a36Sopenharmony_ci			/* Clear all interrupts */
92362306a36Sopenharmony_ci			writel(0xFFFFFFFF, ctrl->hpc_reg + INT_INPUT_CLEAR);
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci		schedule_flag += handle_switch_change((u8)(Diff & 0xFFL), ctrl);
92662306a36Sopenharmony_ci		schedule_flag += handle_presence_change((u16)((Diff & 0xFFFF0000L) >> 16), ctrl);
92762306a36Sopenharmony_ci		schedule_flag += handle_power_fault((u8)((Diff & 0xFF00L) >> 8), ctrl);
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE);
93162306a36Sopenharmony_ci	if (reset & 0x40) {
93262306a36Sopenharmony_ci		/* Bus reset has completed */
93362306a36Sopenharmony_ci		reset &= 0xCF;
93462306a36Sopenharmony_ci		writeb(reset, ctrl->hpc_reg + RESET_FREQ_MODE);
93562306a36Sopenharmony_ci		reset = readb(ctrl->hpc_reg + RESET_FREQ_MODE);
93662306a36Sopenharmony_ci		wake_up_interruptible(&ctrl->queue);
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	if (schedule_flag) {
94062306a36Sopenharmony_ci		wake_up_process(cpqhp_event_thread);
94162306a36Sopenharmony_ci		dbg("Waking even thread");
94262306a36Sopenharmony_ci	}
94362306a36Sopenharmony_ci	return IRQ_HANDLED;
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci/**
94862306a36Sopenharmony_ci * cpqhp_slot_create - Creates a node and adds it to the proper bus.
94962306a36Sopenharmony_ci * @busnumber: bus where new node is to be located
95062306a36Sopenharmony_ci *
95162306a36Sopenharmony_ci * Returns pointer to the new node or %NULL if unsuccessful.
95262306a36Sopenharmony_ci */
95362306a36Sopenharmony_cistruct pci_func *cpqhp_slot_create(u8 busnumber)
95462306a36Sopenharmony_ci{
95562306a36Sopenharmony_ci	struct pci_func *new_slot;
95662306a36Sopenharmony_ci	struct pci_func *next;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	new_slot = kzalloc(sizeof(*new_slot), GFP_KERNEL);
95962306a36Sopenharmony_ci	if (new_slot == NULL)
96062306a36Sopenharmony_ci		return new_slot;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	new_slot->next = NULL;
96362306a36Sopenharmony_ci	new_slot->configured = 1;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (cpqhp_slot_list[busnumber] == NULL) {
96662306a36Sopenharmony_ci		cpqhp_slot_list[busnumber] = new_slot;
96762306a36Sopenharmony_ci	} else {
96862306a36Sopenharmony_ci		next = cpqhp_slot_list[busnumber];
96962306a36Sopenharmony_ci		while (next->next != NULL)
97062306a36Sopenharmony_ci			next = next->next;
97162306a36Sopenharmony_ci		next->next = new_slot;
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci	return new_slot;
97462306a36Sopenharmony_ci}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci/**
97862306a36Sopenharmony_ci * slot_remove - Removes a node from the linked list of slots.
97962306a36Sopenharmony_ci * @old_slot: slot to remove
98062306a36Sopenharmony_ci *
98162306a36Sopenharmony_ci * Returns %0 if successful, !0 otherwise.
98262306a36Sopenharmony_ci */
98362306a36Sopenharmony_cistatic int slot_remove(struct pci_func *old_slot)
98462306a36Sopenharmony_ci{
98562306a36Sopenharmony_ci	struct pci_func *next;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	if (old_slot == NULL)
98862306a36Sopenharmony_ci		return 1;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	next = cpqhp_slot_list[old_slot->bus];
99162306a36Sopenharmony_ci	if (next == NULL)
99262306a36Sopenharmony_ci		return 1;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (next == old_slot) {
99562306a36Sopenharmony_ci		cpqhp_slot_list[old_slot->bus] = old_slot->next;
99662306a36Sopenharmony_ci		cpqhp_destroy_board_resources(old_slot);
99762306a36Sopenharmony_ci		kfree(old_slot);
99862306a36Sopenharmony_ci		return 0;
99962306a36Sopenharmony_ci	}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	while ((next->next != old_slot) && (next->next != NULL))
100262306a36Sopenharmony_ci		next = next->next;
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	if (next->next == old_slot) {
100562306a36Sopenharmony_ci		next->next = old_slot->next;
100662306a36Sopenharmony_ci		cpqhp_destroy_board_resources(old_slot);
100762306a36Sopenharmony_ci		kfree(old_slot);
100862306a36Sopenharmony_ci		return 0;
100962306a36Sopenharmony_ci	} else
101062306a36Sopenharmony_ci		return 2;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci/**
101562306a36Sopenharmony_ci * bridge_slot_remove - Removes a node from the linked list of slots.
101662306a36Sopenharmony_ci * @bridge: bridge to remove
101762306a36Sopenharmony_ci *
101862306a36Sopenharmony_ci * Returns %0 if successful, !0 otherwise.
101962306a36Sopenharmony_ci */
102062306a36Sopenharmony_cistatic int bridge_slot_remove(struct pci_func *bridge)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	u8 subordinateBus, secondaryBus;
102362306a36Sopenharmony_ci	u8 tempBus;
102462306a36Sopenharmony_ci	struct pci_func *next;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
102762306a36Sopenharmony_ci	subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
103062306a36Sopenharmony_ci		next = cpqhp_slot_list[tempBus];
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		while (!slot_remove(next))
103362306a36Sopenharmony_ci			next = cpqhp_slot_list[tempBus];
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	next = cpqhp_slot_list[bridge->bus];
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (next == NULL)
103962306a36Sopenharmony_ci		return 1;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	if (next == bridge) {
104262306a36Sopenharmony_ci		cpqhp_slot_list[bridge->bus] = bridge->next;
104362306a36Sopenharmony_ci		goto out;
104462306a36Sopenharmony_ci	}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	while ((next->next != bridge) && (next->next != NULL))
104762306a36Sopenharmony_ci		next = next->next;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	if (next->next != bridge)
105062306a36Sopenharmony_ci		return 2;
105162306a36Sopenharmony_ci	next->next = bridge->next;
105262306a36Sopenharmony_ciout:
105362306a36Sopenharmony_ci	kfree(bridge);
105462306a36Sopenharmony_ci	return 0;
105562306a36Sopenharmony_ci}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci/**
105962306a36Sopenharmony_ci * cpqhp_slot_find - Looks for a node by bus, and device, multiple functions accessed
106062306a36Sopenharmony_ci * @bus: bus to find
106162306a36Sopenharmony_ci * @device: device to find
106262306a36Sopenharmony_ci * @index: is %0 for first function found, %1 for the second...
106362306a36Sopenharmony_ci *
106462306a36Sopenharmony_ci * Returns pointer to the node if successful, %NULL otherwise.
106562306a36Sopenharmony_ci */
106662306a36Sopenharmony_cistruct pci_func *cpqhp_slot_find(u8 bus, u8 device, u8 index)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	int found = -1;
106962306a36Sopenharmony_ci	struct pci_func *func;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	func = cpqhp_slot_list[bus];
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	if ((func == NULL) || ((func->device == device) && (index == 0)))
107462306a36Sopenharmony_ci		return func;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (func->device == device)
107762306a36Sopenharmony_ci		found++;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	while (func->next != NULL) {
108062306a36Sopenharmony_ci		func = func->next;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci		if (func->device == device)
108362306a36Sopenharmony_ci			found++;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci		if (found == index)
108662306a36Sopenharmony_ci			return func;
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	return NULL;
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci/* DJZ: I don't think is_bridge will work as is.
109462306a36Sopenharmony_ci * FIXME */
109562306a36Sopenharmony_cistatic int is_bridge(struct pci_func *func)
109662306a36Sopenharmony_ci{
109762306a36Sopenharmony_ci	/* Check the header type */
109862306a36Sopenharmony_ci	if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
109962306a36Sopenharmony_ci		return 1;
110062306a36Sopenharmony_ci	else
110162306a36Sopenharmony_ci		return 0;
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci/**
110662306a36Sopenharmony_ci * set_controller_speed - set the frequency and/or mode of a specific controller segment.
110762306a36Sopenharmony_ci * @ctrl: controller to change frequency/mode for.
110862306a36Sopenharmony_ci * @adapter_speed: the speed of the adapter we want to match.
110962306a36Sopenharmony_ci * @hp_slot: the slot number where the adapter is installed.
111062306a36Sopenharmony_ci *
111162306a36Sopenharmony_ci * Returns %0 if we successfully change frequency and/or mode to match the
111262306a36Sopenharmony_ci * adapter speed.
111362306a36Sopenharmony_ci */
111462306a36Sopenharmony_cistatic u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_slot)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	struct slot *slot;
111762306a36Sopenharmony_ci	struct pci_bus *bus = ctrl->pci_bus;
111862306a36Sopenharmony_ci	u8 reg;
111962306a36Sopenharmony_ci	u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER);
112062306a36Sopenharmony_ci	u16 reg16;
112162306a36Sopenharmony_ci	u32 leds = readl(ctrl->hpc_reg + LED_CONTROL);
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	if (bus->cur_bus_speed == adapter_speed)
112462306a36Sopenharmony_ci		return 0;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	/* We don't allow freq/mode changes if we find another adapter running
112762306a36Sopenharmony_ci	 * in another slot on this controller
112862306a36Sopenharmony_ci	 */
112962306a36Sopenharmony_ci	for (slot = ctrl->slot; slot; slot = slot->next) {
113062306a36Sopenharmony_ci		if (slot->device == (hp_slot + ctrl->slot_device_offset))
113162306a36Sopenharmony_ci			continue;
113262306a36Sopenharmony_ci		if (get_presence_status(ctrl, slot) == 0)
113362306a36Sopenharmony_ci			continue;
113462306a36Sopenharmony_ci		/* If another adapter is running on the same segment but at a
113562306a36Sopenharmony_ci		 * lower speed/mode, we allow the new adapter to function at
113662306a36Sopenharmony_ci		 * this rate if supported
113762306a36Sopenharmony_ci		 */
113862306a36Sopenharmony_ci		if (bus->cur_bus_speed < adapter_speed)
113962306a36Sopenharmony_ci			return 0;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci		return 1;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	/* If the controller doesn't support freq/mode changes and the
114562306a36Sopenharmony_ci	 * controller is running at a higher mode, we bail
114662306a36Sopenharmony_ci	 */
114762306a36Sopenharmony_ci	if ((bus->cur_bus_speed > adapter_speed) && (!ctrl->pcix_speed_capability))
114862306a36Sopenharmony_ci		return 1;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	/* But we allow the adapter to run at a lower rate if possible */
115162306a36Sopenharmony_ci	if ((bus->cur_bus_speed < adapter_speed) && (!ctrl->pcix_speed_capability))
115262306a36Sopenharmony_ci		return 0;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	/* We try to set the max speed supported by both the adapter and
115562306a36Sopenharmony_ci	 * controller
115662306a36Sopenharmony_ci	 */
115762306a36Sopenharmony_ci	if (bus->max_bus_speed < adapter_speed) {
115862306a36Sopenharmony_ci		if (bus->cur_bus_speed == bus->max_bus_speed)
115962306a36Sopenharmony_ci			return 0;
116062306a36Sopenharmony_ci		adapter_speed = bus->max_bus_speed;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	writel(0x0L, ctrl->hpc_reg + LED_CONTROL);
116462306a36Sopenharmony_ci	writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci	set_SOGO(ctrl);
116762306a36Sopenharmony_ci	wait_for_ctrl_irq(ctrl);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (adapter_speed != PCI_SPEED_133MHz_PCIX)
117062306a36Sopenharmony_ci		reg = 0xF5;
117162306a36Sopenharmony_ci	else
117262306a36Sopenharmony_ci		reg = 0xF4;
117362306a36Sopenharmony_ci	pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ);
117662306a36Sopenharmony_ci	reg16 &= ~0x000F;
117762306a36Sopenharmony_ci	switch (adapter_speed) {
117862306a36Sopenharmony_ci		case(PCI_SPEED_133MHz_PCIX):
117962306a36Sopenharmony_ci			reg = 0x75;
118062306a36Sopenharmony_ci			reg16 |= 0xB;
118162306a36Sopenharmony_ci			break;
118262306a36Sopenharmony_ci		case(PCI_SPEED_100MHz_PCIX):
118362306a36Sopenharmony_ci			reg = 0x74;
118462306a36Sopenharmony_ci			reg16 |= 0xA;
118562306a36Sopenharmony_ci			break;
118662306a36Sopenharmony_ci		case(PCI_SPEED_66MHz_PCIX):
118762306a36Sopenharmony_ci			reg = 0x73;
118862306a36Sopenharmony_ci			reg16 |= 0x9;
118962306a36Sopenharmony_ci			break;
119062306a36Sopenharmony_ci		case(PCI_SPEED_66MHz):
119162306a36Sopenharmony_ci			reg = 0x73;
119262306a36Sopenharmony_ci			reg16 |= 0x1;
119362306a36Sopenharmony_ci			break;
119462306a36Sopenharmony_ci		default: /* 33MHz PCI 2.2 */
119562306a36Sopenharmony_ci			reg = 0x71;
119662306a36Sopenharmony_ci			break;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci	reg16 |= 0xB << 12;
120062306a36Sopenharmony_ci	writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	mdelay(5);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	/* Re-enable interrupts */
120562306a36Sopenharmony_ci	writel(0, ctrl->hpc_reg + INT_MASK);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	pci_write_config_byte(ctrl->pci_dev, 0x41, reg);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	/* Restart state machine */
121062306a36Sopenharmony_ci	reg = ~0xF;
121162306a36Sopenharmony_ci	pci_read_config_byte(ctrl->pci_dev, 0x43, &reg);
121262306a36Sopenharmony_ci	pci_write_config_byte(ctrl->pci_dev, 0x43, reg);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	/* Only if mode change...*/
121562306a36Sopenharmony_ci	if (((bus->cur_bus_speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) ||
121662306a36Sopenharmony_ci		((bus->cur_bus_speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz)))
121762306a36Sopenharmony_ci			set_SOGO(ctrl);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	wait_for_ctrl_irq(ctrl);
122062306a36Sopenharmony_ci	mdelay(1100);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	/* Restore LED/Slot state */
122362306a36Sopenharmony_ci	writel(leds, ctrl->hpc_reg + LED_CONTROL);
122462306a36Sopenharmony_ci	writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE);
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	set_SOGO(ctrl);
122762306a36Sopenharmony_ci	wait_for_ctrl_irq(ctrl);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	bus->cur_bus_speed = adapter_speed;
123062306a36Sopenharmony_ci	slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	info("Successfully changed frequency/mode for adapter in slot %d\n",
123362306a36Sopenharmony_ci			slot->number);
123462306a36Sopenharmony_ci	return 0;
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci/* the following routines constitute the bulk of the
123862306a36Sopenharmony_ci * hotplug controller logic
123962306a36Sopenharmony_ci */
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci/**
124362306a36Sopenharmony_ci * board_replaced - Called after a board has been replaced in the system.
124462306a36Sopenharmony_ci * @func: PCI device/function information
124562306a36Sopenharmony_ci * @ctrl: hotplug controller
124662306a36Sopenharmony_ci *
124762306a36Sopenharmony_ci * This is only used if we don't have resources for hot add.
124862306a36Sopenharmony_ci * Turns power on for the board.
124962306a36Sopenharmony_ci * Checks to see if board is the same.
125062306a36Sopenharmony_ci * If board is same, reconfigures it.
125162306a36Sopenharmony_ci * If board isn't same, turns it back off.
125262306a36Sopenharmony_ci */
125362306a36Sopenharmony_cistatic u32 board_replaced(struct pci_func *func, struct controller *ctrl)
125462306a36Sopenharmony_ci{
125562306a36Sopenharmony_ci	struct pci_bus *bus = ctrl->pci_bus;
125662306a36Sopenharmony_ci	u8 hp_slot;
125762306a36Sopenharmony_ci	u8 temp_byte;
125862306a36Sopenharmony_ci	u8 adapter_speed;
125962306a36Sopenharmony_ci	u32 rc = 0;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	hp_slot = func->device - ctrl->slot_device_offset;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	/*
126462306a36Sopenharmony_ci	 * The switch is open.
126562306a36Sopenharmony_ci	 */
126662306a36Sopenharmony_ci	if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot))
126762306a36Sopenharmony_ci		rc = INTERLOCK_OPEN;
126862306a36Sopenharmony_ci	/*
126962306a36Sopenharmony_ci	 * The board is already on
127062306a36Sopenharmony_ci	 */
127162306a36Sopenharmony_ci	else if (is_slot_enabled(ctrl, hp_slot))
127262306a36Sopenharmony_ci		rc = CARD_FUNCTIONING;
127362306a36Sopenharmony_ci	else {
127462306a36Sopenharmony_ci		mutex_lock(&ctrl->crit_sect);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci		/* turn on board without attaching to the bus */
127762306a36Sopenharmony_ci		enable_slot_power(ctrl, hp_slot);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci		set_SOGO(ctrl);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci		/* Wait for SOBS to be unset */
128262306a36Sopenharmony_ci		wait_for_ctrl_irq(ctrl);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci		/* Change bits in slot power register to force another shift out
128562306a36Sopenharmony_ci		 * NOTE: this is to work around the timer bug */
128662306a36Sopenharmony_ci		temp_byte = readb(ctrl->hpc_reg + SLOT_POWER);
128762306a36Sopenharmony_ci		writeb(0x00, ctrl->hpc_reg + SLOT_POWER);
128862306a36Sopenharmony_ci		writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci		set_SOGO(ctrl);
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci		/* Wait for SOBS to be unset */
129362306a36Sopenharmony_ci		wait_for_ctrl_irq(ctrl);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci		adapter_speed = get_adapter_speed(ctrl, hp_slot);
129662306a36Sopenharmony_ci		if (bus->cur_bus_speed != adapter_speed)
129762306a36Sopenharmony_ci			if (set_controller_speed(ctrl, adapter_speed, hp_slot))
129862306a36Sopenharmony_ci				rc = WRONG_BUS_FREQUENCY;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci		/* turn off board without attaching to the bus */
130162306a36Sopenharmony_ci		disable_slot_power(ctrl, hp_slot);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci		set_SOGO(ctrl);
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci		/* Wait for SOBS to be unset */
130662306a36Sopenharmony_ci		wait_for_ctrl_irq(ctrl);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci		mutex_unlock(&ctrl->crit_sect);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci		if (rc)
131162306a36Sopenharmony_ci			return rc;
131262306a36Sopenharmony_ci
131362306a36Sopenharmony_ci		mutex_lock(&ctrl->crit_sect);
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci		slot_enable(ctrl, hp_slot);
131662306a36Sopenharmony_ci		green_LED_blink(ctrl, hp_slot);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci		amber_LED_off(ctrl, hp_slot);
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci		set_SOGO(ctrl);
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci		/* Wait for SOBS to be unset */
132362306a36Sopenharmony_ci		wait_for_ctrl_irq(ctrl);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci		mutex_unlock(&ctrl->crit_sect);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci		/* Wait for ~1 second because of hot plug spec */
132862306a36Sopenharmony_ci		long_delay(1*HZ);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci		/* Check for a power fault */
133162306a36Sopenharmony_ci		if (func->status == 0xFF) {
133262306a36Sopenharmony_ci			/* power fault occurred, but it was benign */
133362306a36Sopenharmony_ci			rc = POWER_FAILURE;
133462306a36Sopenharmony_ci			func->status = 0;
133562306a36Sopenharmony_ci		} else
133662306a36Sopenharmony_ci			rc = cpqhp_valid_replace(ctrl, func);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci		if (!rc) {
133962306a36Sopenharmony_ci			/* It must be the same board */
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci			rc = cpqhp_configure_board(ctrl, func);
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci			/* If configuration fails, turn it off
134462306a36Sopenharmony_ci			 * Get slot won't work for devices behind
134562306a36Sopenharmony_ci			 * bridges, but in this case it will always be
134662306a36Sopenharmony_ci			 * called for the "base" bus/dev/func of an
134762306a36Sopenharmony_ci			 * adapter.
134862306a36Sopenharmony_ci			 */
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci			mutex_lock(&ctrl->crit_sect);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci			amber_LED_on(ctrl, hp_slot);
135362306a36Sopenharmony_ci			green_LED_off(ctrl, hp_slot);
135462306a36Sopenharmony_ci			slot_disable(ctrl, hp_slot);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci			set_SOGO(ctrl);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci			/* Wait for SOBS to be unset */
135962306a36Sopenharmony_ci			wait_for_ctrl_irq(ctrl);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci			mutex_unlock(&ctrl->crit_sect);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci			if (rc)
136462306a36Sopenharmony_ci				return rc;
136562306a36Sopenharmony_ci			else
136662306a36Sopenharmony_ci				return 1;
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci		} else {
136962306a36Sopenharmony_ci			/* Something is wrong
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci			 * Get slot won't work for devices behind bridges, but
137262306a36Sopenharmony_ci			 * in this case it will always be called for the "base"
137362306a36Sopenharmony_ci			 * bus/dev/func of an adapter.
137462306a36Sopenharmony_ci			 */
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci			mutex_lock(&ctrl->crit_sect);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci			amber_LED_on(ctrl, hp_slot);
137962306a36Sopenharmony_ci			green_LED_off(ctrl, hp_slot);
138062306a36Sopenharmony_ci			slot_disable(ctrl, hp_slot);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci			set_SOGO(ctrl);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci			/* Wait for SOBS to be unset */
138562306a36Sopenharmony_ci			wait_for_ctrl_irq(ctrl);
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci			mutex_unlock(&ctrl->crit_sect);
138862306a36Sopenharmony_ci		}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci	return rc;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci/**
139762306a36Sopenharmony_ci * board_added - Called after a board has been added to the system.
139862306a36Sopenharmony_ci * @func: PCI device/function info
139962306a36Sopenharmony_ci * @ctrl: hotplug controller
140062306a36Sopenharmony_ci *
140162306a36Sopenharmony_ci * Turns power on for the board.
140262306a36Sopenharmony_ci * Configures board.
140362306a36Sopenharmony_ci */
140462306a36Sopenharmony_cistatic u32 board_added(struct pci_func *func, struct controller *ctrl)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	u8 hp_slot;
140762306a36Sopenharmony_ci	u8 temp_byte;
140862306a36Sopenharmony_ci	u8 adapter_speed;
140962306a36Sopenharmony_ci	int index;
141062306a36Sopenharmony_ci	u32 temp_register = 0xFFFFFFFF;
141162306a36Sopenharmony_ci	u32 rc = 0;
141262306a36Sopenharmony_ci	struct pci_func *new_slot = NULL;
141362306a36Sopenharmony_ci	struct pci_bus *bus = ctrl->pci_bus;
141462306a36Sopenharmony_ci	struct resource_lists res_lists;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	hp_slot = func->device - ctrl->slot_device_offset;
141762306a36Sopenharmony_ci	dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n",
141862306a36Sopenharmony_ci	    __func__, func->device, ctrl->slot_device_offset, hp_slot);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	mutex_lock(&ctrl->crit_sect);
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	/* turn on board without attaching to the bus */
142362306a36Sopenharmony_ci	enable_slot_power(ctrl, hp_slot);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	set_SOGO(ctrl);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	/* Wait for SOBS to be unset */
142862306a36Sopenharmony_ci	wait_for_ctrl_irq(ctrl);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	/* Change bits in slot power register to force another shift out
143162306a36Sopenharmony_ci	 * NOTE: this is to work around the timer bug
143262306a36Sopenharmony_ci	 */
143362306a36Sopenharmony_ci	temp_byte = readb(ctrl->hpc_reg + SLOT_POWER);
143462306a36Sopenharmony_ci	writeb(0x00, ctrl->hpc_reg + SLOT_POWER);
143562306a36Sopenharmony_ci	writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	set_SOGO(ctrl);
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	/* Wait for SOBS to be unset */
144062306a36Sopenharmony_ci	wait_for_ctrl_irq(ctrl);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	adapter_speed = get_adapter_speed(ctrl, hp_slot);
144362306a36Sopenharmony_ci	if (bus->cur_bus_speed != adapter_speed)
144462306a36Sopenharmony_ci		if (set_controller_speed(ctrl, adapter_speed, hp_slot))
144562306a36Sopenharmony_ci			rc = WRONG_BUS_FREQUENCY;
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	/* turn off board without attaching to the bus */
144862306a36Sopenharmony_ci	disable_slot_power(ctrl, hp_slot);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	set_SOGO(ctrl);
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	/* Wait for SOBS to be unset */
145362306a36Sopenharmony_ci	wait_for_ctrl_irq(ctrl);
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	mutex_unlock(&ctrl->crit_sect);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	if (rc)
145862306a36Sopenharmony_ci		return rc;
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	/* turn on board and blink green LED */
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	dbg("%s: before down\n", __func__);
146562306a36Sopenharmony_ci	mutex_lock(&ctrl->crit_sect);
146662306a36Sopenharmony_ci	dbg("%s: after down\n", __func__);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	dbg("%s: before slot_enable\n", __func__);
146962306a36Sopenharmony_ci	slot_enable(ctrl, hp_slot);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	dbg("%s: before green_LED_blink\n", __func__);
147262306a36Sopenharmony_ci	green_LED_blink(ctrl, hp_slot);
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	dbg("%s: before amber_LED_blink\n", __func__);
147562306a36Sopenharmony_ci	amber_LED_off(ctrl, hp_slot);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	dbg("%s: before set_SOGO\n", __func__);
147862306a36Sopenharmony_ci	set_SOGO(ctrl);
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	/* Wait for SOBS to be unset */
148162306a36Sopenharmony_ci	dbg("%s: before wait_for_ctrl_irq\n", __func__);
148262306a36Sopenharmony_ci	wait_for_ctrl_irq(ctrl);
148362306a36Sopenharmony_ci	dbg("%s: after wait_for_ctrl_irq\n", __func__);
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	dbg("%s: before up\n", __func__);
148662306a36Sopenharmony_ci	mutex_unlock(&ctrl->crit_sect);
148762306a36Sopenharmony_ci	dbg("%s: after up\n", __func__);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	/* Wait for ~1 second because of hot plug spec */
149062306a36Sopenharmony_ci	dbg("%s: before long_delay\n", __func__);
149162306a36Sopenharmony_ci	long_delay(1*HZ);
149262306a36Sopenharmony_ci	dbg("%s: after long_delay\n", __func__);
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	dbg("%s: func status = %x\n", __func__, func->status);
149562306a36Sopenharmony_ci	/* Check for a power fault */
149662306a36Sopenharmony_ci	if (func->status == 0xFF) {
149762306a36Sopenharmony_ci		/* power fault occurred, but it was benign */
149862306a36Sopenharmony_ci		temp_register = 0xFFFFFFFF;
149962306a36Sopenharmony_ci		dbg("%s: temp register set to %x by power fault\n", __func__, temp_register);
150062306a36Sopenharmony_ci		rc = POWER_FAILURE;
150162306a36Sopenharmony_ci		func->status = 0;
150262306a36Sopenharmony_ci	} else {
150362306a36Sopenharmony_ci		/* Get vendor/device ID u32 */
150462306a36Sopenharmony_ci		ctrl->pci_bus->number = func->bus;
150562306a36Sopenharmony_ci		rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register);
150662306a36Sopenharmony_ci		dbg("%s: pci_read_config_dword returns %d\n", __func__, rc);
150762306a36Sopenharmony_ci		dbg("%s: temp_register is %x\n", __func__, temp_register);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci		if (rc != 0) {
151062306a36Sopenharmony_ci			/* Something's wrong here */
151162306a36Sopenharmony_ci			temp_register = 0xFFFFFFFF;
151262306a36Sopenharmony_ci			dbg("%s: temp register set to %x by error\n", __func__, temp_register);
151362306a36Sopenharmony_ci		}
151462306a36Sopenharmony_ci		/* Preset return code.  It will be changed later if things go okay. */
151562306a36Sopenharmony_ci		rc = NO_ADAPTER_PRESENT;
151662306a36Sopenharmony_ci	}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	/* All F's is an empty slot or an invalid board */
151962306a36Sopenharmony_ci	if (temp_register != 0xFFFFFFFF) {
152062306a36Sopenharmony_ci		res_lists.io_head = ctrl->io_head;
152162306a36Sopenharmony_ci		res_lists.mem_head = ctrl->mem_head;
152262306a36Sopenharmony_ci		res_lists.p_mem_head = ctrl->p_mem_head;
152362306a36Sopenharmony_ci		res_lists.bus_head = ctrl->bus_head;
152462306a36Sopenharmony_ci		res_lists.irqs = NULL;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci		rc = configure_new_device(ctrl, func, 0, &res_lists);
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci		dbg("%s: back from configure_new_device\n", __func__);
152962306a36Sopenharmony_ci		ctrl->io_head = res_lists.io_head;
153062306a36Sopenharmony_ci		ctrl->mem_head = res_lists.mem_head;
153162306a36Sopenharmony_ci		ctrl->p_mem_head = res_lists.p_mem_head;
153262306a36Sopenharmony_ci		ctrl->bus_head = res_lists.bus_head;
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci		cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
153562306a36Sopenharmony_ci		cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
153662306a36Sopenharmony_ci		cpqhp_resource_sort_and_combine(&(ctrl->io_head));
153762306a36Sopenharmony_ci		cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci		if (rc) {
154062306a36Sopenharmony_ci			mutex_lock(&ctrl->crit_sect);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci			amber_LED_on(ctrl, hp_slot);
154362306a36Sopenharmony_ci			green_LED_off(ctrl, hp_slot);
154462306a36Sopenharmony_ci			slot_disable(ctrl, hp_slot);
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci			set_SOGO(ctrl);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci			/* Wait for SOBS to be unset */
154962306a36Sopenharmony_ci			wait_for_ctrl_irq(ctrl);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci			mutex_unlock(&ctrl->crit_sect);
155262306a36Sopenharmony_ci			return rc;
155362306a36Sopenharmony_ci		} else {
155462306a36Sopenharmony_ci			cpqhp_save_slot_config(ctrl, func);
155562306a36Sopenharmony_ci		}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci		func->status = 0;
155962306a36Sopenharmony_ci		func->switch_save = 0x10;
156062306a36Sopenharmony_ci		func->is_a_board = 0x01;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci		/* next, we will instantiate the linux pci_dev structures (with
156362306a36Sopenharmony_ci		 * appropriate driver notification, if already present) */
156462306a36Sopenharmony_ci		dbg("%s: configure linux pci_dev structure\n", __func__);
156562306a36Sopenharmony_ci		index = 0;
156662306a36Sopenharmony_ci		do {
156762306a36Sopenharmony_ci			new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++);
156862306a36Sopenharmony_ci			if (new_slot && !new_slot->pci_dev)
156962306a36Sopenharmony_ci				cpqhp_configure_device(ctrl, new_slot);
157062306a36Sopenharmony_ci		} while (new_slot);
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci		mutex_lock(&ctrl->crit_sect);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci		green_LED_on(ctrl, hp_slot);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci		set_SOGO(ctrl);
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci		/* Wait for SOBS to be unset */
157962306a36Sopenharmony_ci		wait_for_ctrl_irq(ctrl);
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci		mutex_unlock(&ctrl->crit_sect);
158262306a36Sopenharmony_ci	} else {
158362306a36Sopenharmony_ci		mutex_lock(&ctrl->crit_sect);
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci		amber_LED_on(ctrl, hp_slot);
158662306a36Sopenharmony_ci		green_LED_off(ctrl, hp_slot);
158762306a36Sopenharmony_ci		slot_disable(ctrl, hp_slot);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci		set_SOGO(ctrl);
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci		/* Wait for SOBS to be unset */
159262306a36Sopenharmony_ci		wait_for_ctrl_irq(ctrl);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci		mutex_unlock(&ctrl->crit_sect);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci		return rc;
159762306a36Sopenharmony_ci	}
159862306a36Sopenharmony_ci	return 0;
159962306a36Sopenharmony_ci}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci/**
160362306a36Sopenharmony_ci * remove_board - Turns off slot and LEDs
160462306a36Sopenharmony_ci * @func: PCI device/function info
160562306a36Sopenharmony_ci * @replace_flag: whether replacing or adding a new device
160662306a36Sopenharmony_ci * @ctrl: target controller
160762306a36Sopenharmony_ci */
160862306a36Sopenharmony_cistatic u32 remove_board(struct pci_func *func, u32 replace_flag, struct controller *ctrl)
160962306a36Sopenharmony_ci{
161062306a36Sopenharmony_ci	int index;
161162306a36Sopenharmony_ci	u8 skip = 0;
161262306a36Sopenharmony_ci	u8 device;
161362306a36Sopenharmony_ci	u8 hp_slot;
161462306a36Sopenharmony_ci	u8 temp_byte;
161562306a36Sopenharmony_ci	struct resource_lists res_lists;
161662306a36Sopenharmony_ci	struct pci_func *temp_func;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	if (cpqhp_unconfigure_device(func))
161962306a36Sopenharmony_ci		return 1;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	device = func->device;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	hp_slot = func->device - ctrl->slot_device_offset;
162462306a36Sopenharmony_ci	dbg("In %s, hp_slot = %d\n", __func__, hp_slot);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	/* When we get here, it is safe to change base address registers.
162762306a36Sopenharmony_ci	 * We will attempt to save the base address register lengths */
162862306a36Sopenharmony_ci	if (replace_flag || !ctrl->add_support)
162962306a36Sopenharmony_ci		cpqhp_save_base_addr_length(ctrl, func);
163062306a36Sopenharmony_ci	else if (!func->bus_head && !func->mem_head &&
163162306a36Sopenharmony_ci		 !func->p_mem_head && !func->io_head) {
163262306a36Sopenharmony_ci		/* Here we check to see if we've saved any of the board's
163362306a36Sopenharmony_ci		 * resources already.  If so, we'll skip the attempt to
163462306a36Sopenharmony_ci		 * determine what's being used. */
163562306a36Sopenharmony_ci		index = 0;
163662306a36Sopenharmony_ci		temp_func = cpqhp_slot_find(func->bus, func->device, index++);
163762306a36Sopenharmony_ci		while (temp_func) {
163862306a36Sopenharmony_ci			if (temp_func->bus_head || temp_func->mem_head
163962306a36Sopenharmony_ci			    || temp_func->p_mem_head || temp_func->io_head) {
164062306a36Sopenharmony_ci				skip = 1;
164162306a36Sopenharmony_ci				break;
164262306a36Sopenharmony_ci			}
164362306a36Sopenharmony_ci			temp_func = cpqhp_slot_find(temp_func->bus, temp_func->device, index++);
164462306a36Sopenharmony_ci		}
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci		if (!skip)
164762306a36Sopenharmony_ci			cpqhp_save_used_resources(ctrl, func);
164862306a36Sopenharmony_ci	}
164962306a36Sopenharmony_ci	/* Change status to shutdown */
165062306a36Sopenharmony_ci	if (func->is_a_board)
165162306a36Sopenharmony_ci		func->status = 0x01;
165262306a36Sopenharmony_ci	func->configured = 0;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	mutex_lock(&ctrl->crit_sect);
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_ci	green_LED_off(ctrl, hp_slot);
165762306a36Sopenharmony_ci	slot_disable(ctrl, hp_slot);
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	set_SOGO(ctrl);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	/* turn off SERR for slot */
166262306a36Sopenharmony_ci	temp_byte = readb(ctrl->hpc_reg + SLOT_SERR);
166362306a36Sopenharmony_ci	temp_byte &= ~(0x01 << hp_slot);
166462306a36Sopenharmony_ci	writeb(temp_byte, ctrl->hpc_reg + SLOT_SERR);
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	/* Wait for SOBS to be unset */
166762306a36Sopenharmony_ci	wait_for_ctrl_irq(ctrl);
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_ci	mutex_unlock(&ctrl->crit_sect);
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	if (!replace_flag && ctrl->add_support) {
167262306a36Sopenharmony_ci		while (func) {
167362306a36Sopenharmony_ci			res_lists.io_head = ctrl->io_head;
167462306a36Sopenharmony_ci			res_lists.mem_head = ctrl->mem_head;
167562306a36Sopenharmony_ci			res_lists.p_mem_head = ctrl->p_mem_head;
167662306a36Sopenharmony_ci			res_lists.bus_head = ctrl->bus_head;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci			cpqhp_return_board_resources(func, &res_lists);
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci			ctrl->io_head = res_lists.io_head;
168162306a36Sopenharmony_ci			ctrl->mem_head = res_lists.mem_head;
168262306a36Sopenharmony_ci			ctrl->p_mem_head = res_lists.p_mem_head;
168362306a36Sopenharmony_ci			ctrl->bus_head = res_lists.bus_head;
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci			cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
168662306a36Sopenharmony_ci			cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
168762306a36Sopenharmony_ci			cpqhp_resource_sort_and_combine(&(ctrl->io_head));
168862306a36Sopenharmony_ci			cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci			if (is_bridge(func)) {
169162306a36Sopenharmony_ci				bridge_slot_remove(func);
169262306a36Sopenharmony_ci			} else
169362306a36Sopenharmony_ci				slot_remove(func);
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci			func = cpqhp_slot_find(ctrl->bus, device, 0);
169662306a36Sopenharmony_ci		}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci		/* Setup slot structure with entry for empty slot */
169962306a36Sopenharmony_ci		func = cpqhp_slot_create(ctrl->bus);
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci		if (func == NULL)
170262306a36Sopenharmony_ci			return 1;
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci		func->bus = ctrl->bus;
170562306a36Sopenharmony_ci		func->device = device;
170662306a36Sopenharmony_ci		func->function = 0;
170762306a36Sopenharmony_ci		func->configured = 0;
170862306a36Sopenharmony_ci		func->switch_save = 0x10;
170962306a36Sopenharmony_ci		func->is_a_board = 0;
171062306a36Sopenharmony_ci		func->p_task_event = NULL;
171162306a36Sopenharmony_ci	}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	return 0;
171462306a36Sopenharmony_ci}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_cistatic void pushbutton_helper_thread(struct timer_list *t)
171762306a36Sopenharmony_ci{
171862306a36Sopenharmony_ci	pushbutton_pending = t;
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci	wake_up_process(cpqhp_event_thread);
172162306a36Sopenharmony_ci}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci/* this is the main worker thread */
172562306a36Sopenharmony_cistatic int event_thread(void *data)
172662306a36Sopenharmony_ci{
172762306a36Sopenharmony_ci	struct controller *ctrl;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	while (1) {
173062306a36Sopenharmony_ci		dbg("!!!!event_thread sleeping\n");
173162306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
173262306a36Sopenharmony_ci		schedule();
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci		if (kthread_should_stop())
173562306a36Sopenharmony_ci			break;
173662306a36Sopenharmony_ci		/* Do stuff here */
173762306a36Sopenharmony_ci		if (pushbutton_pending)
173862306a36Sopenharmony_ci			cpqhp_pushbutton_thread(pushbutton_pending);
173962306a36Sopenharmony_ci		else
174062306a36Sopenharmony_ci			for (ctrl = cpqhp_ctrl_list; ctrl; ctrl = ctrl->next)
174162306a36Sopenharmony_ci				interrupt_event_handler(ctrl);
174262306a36Sopenharmony_ci	}
174362306a36Sopenharmony_ci	dbg("event_thread signals exit\n");
174462306a36Sopenharmony_ci	return 0;
174562306a36Sopenharmony_ci}
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ciint cpqhp_event_start_thread(void)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	cpqhp_event_thread = kthread_run(event_thread, NULL, "phpd_event");
175062306a36Sopenharmony_ci	if (IS_ERR(cpqhp_event_thread)) {
175162306a36Sopenharmony_ci		err("Can't start up our event thread\n");
175262306a36Sopenharmony_ci		return PTR_ERR(cpqhp_event_thread);
175362306a36Sopenharmony_ci	}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_ci	return 0;
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_civoid cpqhp_event_stop_thread(void)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	kthread_stop(cpqhp_event_thread);
176262306a36Sopenharmony_ci}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_cistatic void interrupt_event_handler(struct controller *ctrl)
176662306a36Sopenharmony_ci{
176762306a36Sopenharmony_ci	int loop;
176862306a36Sopenharmony_ci	int change = 1;
176962306a36Sopenharmony_ci	struct pci_func *func;
177062306a36Sopenharmony_ci	u8 hp_slot;
177162306a36Sopenharmony_ci	struct slot *p_slot;
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	while (change) {
177462306a36Sopenharmony_ci		change = 0;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci		for (loop = 0; loop < 10; loop++) {
177762306a36Sopenharmony_ci			/* dbg("loop %d\n", loop); */
177862306a36Sopenharmony_ci			if (ctrl->event_queue[loop].event_type != 0) {
177962306a36Sopenharmony_ci				hp_slot = ctrl->event_queue[loop].hp_slot;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci				func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0);
178262306a36Sopenharmony_ci				if (!func)
178362306a36Sopenharmony_ci					return;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci				p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
178662306a36Sopenharmony_ci				if (!p_slot)
178762306a36Sopenharmony_ci					return;
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci				dbg("hp_slot %d, func %p, p_slot %p\n",
179062306a36Sopenharmony_ci				    hp_slot, func, p_slot);
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci				if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
179362306a36Sopenharmony_ci					dbg("button pressed\n");
179462306a36Sopenharmony_ci				} else if (ctrl->event_queue[loop].event_type ==
179562306a36Sopenharmony_ci					   INT_BUTTON_CANCEL) {
179662306a36Sopenharmony_ci					dbg("button cancel\n");
179762306a36Sopenharmony_ci					del_timer(&p_slot->task_event);
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci					mutex_lock(&ctrl->crit_sect);
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci					if (p_slot->state == BLINKINGOFF_STATE) {
180262306a36Sopenharmony_ci						/* slot is on */
180362306a36Sopenharmony_ci						dbg("turn on green LED\n");
180462306a36Sopenharmony_ci						green_LED_on(ctrl, hp_slot);
180562306a36Sopenharmony_ci					} else if (p_slot->state == BLINKINGON_STATE) {
180662306a36Sopenharmony_ci						/* slot is off */
180762306a36Sopenharmony_ci						dbg("turn off green LED\n");
180862306a36Sopenharmony_ci						green_LED_off(ctrl, hp_slot);
180962306a36Sopenharmony_ci					}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci					info(msg_button_cancel, p_slot->number);
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci					p_slot->state = STATIC_STATE;
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci					amber_LED_off(ctrl, hp_slot);
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci					set_SOGO(ctrl);
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci					/* Wait for SOBS to be unset */
182062306a36Sopenharmony_ci					wait_for_ctrl_irq(ctrl);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci					mutex_unlock(&ctrl->crit_sect);
182362306a36Sopenharmony_ci				}
182462306a36Sopenharmony_ci				/*** button Released (No action on press...) */
182562306a36Sopenharmony_ci				else if (ctrl->event_queue[loop].event_type == INT_BUTTON_RELEASE) {
182662306a36Sopenharmony_ci					dbg("button release\n");
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci					if (is_slot_enabled(ctrl, hp_slot)) {
182962306a36Sopenharmony_ci						dbg("slot is on\n");
183062306a36Sopenharmony_ci						p_slot->state = BLINKINGOFF_STATE;
183162306a36Sopenharmony_ci						info(msg_button_off, p_slot->number);
183262306a36Sopenharmony_ci					} else {
183362306a36Sopenharmony_ci						dbg("slot is off\n");
183462306a36Sopenharmony_ci						p_slot->state = BLINKINGON_STATE;
183562306a36Sopenharmony_ci						info(msg_button_on, p_slot->number);
183662306a36Sopenharmony_ci					}
183762306a36Sopenharmony_ci					mutex_lock(&ctrl->crit_sect);
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci					dbg("blink green LED and turn off amber\n");
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci					amber_LED_off(ctrl, hp_slot);
184262306a36Sopenharmony_ci					green_LED_blink(ctrl, hp_slot);
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci					set_SOGO(ctrl);
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci					/* Wait for SOBS to be unset */
184762306a36Sopenharmony_ci					wait_for_ctrl_irq(ctrl);
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci					mutex_unlock(&ctrl->crit_sect);
185062306a36Sopenharmony_ci					timer_setup(&p_slot->task_event,
185162306a36Sopenharmony_ci						    pushbutton_helper_thread,
185262306a36Sopenharmony_ci						    0);
185362306a36Sopenharmony_ci					p_slot->hp_slot = hp_slot;
185462306a36Sopenharmony_ci					p_slot->ctrl = ctrl;
185562306a36Sopenharmony_ci/*					p_slot->physical_slot = physical_slot; */
185662306a36Sopenharmony_ci					p_slot->task_event.expires = jiffies + 5 * HZ;   /* 5 second delay */
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci					dbg("add_timer p_slot = %p\n", p_slot);
185962306a36Sopenharmony_ci					add_timer(&p_slot->task_event);
186062306a36Sopenharmony_ci				}
186162306a36Sopenharmony_ci				/***********POWER FAULT */
186262306a36Sopenharmony_ci				else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
186362306a36Sopenharmony_ci					dbg("power fault\n");
186462306a36Sopenharmony_ci				}
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci				ctrl->event_queue[loop].event_type = 0;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci				change = 1;
186962306a36Sopenharmony_ci			}
187062306a36Sopenharmony_ci		}		/* End of FOR loop */
187162306a36Sopenharmony_ci	}
187262306a36Sopenharmony_ci}
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci/**
187662306a36Sopenharmony_ci * cpqhp_pushbutton_thread - handle pushbutton events
187762306a36Sopenharmony_ci * @t: pointer to struct timer_list which holds all timer-related callbacks
187862306a36Sopenharmony_ci *
187962306a36Sopenharmony_ci * Scheduled procedure to handle blocking stuff for the pushbuttons.
188062306a36Sopenharmony_ci * Handles all pending events and exits.
188162306a36Sopenharmony_ci */
188262306a36Sopenharmony_civoid cpqhp_pushbutton_thread(struct timer_list *t)
188362306a36Sopenharmony_ci{
188462306a36Sopenharmony_ci	u8 hp_slot;
188562306a36Sopenharmony_ci	struct pci_func *func;
188662306a36Sopenharmony_ci	struct slot *p_slot = from_timer(p_slot, t, task_event);
188762306a36Sopenharmony_ci	struct controller *ctrl = (struct controller *) p_slot->ctrl;
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci	pushbutton_pending = NULL;
189062306a36Sopenharmony_ci	hp_slot = p_slot->hp_slot;
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	if (is_slot_enabled(ctrl, hp_slot)) {
189362306a36Sopenharmony_ci		p_slot->state = POWEROFF_STATE;
189462306a36Sopenharmony_ci		/* power Down board */
189562306a36Sopenharmony_ci		func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0);
189662306a36Sopenharmony_ci		dbg("In power_down_board, func = %p, ctrl = %p\n", func, ctrl);
189762306a36Sopenharmony_ci		if (!func) {
189862306a36Sopenharmony_ci			dbg("Error! func NULL in %s\n", __func__);
189962306a36Sopenharmony_ci			return;
190062306a36Sopenharmony_ci		}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci		if (cpqhp_process_SS(ctrl, func) != 0) {
190362306a36Sopenharmony_ci			amber_LED_on(ctrl, hp_slot);
190462306a36Sopenharmony_ci			green_LED_on(ctrl, hp_slot);
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci			set_SOGO(ctrl);
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci			/* Wait for SOBS to be unset */
190962306a36Sopenharmony_ci			wait_for_ctrl_irq(ctrl);
191062306a36Sopenharmony_ci		}
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci		p_slot->state = STATIC_STATE;
191362306a36Sopenharmony_ci	} else {
191462306a36Sopenharmony_ci		p_slot->state = POWERON_STATE;
191562306a36Sopenharmony_ci		/* slot is off */
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci		func = cpqhp_slot_find(p_slot->bus, p_slot->device, 0);
191862306a36Sopenharmony_ci		dbg("In add_board, func = %p, ctrl = %p\n", func, ctrl);
191962306a36Sopenharmony_ci		if (!func) {
192062306a36Sopenharmony_ci			dbg("Error! func NULL in %s\n", __func__);
192162306a36Sopenharmony_ci			return;
192262306a36Sopenharmony_ci		}
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci		if (ctrl != NULL) {
192562306a36Sopenharmony_ci			if (cpqhp_process_SI(ctrl, func) != 0) {
192662306a36Sopenharmony_ci				amber_LED_on(ctrl, hp_slot);
192762306a36Sopenharmony_ci				green_LED_off(ctrl, hp_slot);
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci				set_SOGO(ctrl);
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci				/* Wait for SOBS to be unset */
193262306a36Sopenharmony_ci				wait_for_ctrl_irq(ctrl);
193362306a36Sopenharmony_ci			}
193462306a36Sopenharmony_ci		}
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci		p_slot->state = STATIC_STATE;
193762306a36Sopenharmony_ci	}
193862306a36Sopenharmony_ci}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ciint cpqhp_process_SI(struct controller *ctrl, struct pci_func *func)
194262306a36Sopenharmony_ci{
194362306a36Sopenharmony_ci	u8 device, hp_slot;
194462306a36Sopenharmony_ci	u16 temp_word;
194562306a36Sopenharmony_ci	u32 tempdword;
194662306a36Sopenharmony_ci	int rc;
194762306a36Sopenharmony_ci	struct slot *p_slot;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	tempdword = 0;
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	device = func->device;
195262306a36Sopenharmony_ci	hp_slot = device - ctrl->slot_device_offset;
195362306a36Sopenharmony_ci	p_slot = cpqhp_find_slot(ctrl, device);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	/* Check to see if the interlock is closed */
195662306a36Sopenharmony_ci	tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR);
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	if (tempdword & (0x01 << hp_slot))
195962306a36Sopenharmony_ci		return 1;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	if (func->is_a_board) {
196262306a36Sopenharmony_ci		rc = board_replaced(func, ctrl);
196362306a36Sopenharmony_ci	} else {
196462306a36Sopenharmony_ci		/* add board */
196562306a36Sopenharmony_ci		slot_remove(func);
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci		func = cpqhp_slot_create(ctrl->bus);
196862306a36Sopenharmony_ci		if (func == NULL)
196962306a36Sopenharmony_ci			return 1;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci		func->bus = ctrl->bus;
197262306a36Sopenharmony_ci		func->device = device;
197362306a36Sopenharmony_ci		func->function = 0;
197462306a36Sopenharmony_ci		func->configured = 0;
197562306a36Sopenharmony_ci		func->is_a_board = 1;
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci		/* We have to save the presence info for these slots */
197862306a36Sopenharmony_ci		temp_word = ctrl->ctrl_int_comp >> 16;
197962306a36Sopenharmony_ci		func->presence_save = (temp_word >> hp_slot) & 0x01;
198062306a36Sopenharmony_ci		func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci		if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
198362306a36Sopenharmony_ci			func->switch_save = 0;
198462306a36Sopenharmony_ci		} else {
198562306a36Sopenharmony_ci			func->switch_save = 0x10;
198662306a36Sopenharmony_ci		}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci		rc = board_added(func, ctrl);
198962306a36Sopenharmony_ci		if (rc) {
199062306a36Sopenharmony_ci			if (is_bridge(func)) {
199162306a36Sopenharmony_ci				bridge_slot_remove(func);
199262306a36Sopenharmony_ci			} else
199362306a36Sopenharmony_ci				slot_remove(func);
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci			/* Setup slot structure with entry for empty slot */
199662306a36Sopenharmony_ci			func = cpqhp_slot_create(ctrl->bus);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci			if (func == NULL)
199962306a36Sopenharmony_ci				return 1;
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci			func->bus = ctrl->bus;
200262306a36Sopenharmony_ci			func->device = device;
200362306a36Sopenharmony_ci			func->function = 0;
200462306a36Sopenharmony_ci			func->configured = 0;
200562306a36Sopenharmony_ci			func->is_a_board = 0;
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci			/* We have to save the presence info for these slots */
200862306a36Sopenharmony_ci			temp_word = ctrl->ctrl_int_comp >> 16;
200962306a36Sopenharmony_ci			func->presence_save = (temp_word >> hp_slot) & 0x01;
201062306a36Sopenharmony_ci			func->presence_save |=
201162306a36Sopenharmony_ci			(temp_word >> (hp_slot + 7)) & 0x02;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci			if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
201462306a36Sopenharmony_ci				func->switch_save = 0;
201562306a36Sopenharmony_ci			} else {
201662306a36Sopenharmony_ci				func->switch_save = 0x10;
201762306a36Sopenharmony_ci			}
201862306a36Sopenharmony_ci		}
201962306a36Sopenharmony_ci	}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	if (rc)
202262306a36Sopenharmony_ci		dbg("%s: rc = %d\n", __func__, rc);
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	return rc;
202562306a36Sopenharmony_ci}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ciint cpqhp_process_SS(struct controller *ctrl, struct pci_func *func)
202962306a36Sopenharmony_ci{
203062306a36Sopenharmony_ci	u8 device, class_code, header_type, BCR;
203162306a36Sopenharmony_ci	u8 index = 0;
203262306a36Sopenharmony_ci	u8 replace_flag;
203362306a36Sopenharmony_ci	u32 rc = 0;
203462306a36Sopenharmony_ci	unsigned int devfn;
203562306a36Sopenharmony_ci	struct slot *p_slot;
203662306a36Sopenharmony_ci	struct pci_bus *pci_bus = ctrl->pci_bus;
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci	device = func->device;
203962306a36Sopenharmony_ci	func = cpqhp_slot_find(ctrl->bus, device, index++);
204062306a36Sopenharmony_ci	p_slot = cpqhp_find_slot(ctrl, device);
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	/* Make sure there are no video controllers here */
204362306a36Sopenharmony_ci	while (func && !rc) {
204462306a36Sopenharmony_ci		pci_bus->number = func->bus;
204562306a36Sopenharmony_ci		devfn = PCI_DEVFN(func->device, func->function);
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci		/* Check the Class Code */
204862306a36Sopenharmony_ci		rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code);
204962306a36Sopenharmony_ci		if (rc)
205062306a36Sopenharmony_ci			return rc;
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci		if (class_code == PCI_BASE_CLASS_DISPLAY) {
205362306a36Sopenharmony_ci			/* Display/Video adapter (not supported) */
205462306a36Sopenharmony_ci			rc = REMOVE_NOT_SUPPORTED;
205562306a36Sopenharmony_ci		} else {
205662306a36Sopenharmony_ci			/* See if it's a bridge */
205762306a36Sopenharmony_ci			rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
205862306a36Sopenharmony_ci			if (rc)
205962306a36Sopenharmony_ci				return rc;
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci			/* If it's a bridge, check the VGA Enable bit */
206262306a36Sopenharmony_ci			if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
206362306a36Sopenharmony_ci				rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR);
206462306a36Sopenharmony_ci				if (rc)
206562306a36Sopenharmony_ci					return rc;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci				/* If the VGA Enable bit is set, remove isn't
206862306a36Sopenharmony_ci				 * supported */
206962306a36Sopenharmony_ci				if (BCR & PCI_BRIDGE_CTL_VGA)
207062306a36Sopenharmony_ci					rc = REMOVE_NOT_SUPPORTED;
207162306a36Sopenharmony_ci			}
207262306a36Sopenharmony_ci		}
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci		func = cpqhp_slot_find(ctrl->bus, device, index++);
207562306a36Sopenharmony_ci	}
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	func = cpqhp_slot_find(ctrl->bus, device, 0);
207862306a36Sopenharmony_ci	if ((func != NULL) && !rc) {
207962306a36Sopenharmony_ci		/* FIXME: Replace flag should be passed into process_SS */
208062306a36Sopenharmony_ci		replace_flag = !(ctrl->add_support);
208162306a36Sopenharmony_ci		rc = remove_board(func, replace_flag, ctrl);
208262306a36Sopenharmony_ci	} else if (!rc) {
208362306a36Sopenharmony_ci		rc = 1;
208462306a36Sopenharmony_ci	}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	return rc;
208762306a36Sopenharmony_ci}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci/**
209062306a36Sopenharmony_ci * switch_leds - switch the leds, go from one site to the other.
209162306a36Sopenharmony_ci * @ctrl: controller to use
209262306a36Sopenharmony_ci * @num_of_slots: number of slots to use
209362306a36Sopenharmony_ci * @work_LED: LED control value
209462306a36Sopenharmony_ci * @direction: 1 to start from the left side, 0 to start right.
209562306a36Sopenharmony_ci */
209662306a36Sopenharmony_cistatic void switch_leds(struct controller *ctrl, const int num_of_slots,
209762306a36Sopenharmony_ci			u32 *work_LED, const int direction)
209862306a36Sopenharmony_ci{
209962306a36Sopenharmony_ci	int loop;
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	for (loop = 0; loop < num_of_slots; loop++) {
210262306a36Sopenharmony_ci		if (direction)
210362306a36Sopenharmony_ci			*work_LED = *work_LED >> 1;
210462306a36Sopenharmony_ci		else
210562306a36Sopenharmony_ci			*work_LED = *work_LED << 1;
210662306a36Sopenharmony_ci		writel(*work_LED, ctrl->hpc_reg + LED_CONTROL);
210762306a36Sopenharmony_ci
210862306a36Sopenharmony_ci		set_SOGO(ctrl);
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci		/* Wait for SOGO interrupt */
211162306a36Sopenharmony_ci		wait_for_ctrl_irq(ctrl);
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci		/* Get ready for next iteration */
211462306a36Sopenharmony_ci		long_delay((2*HZ)/10);
211562306a36Sopenharmony_ci	}
211662306a36Sopenharmony_ci}
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci/**
211962306a36Sopenharmony_ci * cpqhp_hardware_test - runs hardware tests
212062306a36Sopenharmony_ci * @ctrl: target controller
212162306a36Sopenharmony_ci * @test_num: the number written to the "test" file in sysfs.
212262306a36Sopenharmony_ci *
212362306a36Sopenharmony_ci * For hot plug ctrl folks to play with.
212462306a36Sopenharmony_ci */
212562306a36Sopenharmony_ciint cpqhp_hardware_test(struct controller *ctrl, int test_num)
212662306a36Sopenharmony_ci{
212762306a36Sopenharmony_ci	u32 save_LED;
212862306a36Sopenharmony_ci	u32 work_LED;
212962306a36Sopenharmony_ci	int loop;
213062306a36Sopenharmony_ci	int num_of_slots;
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0f;
213362306a36Sopenharmony_ci
213462306a36Sopenharmony_ci	switch (test_num) {
213562306a36Sopenharmony_ci	case 1:
213662306a36Sopenharmony_ci		/* Do stuff here! */
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_ci		/* Do that funky LED thing */
213962306a36Sopenharmony_ci		/* so we can restore them later */
214062306a36Sopenharmony_ci		save_LED = readl(ctrl->hpc_reg + LED_CONTROL);
214162306a36Sopenharmony_ci		work_LED = 0x01010101;
214262306a36Sopenharmony_ci		switch_leds(ctrl, num_of_slots, &work_LED, 0);
214362306a36Sopenharmony_ci		switch_leds(ctrl, num_of_slots, &work_LED, 1);
214462306a36Sopenharmony_ci		switch_leds(ctrl, num_of_slots, &work_LED, 0);
214562306a36Sopenharmony_ci		switch_leds(ctrl, num_of_slots, &work_LED, 1);
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci		work_LED = 0x01010000;
214862306a36Sopenharmony_ci		writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
214962306a36Sopenharmony_ci		switch_leds(ctrl, num_of_slots, &work_LED, 0);
215062306a36Sopenharmony_ci		switch_leds(ctrl, num_of_slots, &work_LED, 1);
215162306a36Sopenharmony_ci		work_LED = 0x00000101;
215262306a36Sopenharmony_ci		writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
215362306a36Sopenharmony_ci		switch_leds(ctrl, num_of_slots, &work_LED, 0);
215462306a36Sopenharmony_ci		switch_leds(ctrl, num_of_slots, &work_LED, 1);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci		work_LED = 0x01010000;
215762306a36Sopenharmony_ci		writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
215862306a36Sopenharmony_ci		for (loop = 0; loop < num_of_slots; loop++) {
215962306a36Sopenharmony_ci			set_SOGO(ctrl);
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci			/* Wait for SOGO interrupt */
216262306a36Sopenharmony_ci			wait_for_ctrl_irq(ctrl);
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci			/* Get ready for next iteration */
216562306a36Sopenharmony_ci			long_delay((3*HZ)/10);
216662306a36Sopenharmony_ci			work_LED = work_LED >> 16;
216762306a36Sopenharmony_ci			writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci			set_SOGO(ctrl);
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci			/* Wait for SOGO interrupt */
217262306a36Sopenharmony_ci			wait_for_ctrl_irq(ctrl);
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci			/* Get ready for next iteration */
217562306a36Sopenharmony_ci			long_delay((3*HZ)/10);
217662306a36Sopenharmony_ci			work_LED = work_LED << 16;
217762306a36Sopenharmony_ci			writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
217862306a36Sopenharmony_ci			work_LED = work_LED << 1;
217962306a36Sopenharmony_ci			writel(work_LED, ctrl->hpc_reg + LED_CONTROL);
218062306a36Sopenharmony_ci		}
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci		/* put it back the way it was */
218362306a36Sopenharmony_ci		writel(save_LED, ctrl->hpc_reg + LED_CONTROL);
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci		set_SOGO(ctrl);
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci		/* Wait for SOBS to be unset */
218862306a36Sopenharmony_ci		wait_for_ctrl_irq(ctrl);
218962306a36Sopenharmony_ci		break;
219062306a36Sopenharmony_ci	case 2:
219162306a36Sopenharmony_ci		/* Do other stuff here! */
219262306a36Sopenharmony_ci		break;
219362306a36Sopenharmony_ci	case 3:
219462306a36Sopenharmony_ci		/* and more... */
219562306a36Sopenharmony_ci		break;
219662306a36Sopenharmony_ci	}
219762306a36Sopenharmony_ci	return 0;
219862306a36Sopenharmony_ci}
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci/**
220262306a36Sopenharmony_ci * configure_new_device - Configures the PCI header information of one board.
220362306a36Sopenharmony_ci * @ctrl: pointer to controller structure
220462306a36Sopenharmony_ci * @func: pointer to function structure
220562306a36Sopenharmony_ci * @behind_bridge: 1 if this is a recursive call, 0 if not
220662306a36Sopenharmony_ci * @resources: pointer to set of resource lists
220762306a36Sopenharmony_ci *
220862306a36Sopenharmony_ci * Returns 0 if success.
220962306a36Sopenharmony_ci */
221062306a36Sopenharmony_cistatic u32 configure_new_device(struct controller  *ctrl, struct pci_func  *func,
221162306a36Sopenharmony_ci				 u8 behind_bridge, struct resource_lists  *resources)
221262306a36Sopenharmony_ci{
221362306a36Sopenharmony_ci	u8 temp_byte, function, max_functions, stop_it;
221462306a36Sopenharmony_ci	int rc;
221562306a36Sopenharmony_ci	u32 ID;
221662306a36Sopenharmony_ci	struct pci_func *new_slot;
221762306a36Sopenharmony_ci	int index;
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	new_slot = func;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	dbg("%s\n", __func__);
222262306a36Sopenharmony_ci	/* Check for Multi-function device */
222362306a36Sopenharmony_ci	ctrl->pci_bus->number = func->bus;
222462306a36Sopenharmony_ci	rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
222562306a36Sopenharmony_ci	if (rc) {
222662306a36Sopenharmony_ci		dbg("%s: rc = %d\n", __func__, rc);
222762306a36Sopenharmony_ci		return rc;
222862306a36Sopenharmony_ci	}
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	if (temp_byte & 0x80)	/* Multi-function device */
223162306a36Sopenharmony_ci		max_functions = 8;
223262306a36Sopenharmony_ci	else
223362306a36Sopenharmony_ci		max_functions = 1;
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci	function = 0;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	do {
223862306a36Sopenharmony_ci		rc = configure_new_function(ctrl, new_slot, behind_bridge, resources);
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci		if (rc) {
224162306a36Sopenharmony_ci			dbg("configure_new_function failed %d\n", rc);
224262306a36Sopenharmony_ci			index = 0;
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci			while (new_slot) {
224562306a36Sopenharmony_ci				new_slot = cpqhp_slot_find(new_slot->bus, new_slot->device, index++);
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci				if (new_slot)
224862306a36Sopenharmony_ci					cpqhp_return_board_resources(new_slot, resources);
224962306a36Sopenharmony_ci			}
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci			return rc;
225262306a36Sopenharmony_ci		}
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci		function++;
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci		stop_it = 0;
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci		/* The following loop skips to the next present function
225962306a36Sopenharmony_ci		 * and creates a board structure */
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci		while ((function < max_functions) && (!stop_it)) {
226262306a36Sopenharmony_ci			pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci			if (PCI_POSSIBLE_ERROR(ID)) {
226562306a36Sopenharmony_ci				function++;
226662306a36Sopenharmony_ci			} else {
226762306a36Sopenharmony_ci				/* Setup slot structure. */
226862306a36Sopenharmony_ci				new_slot = cpqhp_slot_create(func->bus);
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci				if (new_slot == NULL)
227162306a36Sopenharmony_ci					return 1;
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci				new_slot->bus = func->bus;
227462306a36Sopenharmony_ci				new_slot->device = func->device;
227562306a36Sopenharmony_ci				new_slot->function = function;
227662306a36Sopenharmony_ci				new_slot->is_a_board = 1;
227762306a36Sopenharmony_ci				new_slot->status = 0;
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci				stop_it++;
228062306a36Sopenharmony_ci			}
228162306a36Sopenharmony_ci		}
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	} while (function < max_functions);
228462306a36Sopenharmony_ci	dbg("returning from configure_new_device\n");
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	return 0;
228762306a36Sopenharmony_ci}
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci/*
229162306a36Sopenharmony_ci * Configuration logic that involves the hotplug data structures and
229262306a36Sopenharmony_ci * their bookkeeping
229362306a36Sopenharmony_ci */
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci/**
229762306a36Sopenharmony_ci * configure_new_function - Configures the PCI header information of one device
229862306a36Sopenharmony_ci * @ctrl: pointer to controller structure
229962306a36Sopenharmony_ci * @func: pointer to function structure
230062306a36Sopenharmony_ci * @behind_bridge: 1 if this is a recursive call, 0 if not
230162306a36Sopenharmony_ci * @resources: pointer to set of resource lists
230262306a36Sopenharmony_ci *
230362306a36Sopenharmony_ci * Calls itself recursively for bridged devices.
230462306a36Sopenharmony_ci * Returns 0 if success.
230562306a36Sopenharmony_ci */
230662306a36Sopenharmony_cistatic int configure_new_function(struct controller *ctrl, struct pci_func *func,
230762306a36Sopenharmony_ci				   u8 behind_bridge,
230862306a36Sopenharmony_ci				   struct resource_lists *resources)
230962306a36Sopenharmony_ci{
231062306a36Sopenharmony_ci	int cloop;
231162306a36Sopenharmony_ci	u8 IRQ = 0;
231262306a36Sopenharmony_ci	u8 temp_byte;
231362306a36Sopenharmony_ci	u8 device;
231462306a36Sopenharmony_ci	u8 class_code;
231562306a36Sopenharmony_ci	u16 command;
231662306a36Sopenharmony_ci	u16 temp_word;
231762306a36Sopenharmony_ci	u32 temp_dword;
231862306a36Sopenharmony_ci	u32 rc;
231962306a36Sopenharmony_ci	u32 temp_register;
232062306a36Sopenharmony_ci	u32 base;
232162306a36Sopenharmony_ci	u32 ID;
232262306a36Sopenharmony_ci	unsigned int devfn;
232362306a36Sopenharmony_ci	struct pci_resource *mem_node;
232462306a36Sopenharmony_ci	struct pci_resource *p_mem_node;
232562306a36Sopenharmony_ci	struct pci_resource *io_node;
232662306a36Sopenharmony_ci	struct pci_resource *bus_node;
232762306a36Sopenharmony_ci	struct pci_resource *hold_mem_node;
232862306a36Sopenharmony_ci	struct pci_resource *hold_p_mem_node;
232962306a36Sopenharmony_ci	struct pci_resource *hold_IO_node;
233062306a36Sopenharmony_ci	struct pci_resource *hold_bus_node;
233162306a36Sopenharmony_ci	struct irq_mapping irqs;
233262306a36Sopenharmony_ci	struct pci_func *new_slot;
233362306a36Sopenharmony_ci	struct pci_bus *pci_bus;
233462306a36Sopenharmony_ci	struct resource_lists temp_resources;
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci	pci_bus = ctrl->pci_bus;
233762306a36Sopenharmony_ci	pci_bus->number = func->bus;
233862306a36Sopenharmony_ci	devfn = PCI_DEVFN(func->device, func->function);
233962306a36Sopenharmony_ci
234062306a36Sopenharmony_ci	/* Check for Bridge */
234162306a36Sopenharmony_ci	rc = pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte);
234262306a36Sopenharmony_ci	if (rc)
234362306a36Sopenharmony_ci		return rc;
234462306a36Sopenharmony_ci
234562306a36Sopenharmony_ci	if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
234662306a36Sopenharmony_ci		/* set Primary bus */
234762306a36Sopenharmony_ci		dbg("set Primary bus = %d\n", func->bus);
234862306a36Sopenharmony_ci		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
234962306a36Sopenharmony_ci		if (rc)
235062306a36Sopenharmony_ci			return rc;
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci		/* find range of buses to use */
235362306a36Sopenharmony_ci		dbg("find ranges of buses to use\n");
235462306a36Sopenharmony_ci		bus_node = get_max_resource(&(resources->bus_head), 1);
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci		/* If we don't have any buses to allocate, we can't continue */
235762306a36Sopenharmony_ci		if (!bus_node)
235862306a36Sopenharmony_ci			return -ENOMEM;
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci		/* set Secondary bus */
236162306a36Sopenharmony_ci		temp_byte = bus_node->base;
236262306a36Sopenharmony_ci		dbg("set Secondary bus = %d\n", bus_node->base);
236362306a36Sopenharmony_ci		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte);
236462306a36Sopenharmony_ci		if (rc)
236562306a36Sopenharmony_ci			return rc;
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_ci		/* set subordinate bus */
236862306a36Sopenharmony_ci		temp_byte = bus_node->base + bus_node->length - 1;
236962306a36Sopenharmony_ci		dbg("set subordinate bus = %d\n", bus_node->base + bus_node->length - 1);
237062306a36Sopenharmony_ci		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
237162306a36Sopenharmony_ci		if (rc)
237262306a36Sopenharmony_ci			return rc;
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci		/* set subordinate Latency Timer and base Latency Timer */
237562306a36Sopenharmony_ci		temp_byte = 0x40;
237662306a36Sopenharmony_ci		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
237762306a36Sopenharmony_ci		if (rc)
237862306a36Sopenharmony_ci			return rc;
237962306a36Sopenharmony_ci		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
238062306a36Sopenharmony_ci		if (rc)
238162306a36Sopenharmony_ci			return rc;
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci		/* set Cache Line size */
238462306a36Sopenharmony_ci		temp_byte = 0x08;
238562306a36Sopenharmony_ci		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
238662306a36Sopenharmony_ci		if (rc)
238762306a36Sopenharmony_ci			return rc;
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci		/* Setup the IO, memory, and prefetchable windows */
239062306a36Sopenharmony_ci		io_node = get_max_resource(&(resources->io_head), 0x1000);
239162306a36Sopenharmony_ci		if (!io_node)
239262306a36Sopenharmony_ci			return -ENOMEM;
239362306a36Sopenharmony_ci		mem_node = get_max_resource(&(resources->mem_head), 0x100000);
239462306a36Sopenharmony_ci		if (!mem_node)
239562306a36Sopenharmony_ci			return -ENOMEM;
239662306a36Sopenharmony_ci		p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000);
239762306a36Sopenharmony_ci		if (!p_mem_node)
239862306a36Sopenharmony_ci			return -ENOMEM;
239962306a36Sopenharmony_ci		dbg("Setup the IO, memory, and prefetchable windows\n");
240062306a36Sopenharmony_ci		dbg("io_node\n");
240162306a36Sopenharmony_ci		dbg("(base, len, next) (%x, %x, %p)\n", io_node->base,
240262306a36Sopenharmony_ci					io_node->length, io_node->next);
240362306a36Sopenharmony_ci		dbg("mem_node\n");
240462306a36Sopenharmony_ci		dbg("(base, len, next) (%x, %x, %p)\n", mem_node->base,
240562306a36Sopenharmony_ci					mem_node->length, mem_node->next);
240662306a36Sopenharmony_ci		dbg("p_mem_node\n");
240762306a36Sopenharmony_ci		dbg("(base, len, next) (%x, %x, %p)\n", p_mem_node->base,
240862306a36Sopenharmony_ci					p_mem_node->length, p_mem_node->next);
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci		/* set up the IRQ info */
241162306a36Sopenharmony_ci		if (!resources->irqs) {
241262306a36Sopenharmony_ci			irqs.barber_pole = 0;
241362306a36Sopenharmony_ci			irqs.interrupt[0] = 0;
241462306a36Sopenharmony_ci			irqs.interrupt[1] = 0;
241562306a36Sopenharmony_ci			irqs.interrupt[2] = 0;
241662306a36Sopenharmony_ci			irqs.interrupt[3] = 0;
241762306a36Sopenharmony_ci			irqs.valid_INT = 0;
241862306a36Sopenharmony_ci		} else {
241962306a36Sopenharmony_ci			irqs.barber_pole = resources->irqs->barber_pole;
242062306a36Sopenharmony_ci			irqs.interrupt[0] = resources->irqs->interrupt[0];
242162306a36Sopenharmony_ci			irqs.interrupt[1] = resources->irqs->interrupt[1];
242262306a36Sopenharmony_ci			irqs.interrupt[2] = resources->irqs->interrupt[2];
242362306a36Sopenharmony_ci			irqs.interrupt[3] = resources->irqs->interrupt[3];
242462306a36Sopenharmony_ci			irqs.valid_INT = resources->irqs->valid_INT;
242562306a36Sopenharmony_ci		}
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci		/* set up resource lists that are now aligned on top and bottom
242862306a36Sopenharmony_ci		 * for anything behind the bridge. */
242962306a36Sopenharmony_ci		temp_resources.bus_head = bus_node;
243062306a36Sopenharmony_ci		temp_resources.io_head = io_node;
243162306a36Sopenharmony_ci		temp_resources.mem_head = mem_node;
243262306a36Sopenharmony_ci		temp_resources.p_mem_head = p_mem_node;
243362306a36Sopenharmony_ci		temp_resources.irqs = &irqs;
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci		/* Make copies of the nodes we are going to pass down so that
243662306a36Sopenharmony_ci		 * if there is a problem,we can just use these to free resources
243762306a36Sopenharmony_ci		 */
243862306a36Sopenharmony_ci		hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL);
243962306a36Sopenharmony_ci		hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL);
244062306a36Sopenharmony_ci		hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL);
244162306a36Sopenharmony_ci		hold_p_mem_node = kmalloc(sizeof(*hold_p_mem_node), GFP_KERNEL);
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci		if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
244462306a36Sopenharmony_ci			kfree(hold_bus_node);
244562306a36Sopenharmony_ci			kfree(hold_IO_node);
244662306a36Sopenharmony_ci			kfree(hold_mem_node);
244762306a36Sopenharmony_ci			kfree(hold_p_mem_node);
244862306a36Sopenharmony_ci
244962306a36Sopenharmony_ci			return 1;
245062306a36Sopenharmony_ci		}
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci		memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource));
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci		bus_node->base += 1;
245562306a36Sopenharmony_ci		bus_node->length -= 1;
245662306a36Sopenharmony_ci		bus_node->next = NULL;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci		/* If we have IO resources copy them and fill in the bridge's
245962306a36Sopenharmony_ci		 * IO range registers */
246062306a36Sopenharmony_ci		memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
246162306a36Sopenharmony_ci		io_node->next = NULL;
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci		/* set IO base and Limit registers */
246462306a36Sopenharmony_ci		temp_byte = io_node->base >> 8;
246562306a36Sopenharmony_ci		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_BASE, temp_byte);
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci		temp_byte = (io_node->base + io_node->length - 1) >> 8;
246862306a36Sopenharmony_ci		rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci		/* Copy the memory resources and fill in the bridge's memory
247162306a36Sopenharmony_ci		 * range registers.
247262306a36Sopenharmony_ci		 */
247362306a36Sopenharmony_ci		memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
247462306a36Sopenharmony_ci		mem_node->next = NULL;
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci		/* set Mem base and Limit registers */
247762306a36Sopenharmony_ci		temp_word = mem_node->base >> 16;
247862306a36Sopenharmony_ci		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci		temp_word = (mem_node->base + mem_node->length - 1) >> 16;
248162306a36Sopenharmony_ci		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci		memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource));
248462306a36Sopenharmony_ci		p_mem_node->next = NULL;
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci		/* set Pre Mem base and Limit registers */
248762306a36Sopenharmony_ci		temp_word = p_mem_node->base >> 16;
248862306a36Sopenharmony_ci		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci		temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16;
249162306a36Sopenharmony_ci		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci		/* Adjust this to compensate for extra adjustment in first loop
249462306a36Sopenharmony_ci		 */
249562306a36Sopenharmony_ci		irqs.barber_pole--;
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci		rc = 0;
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci		/* Here we actually find the devices and configure them */
250062306a36Sopenharmony_ci		for (device = 0; (device <= 0x1F) && !rc; device++) {
250162306a36Sopenharmony_ci			irqs.barber_pole = (irqs.barber_pole + 1) & 0x03;
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci			ID = 0xFFFFFFFF;
250462306a36Sopenharmony_ci			pci_bus->number = hold_bus_node->base;
250562306a36Sopenharmony_ci			pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), 0x00, &ID);
250662306a36Sopenharmony_ci			pci_bus->number = func->bus;
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci			if (!PCI_POSSIBLE_ERROR(ID)) {	  /*  device present */
250962306a36Sopenharmony_ci				/* Setup slot structure. */
251062306a36Sopenharmony_ci				new_slot = cpqhp_slot_create(hold_bus_node->base);
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_ci				if (new_slot == NULL) {
251362306a36Sopenharmony_ci					rc = -ENOMEM;
251462306a36Sopenharmony_ci					continue;
251562306a36Sopenharmony_ci				}
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci				new_slot->bus = hold_bus_node->base;
251862306a36Sopenharmony_ci				new_slot->device = device;
251962306a36Sopenharmony_ci				new_slot->function = 0;
252062306a36Sopenharmony_ci				new_slot->is_a_board = 1;
252162306a36Sopenharmony_ci				new_slot->status = 0;
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci				rc = configure_new_device(ctrl, new_slot, 1, &temp_resources);
252462306a36Sopenharmony_ci				dbg("configure_new_device rc=0x%x\n", rc);
252562306a36Sopenharmony_ci			}	/* End of IF (device in slot?) */
252662306a36Sopenharmony_ci		}		/* End of FOR loop */
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci		if (rc)
252962306a36Sopenharmony_ci			goto free_and_out;
253062306a36Sopenharmony_ci		/* save the interrupt routing information */
253162306a36Sopenharmony_ci		if (resources->irqs) {
253262306a36Sopenharmony_ci			resources->irqs->interrupt[0] = irqs.interrupt[0];
253362306a36Sopenharmony_ci			resources->irqs->interrupt[1] = irqs.interrupt[1];
253462306a36Sopenharmony_ci			resources->irqs->interrupt[2] = irqs.interrupt[2];
253562306a36Sopenharmony_ci			resources->irqs->interrupt[3] = irqs.interrupt[3];
253662306a36Sopenharmony_ci			resources->irqs->valid_INT = irqs.valid_INT;
253762306a36Sopenharmony_ci		} else if (!behind_bridge) {
253862306a36Sopenharmony_ci			/* We need to hook up the interrupts here */
253962306a36Sopenharmony_ci			for (cloop = 0; cloop < 4; cloop++) {
254062306a36Sopenharmony_ci				if (irqs.valid_INT & (0x01 << cloop)) {
254162306a36Sopenharmony_ci					rc = cpqhp_set_irq(func->bus, func->device,
254262306a36Sopenharmony_ci							   cloop + 1, irqs.interrupt[cloop]);
254362306a36Sopenharmony_ci					if (rc)
254462306a36Sopenharmony_ci						goto free_and_out;
254562306a36Sopenharmony_ci				}
254662306a36Sopenharmony_ci			}	/* end of for loop */
254762306a36Sopenharmony_ci		}
254862306a36Sopenharmony_ci		/* Return unused bus resources
254962306a36Sopenharmony_ci		 * First use the temporary node to store information for
255062306a36Sopenharmony_ci		 * the board */
255162306a36Sopenharmony_ci		if (bus_node && temp_resources.bus_head) {
255262306a36Sopenharmony_ci			hold_bus_node->length = bus_node->base - hold_bus_node->base;
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci			hold_bus_node->next = func->bus_head;
255562306a36Sopenharmony_ci			func->bus_head = hold_bus_node;
255662306a36Sopenharmony_ci
255762306a36Sopenharmony_ci			temp_byte = temp_resources.bus_head->base - 1;
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci			/* set subordinate bus */
256062306a36Sopenharmony_ci			rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_ci			if (temp_resources.bus_head->length == 0) {
256362306a36Sopenharmony_ci				kfree(temp_resources.bus_head);
256462306a36Sopenharmony_ci				temp_resources.bus_head = NULL;
256562306a36Sopenharmony_ci			} else {
256662306a36Sopenharmony_ci				return_resource(&(resources->bus_head), temp_resources.bus_head);
256762306a36Sopenharmony_ci			}
256862306a36Sopenharmony_ci		}
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci		/* If we have IO space available and there is some left,
257162306a36Sopenharmony_ci		 * return the unused portion */
257262306a36Sopenharmony_ci		if (hold_IO_node && temp_resources.io_head) {
257362306a36Sopenharmony_ci			io_node = do_pre_bridge_resource_split(&(temp_resources.io_head),
257462306a36Sopenharmony_ci							       &hold_IO_node, 0x1000);
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci			/* Check if we were able to split something off */
257762306a36Sopenharmony_ci			if (io_node) {
257862306a36Sopenharmony_ci				hold_IO_node->base = io_node->base + io_node->length;
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci				temp_byte = (hold_IO_node->base) >> 8;
258162306a36Sopenharmony_ci				rc = pci_bus_write_config_word(pci_bus, devfn, PCI_IO_BASE, temp_byte);
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci				return_resource(&(resources->io_head), io_node);
258462306a36Sopenharmony_ci			}
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci			io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000);
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci			/* Check if we were able to split something off */
258962306a36Sopenharmony_ci			if (io_node) {
259062306a36Sopenharmony_ci				/* First use the temporary node to store
259162306a36Sopenharmony_ci				 * information for the board */
259262306a36Sopenharmony_ci				hold_IO_node->length = io_node->base - hold_IO_node->base;
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci				/* If we used any, add it to the board's list */
259562306a36Sopenharmony_ci				if (hold_IO_node->length) {
259662306a36Sopenharmony_ci					hold_IO_node->next = func->io_head;
259762306a36Sopenharmony_ci					func->io_head = hold_IO_node;
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci					temp_byte = (io_node->base - 1) >> 8;
260062306a36Sopenharmony_ci					rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci					return_resource(&(resources->io_head), io_node);
260362306a36Sopenharmony_ci				} else {
260462306a36Sopenharmony_ci					/* it doesn't need any IO */
260562306a36Sopenharmony_ci					temp_word = 0x0000;
260662306a36Sopenharmony_ci					rc = pci_bus_write_config_word(pci_bus, devfn, PCI_IO_LIMIT, temp_word);
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci					return_resource(&(resources->io_head), io_node);
260962306a36Sopenharmony_ci					kfree(hold_IO_node);
261062306a36Sopenharmony_ci				}
261162306a36Sopenharmony_ci			} else {
261262306a36Sopenharmony_ci				/* it used most of the range */
261362306a36Sopenharmony_ci				hold_IO_node->next = func->io_head;
261462306a36Sopenharmony_ci				func->io_head = hold_IO_node;
261562306a36Sopenharmony_ci			}
261662306a36Sopenharmony_ci		} else if (hold_IO_node) {
261762306a36Sopenharmony_ci			/* it used the whole range */
261862306a36Sopenharmony_ci			hold_IO_node->next = func->io_head;
261962306a36Sopenharmony_ci			func->io_head = hold_IO_node;
262062306a36Sopenharmony_ci		}
262162306a36Sopenharmony_ci		/* If we have memory space available and there is some left,
262262306a36Sopenharmony_ci		 * return the unused portion */
262362306a36Sopenharmony_ci		if (hold_mem_node && temp_resources.mem_head) {
262462306a36Sopenharmony_ci			mem_node = do_pre_bridge_resource_split(&(temp_resources.  mem_head),
262562306a36Sopenharmony_ci								&hold_mem_node, 0x100000);
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci			/* Check if we were able to split something off */
262862306a36Sopenharmony_ci			if (mem_node) {
262962306a36Sopenharmony_ci				hold_mem_node->base = mem_node->base + mem_node->length;
263062306a36Sopenharmony_ci
263162306a36Sopenharmony_ci				temp_word = (hold_mem_node->base) >> 16;
263262306a36Sopenharmony_ci				rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci				return_resource(&(resources->mem_head), mem_node);
263562306a36Sopenharmony_ci			}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci			mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000);
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci			/* Check if we were able to split something off */
264062306a36Sopenharmony_ci			if (mem_node) {
264162306a36Sopenharmony_ci				/* First use the temporary node to store
264262306a36Sopenharmony_ci				 * information for the board */
264362306a36Sopenharmony_ci				hold_mem_node->length = mem_node->base - hold_mem_node->base;
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci				if (hold_mem_node->length) {
264662306a36Sopenharmony_ci					hold_mem_node->next = func->mem_head;
264762306a36Sopenharmony_ci					func->mem_head = hold_mem_node;
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci					/* configure end address */
265062306a36Sopenharmony_ci					temp_word = (mem_node->base - 1) >> 16;
265162306a36Sopenharmony_ci					rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci					/* Return unused resources to the pool */
265462306a36Sopenharmony_ci					return_resource(&(resources->mem_head), mem_node);
265562306a36Sopenharmony_ci				} else {
265662306a36Sopenharmony_ci					/* it doesn't need any Mem */
265762306a36Sopenharmony_ci					temp_word = 0x0000;
265862306a36Sopenharmony_ci					rc = pci_bus_write_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_ci					return_resource(&(resources->mem_head), mem_node);
266162306a36Sopenharmony_ci					kfree(hold_mem_node);
266262306a36Sopenharmony_ci				}
266362306a36Sopenharmony_ci			} else {
266462306a36Sopenharmony_ci				/* it used most of the range */
266562306a36Sopenharmony_ci				hold_mem_node->next = func->mem_head;
266662306a36Sopenharmony_ci				func->mem_head = hold_mem_node;
266762306a36Sopenharmony_ci			}
266862306a36Sopenharmony_ci		} else if (hold_mem_node) {
266962306a36Sopenharmony_ci			/* it used the whole range */
267062306a36Sopenharmony_ci			hold_mem_node->next = func->mem_head;
267162306a36Sopenharmony_ci			func->mem_head = hold_mem_node;
267262306a36Sopenharmony_ci		}
267362306a36Sopenharmony_ci		/* If we have prefetchable memory space available and there
267462306a36Sopenharmony_ci		 * is some left at the end, return the unused portion */
267562306a36Sopenharmony_ci		if (temp_resources.p_mem_head) {
267662306a36Sopenharmony_ci			p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
267762306a36Sopenharmony_ci								  &hold_p_mem_node, 0x100000);
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci			/* Check if we were able to split something off */
268062306a36Sopenharmony_ci			if (p_mem_node) {
268162306a36Sopenharmony_ci				hold_p_mem_node->base = p_mem_node->base + p_mem_node->length;
268262306a36Sopenharmony_ci
268362306a36Sopenharmony_ci				temp_word = (hold_p_mem_node->base) >> 16;
268462306a36Sopenharmony_ci				rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
268562306a36Sopenharmony_ci
268662306a36Sopenharmony_ci				return_resource(&(resources->p_mem_head), p_mem_node);
268762306a36Sopenharmony_ci			}
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci			p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000);
269062306a36Sopenharmony_ci
269162306a36Sopenharmony_ci			/* Check if we were able to split something off */
269262306a36Sopenharmony_ci			if (p_mem_node) {
269362306a36Sopenharmony_ci				/* First use the temporary node to store
269462306a36Sopenharmony_ci				 * information for the board */
269562306a36Sopenharmony_ci				hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base;
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci				/* If we used any, add it to the board's list */
269862306a36Sopenharmony_ci				if (hold_p_mem_node->length) {
269962306a36Sopenharmony_ci					hold_p_mem_node->next = func->p_mem_head;
270062306a36Sopenharmony_ci					func->p_mem_head = hold_p_mem_node;
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci					temp_word = (p_mem_node->base - 1) >> 16;
270362306a36Sopenharmony_ci					rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci					return_resource(&(resources->p_mem_head), p_mem_node);
270662306a36Sopenharmony_ci				} else {
270762306a36Sopenharmony_ci					/* it doesn't need any PMem */
270862306a36Sopenharmony_ci					temp_word = 0x0000;
270962306a36Sopenharmony_ci					rc = pci_bus_write_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci					return_resource(&(resources->p_mem_head), p_mem_node);
271262306a36Sopenharmony_ci					kfree(hold_p_mem_node);
271362306a36Sopenharmony_ci				}
271462306a36Sopenharmony_ci			} else {
271562306a36Sopenharmony_ci				/* it used the most of the range */
271662306a36Sopenharmony_ci				hold_p_mem_node->next = func->p_mem_head;
271762306a36Sopenharmony_ci				func->p_mem_head = hold_p_mem_node;
271862306a36Sopenharmony_ci			}
271962306a36Sopenharmony_ci		} else if (hold_p_mem_node) {
272062306a36Sopenharmony_ci			/* it used the whole range */
272162306a36Sopenharmony_ci			hold_p_mem_node->next = func->p_mem_head;
272262306a36Sopenharmony_ci			func->p_mem_head = hold_p_mem_node;
272362306a36Sopenharmony_ci		}
272462306a36Sopenharmony_ci		/* We should be configuring an IRQ and the bridge's base address
272562306a36Sopenharmony_ci		 * registers if it needs them.  Although we have never seen such
272662306a36Sopenharmony_ci		 * a device */
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci		/* enable card */
272962306a36Sopenharmony_ci		command = 0x0157;	/* = PCI_COMMAND_IO |
273062306a36Sopenharmony_ci					 *   PCI_COMMAND_MEMORY |
273162306a36Sopenharmony_ci					 *   PCI_COMMAND_MASTER |
273262306a36Sopenharmony_ci					 *   PCI_COMMAND_INVALIDATE |
273362306a36Sopenharmony_ci					 *   PCI_COMMAND_PARITY |
273462306a36Sopenharmony_ci					 *   PCI_COMMAND_SERR */
273562306a36Sopenharmony_ci		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci		/* set Bridge Control Register */
273862306a36Sopenharmony_ci		command = 0x07;		/* = PCI_BRIDGE_CTL_PARITY |
273962306a36Sopenharmony_ci					 *   PCI_BRIDGE_CTL_SERR |
274062306a36Sopenharmony_ci					 *   PCI_BRIDGE_CTL_NO_ISA */
274162306a36Sopenharmony_ci		rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
274262306a36Sopenharmony_ci	} else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
274362306a36Sopenharmony_ci		/* Standard device */
274462306a36Sopenharmony_ci		rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code);
274562306a36Sopenharmony_ci
274662306a36Sopenharmony_ci		if (class_code == PCI_BASE_CLASS_DISPLAY) {
274762306a36Sopenharmony_ci			/* Display (video) adapter (not supported) */
274862306a36Sopenharmony_ci			return DEVICE_TYPE_NOT_SUPPORTED;
274962306a36Sopenharmony_ci		}
275062306a36Sopenharmony_ci		/* Figure out IO and memory needs */
275162306a36Sopenharmony_ci		for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
275262306a36Sopenharmony_ci			temp_register = 0xFFFFFFFF;
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_ci			dbg("CND: bus=%d, devfn=%d, offset=%d\n", pci_bus->number, devfn, cloop);
275562306a36Sopenharmony_ci			rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
275662306a36Sopenharmony_ci
275762306a36Sopenharmony_ci			rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register);
275862306a36Sopenharmony_ci			dbg("CND: base = 0x%x\n", temp_register);
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci			if (temp_register) {	  /* If this register is implemented */
276162306a36Sopenharmony_ci				if ((temp_register & 0x03L) == 0x01) {
276262306a36Sopenharmony_ci					/* Map IO */
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci					/* set base = amount of IO space */
276562306a36Sopenharmony_ci					base = temp_register & 0xFFFFFFFC;
276662306a36Sopenharmony_ci					base = ~base + 1;
276762306a36Sopenharmony_ci
276862306a36Sopenharmony_ci					dbg("CND:      length = 0x%x\n", base);
276962306a36Sopenharmony_ci					io_node = get_io_resource(&(resources->io_head), base);
277062306a36Sopenharmony_ci					if (!io_node)
277162306a36Sopenharmony_ci						return -ENOMEM;
277262306a36Sopenharmony_ci					dbg("Got io_node start = %8.8x, length = %8.8x next (%p)\n",
277362306a36Sopenharmony_ci					    io_node->base, io_node->length, io_node->next);
277462306a36Sopenharmony_ci					dbg("func (%p) io_head (%p)\n", func, func->io_head);
277562306a36Sopenharmony_ci
277662306a36Sopenharmony_ci					/* allocate the resource to the board */
277762306a36Sopenharmony_ci					base = io_node->base;
277862306a36Sopenharmony_ci					io_node->next = func->io_head;
277962306a36Sopenharmony_ci					func->io_head = io_node;
278062306a36Sopenharmony_ci				} else if ((temp_register & 0x0BL) == 0x08) {
278162306a36Sopenharmony_ci					/* Map prefetchable memory */
278262306a36Sopenharmony_ci					base = temp_register & 0xFFFFFFF0;
278362306a36Sopenharmony_ci					base = ~base + 1;
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci					dbg("CND:      length = 0x%x\n", base);
278662306a36Sopenharmony_ci					p_mem_node = get_resource(&(resources->p_mem_head), base);
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_ci					/* allocate the resource to the board */
278962306a36Sopenharmony_ci					if (p_mem_node) {
279062306a36Sopenharmony_ci						base = p_mem_node->base;
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci						p_mem_node->next = func->p_mem_head;
279362306a36Sopenharmony_ci						func->p_mem_head = p_mem_node;
279462306a36Sopenharmony_ci					} else
279562306a36Sopenharmony_ci						return -ENOMEM;
279662306a36Sopenharmony_ci				} else if ((temp_register & 0x0BL) == 0x00) {
279762306a36Sopenharmony_ci					/* Map memory */
279862306a36Sopenharmony_ci					base = temp_register & 0xFFFFFFF0;
279962306a36Sopenharmony_ci					base = ~base + 1;
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci					dbg("CND:      length = 0x%x\n", base);
280262306a36Sopenharmony_ci					mem_node = get_resource(&(resources->mem_head), base);
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_ci					/* allocate the resource to the board */
280562306a36Sopenharmony_ci					if (mem_node) {
280662306a36Sopenharmony_ci						base = mem_node->base;
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci						mem_node->next = func->mem_head;
280962306a36Sopenharmony_ci						func->mem_head = mem_node;
281062306a36Sopenharmony_ci					} else
281162306a36Sopenharmony_ci						return -ENOMEM;
281262306a36Sopenharmony_ci				} else {
281362306a36Sopenharmony_ci					/* Reserved bits or requesting space below 1M */
281462306a36Sopenharmony_ci					return NOT_ENOUGH_RESOURCES;
281562306a36Sopenharmony_ci				}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci				rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
281862306a36Sopenharmony_ci
281962306a36Sopenharmony_ci				/* Check for 64-bit base */
282062306a36Sopenharmony_ci				if ((temp_register & 0x07L) == 0x04) {
282162306a36Sopenharmony_ci					cloop += 4;
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci					/* Upper 32 bits of address always zero
282462306a36Sopenharmony_ci					 * on today's systems */
282562306a36Sopenharmony_ci					/* FIXME this is probably not true on
282662306a36Sopenharmony_ci					 * Alpha and ia64??? */
282762306a36Sopenharmony_ci					base = 0;
282862306a36Sopenharmony_ci					rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
282962306a36Sopenharmony_ci				}
283062306a36Sopenharmony_ci			}
283162306a36Sopenharmony_ci		}		/* End of base register loop */
283262306a36Sopenharmony_ci		if (cpqhp_legacy_mode) {
283362306a36Sopenharmony_ci			/* Figure out which interrupt pin this function uses */
283462306a36Sopenharmony_ci			rc = pci_bus_read_config_byte(pci_bus, devfn,
283562306a36Sopenharmony_ci				PCI_INTERRUPT_PIN, &temp_byte);
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ci			/* If this function needs an interrupt and we are behind
283862306a36Sopenharmony_ci			 * a bridge and the pin is tied to something that's
283962306a36Sopenharmony_ci			 * already mapped, set this one the same */
284062306a36Sopenharmony_ci			if (temp_byte && resources->irqs &&
284162306a36Sopenharmony_ci			    (resources->irqs->valid_INT &
284262306a36Sopenharmony_ci			     (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
284362306a36Sopenharmony_ci				/* We have to share with something already set up */
284462306a36Sopenharmony_ci				IRQ = resources->irqs->interrupt[(temp_byte +
284562306a36Sopenharmony_ci					resources->irqs->barber_pole - 1) & 0x03];
284662306a36Sopenharmony_ci			} else {
284762306a36Sopenharmony_ci				/* Program IRQ based on card type */
284862306a36Sopenharmony_ci				rc = pci_bus_read_config_byte(pci_bus, devfn, 0x0B, &class_code);
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci				if (class_code == PCI_BASE_CLASS_STORAGE)
285162306a36Sopenharmony_ci					IRQ = cpqhp_disk_irq;
285262306a36Sopenharmony_ci				else
285362306a36Sopenharmony_ci					IRQ = cpqhp_nic_irq;
285462306a36Sopenharmony_ci			}
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci			/* IRQ Line */
285762306a36Sopenharmony_ci			rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ);
285862306a36Sopenharmony_ci		}
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci		if (!behind_bridge) {
286162306a36Sopenharmony_ci			rc = cpqhp_set_irq(func->bus, func->device, temp_byte, IRQ);
286262306a36Sopenharmony_ci			if (rc)
286362306a36Sopenharmony_ci				return 1;
286462306a36Sopenharmony_ci		} else {
286562306a36Sopenharmony_ci			/* TBD - this code may also belong in the other clause
286662306a36Sopenharmony_ci			 * of this If statement */
286762306a36Sopenharmony_ci			resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ;
286862306a36Sopenharmony_ci			resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03;
286962306a36Sopenharmony_ci		}
287062306a36Sopenharmony_ci
287162306a36Sopenharmony_ci		/* Latency Timer */
287262306a36Sopenharmony_ci		temp_byte = 0x40;
287362306a36Sopenharmony_ci		rc = pci_bus_write_config_byte(pci_bus, devfn,
287462306a36Sopenharmony_ci					PCI_LATENCY_TIMER, temp_byte);
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci		/* Cache Line size */
287762306a36Sopenharmony_ci		temp_byte = 0x08;
287862306a36Sopenharmony_ci		rc = pci_bus_write_config_byte(pci_bus, devfn,
287962306a36Sopenharmony_ci					PCI_CACHE_LINE_SIZE, temp_byte);
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci		/* disable ROM base Address */
288262306a36Sopenharmony_ci		temp_dword = 0x00L;
288362306a36Sopenharmony_ci		rc = pci_bus_write_config_word(pci_bus, devfn,
288462306a36Sopenharmony_ci					PCI_ROM_ADDRESS, temp_dword);
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ci		/* enable card */
288762306a36Sopenharmony_ci		temp_word = 0x0157;	/* = PCI_COMMAND_IO |
288862306a36Sopenharmony_ci					 *   PCI_COMMAND_MEMORY |
288962306a36Sopenharmony_ci					 *   PCI_COMMAND_MASTER |
289062306a36Sopenharmony_ci					 *   PCI_COMMAND_INVALIDATE |
289162306a36Sopenharmony_ci					 *   PCI_COMMAND_PARITY |
289262306a36Sopenharmony_ci					 *   PCI_COMMAND_SERR */
289362306a36Sopenharmony_ci		rc = pci_bus_write_config_word(pci_bus, devfn,
289462306a36Sopenharmony_ci					PCI_COMMAND, temp_word);
289562306a36Sopenharmony_ci	} else {		/* End of Not-A-Bridge else */
289662306a36Sopenharmony_ci		/* It's some strange type of PCI adapter (Cardbus?) */
289762306a36Sopenharmony_ci		return DEVICE_TYPE_NOT_SUPPORTED;
289862306a36Sopenharmony_ci	}
289962306a36Sopenharmony_ci
290062306a36Sopenharmony_ci	func->configured = 1;
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	return 0;
290362306a36Sopenharmony_cifree_and_out:
290462306a36Sopenharmony_ci	cpqhp_destroy_resource_list(&temp_resources);
290562306a36Sopenharmony_ci
290662306a36Sopenharmony_ci	return_resource(&(resources->bus_head), hold_bus_node);
290762306a36Sopenharmony_ci	return_resource(&(resources->io_head), hold_IO_node);
290862306a36Sopenharmony_ci	return_resource(&(resources->mem_head), hold_mem_node);
290962306a36Sopenharmony_ci	return_resource(&(resources->p_mem_head), hold_p_mem_node);
291062306a36Sopenharmony_ci	return rc;
291162306a36Sopenharmony_ci}
2912