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