/*
 * Copyright (c) 2004, 2005 PMC-Sierra, Inc  (www.pmc-sierra.com)
 * 	Added Hypertransport support	brad_larson@pmc-sierra.com
 * Copyright (c) 2001 Opsycon AB  (www.opsycon.se)
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Opsycon AB, Sweden.
 *	This product includes software developed by PMC-Sierra, Inc.
 *	This product includes software developed by Alliance Semiconductor.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/param.h>
#include <sys/device.h>
#include <sys/systm.h>
#include <sys/malloc.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/nppbreg.h>

#include <machine/bus.h>

#include "include/sequoia.h"
#include <machine/rm9000_reg.h>
#include <machine/rm9150_ht.h>
#include <machine/rm9150_pci.h>
#include <machine/rm9150_reg.h>

#include <pmon.h>

#include "htreg.h"

#define HT_DEBUG

extern void *pmalloc (size_t);
extern char *getenv(const char *);

/* 
 *  Built-in delay is not available until the realtime clock 
 *  is initialized so this crude version is used.
 */
#define _DELAY(n)	{ volatile register int N = (n); while (--N > 0); }

#define PCI_CONFIG_ENABLE	0x80000000
#define PCI_REVISION_REG	0x08
#define PCI_BRIDGE_CONTROL	0x3c

/* 
 *  Hypertransport definitions 
 */
#define HT_BUS		2
#define	HT_200MHZ	0x0
#define HT_300MHZ	0x1
#define HT_400MHZ	0x2
#define HT_500MHZ	0x3
#define HT_600MHZ	0x4
#define HT_800MHZ	0x5
#define HT_1000MHZ	0x6

/* Offset from capability 1 pointer */
#define CAP1_COMMAND		0x00
#define CAP1_LINK_CONFIG_0	0x04
#define CAP1_LINK_CONTROL_0	0x04
#define CAP1_LINK_CONFIG_1	0x08
#define CAP1_LINK_CONTROL_1	0x08
#define CAP1_LINK_FREQ_CAP_0	0x0c
#define CAP1_LINK_FREQ_0	0x0c
#define CAP1_LINK_FREQ_CAP_1	0x10
#define CAP1_LINK_FREQ_1	0x10

#define MASTER_HOST_SHIFT	(16 + 10)
#define MASTER_HOST_MASK	0x1
#define LINK_WIDTH_IN_SHIFT	(16 + 8)
#define LINK_WIDTH_OUT_SHIFT	(16 + 12)
#define LINK_WIDTH_MASK		0x7
#define LINK_FREQ_SHIFT		8	
#define LINK_FREQ_MASK		0xf
#define LINK_FREQ_CAP_SHIFT	16
#define LINK_FREQ_CAP_MASK	0xff

/* PCI i/o regions in PCI space */
#define PCI_IO_SPACE_PCI_BASE		0x00000000

/* PCI mem regions in PCI space */
#define PCI_LOCAL_MEM_PCI_BASE		0x00000000	/* CPU Mem accessed from PCI */

/* soft versions of above */
static pcireg_t pci_local_mem_pci_base;

/*
 *  Local functions
 */
static int rm9k_ht_reset(void);
static pcireg_t _pci_conf_readn (pcitag_t, int, int);
static void _pci_conf_writen (pcitag_t, int, pcireg_t, int);

/*
 *  External data
 */
extern int _pcidebug;
extern char hwethadr[6];

/*
 *  Local data
 */
static int dbg = 0;
struct pci_bus *_pci_bus[16];
int _max_pci_bus = 0;
int hypertransport_enable;


#if defined(ENABLE_HT)

#define HT_WARM_RESET 1
#define HT_COLD_RESET 0

/* 0x01c0: RM9150_HT_CAPABILITY_ID_PTR_CMD definitions */
#define HTLCC_CAPTYPE_OFFSET	29
#define HTLCC_DEVNUM_OFFSET	18
#define HTLCC_DBLENDED_OFFSET	17
#define HTLCC_WARMRST_OFFSET	16
#define HTLCC_CAPPTR_OFFSET	8
#define HTLCC_CAPID_OFFSET	0

/* 0x0644: HTLINK definitions */
#define HTLINK_LWO_OFFSET	28
#define HTLINK_LWI_OFFSET	24
#define HTLINK_MAXLWO_OFFSET	20
#define HTLINK_MAXLWI_OFFSET	16
#define HTLINK_STOP_OFFSET	13
#define HTLINK_IE_OFFSET	12
#define HTLINK_CRC_OFFSET	8
#define HTLINK_TXO_OFFSET	7
#define HTLINK_EOC_OFFSET	6
#define HTLINK_INIT_OFFSET	5
#define HTLINK_LF_OFFSET	4
#define HTLINK_CFOER_OFFSET	3
#define HTLINK_CST_OFFSET	2
#define HTLINK_CFLEN_OFFSET	1

/*
 *  Local Function Prototypes
 */
static int  _cfg_read(int bus, int dev, int reg);
static void _cfg_write(int bus, int dev, int reg, int data);

static int  _get_capability_ptr(int dev, int capability);

static int  _ht_chain_init(int freqResize);
static int  _ht_get_freq_cap(int dev, int link);
static int  _ht_get_max_freq(int freqBitmap);
static void _ht_set_freq_cap(int dev, int link, int freq);
static int  _ht_encode_freq(int frequency);
static int  _ht_decode_freq(int code);
static void _ht_set_eoc(int dev, int link);
static void _ht_reset(int type); 


/*
 * Local Function Implementations
 */
 
/*****************************************************************************

	Function: _cfg_read
	Description: CSR read access
					
	Parameters: 
		bus - PCI/HT bus number
		dev - PCI/HT device number.
		reg - CSR register number.
	note:   function number is fixed to 0.		
******************************************************************************/

static int
_cfg_read(int bus, int dev, int reg)
{	
	pcireg_t data;

        RM9150_HT_WRITE(RM9150_HT_CONFIG_ADDR, 
		(bus<<16) | (dev<<11) | PCI_CONFIG_ENABLE | reg);

	if (load_word((register_t)(PHYS_TO_UNCACHED(RM9150_HT_DCR) + 
			RM9150_HT_CONFIG_DATA), &data))
		data = ~0;

        return data;
}

/*****************************************************************************

	Function: _cfg_write
	Description: CSR write access
					
	Parameters: 
		bus - PCI/HT bus number
		dev - PCI/HT device number.
		reg - CSR register number.
		data - value to be written.
	note:   function number is fixed to 0.		
******************************************************************************/

static void
_cfg_write(int bus, int dev, int reg, int data)
{	
        RM9150_HT_WRITE(RM9150_HT_CONFIG_ADDR, 
		(bus<<16) | (dev<<11) | PCI_CONFIG_ENABLE | reg);
        RM9150_HT_WRITE(RM9150_HT_CONFIG_DATA, data);

}

/*****************************************************************************

	Function: _get_capability_ptr
	
	Description: Utility function to get the offset of a particular capablity		
					
	Parameters: 
		dev - PCI/HT device number.
		capability - capablity ID to search for (0x8 for HT capablity)		
******************************************************************************/

static int
_get_capability_ptr(int dev, int capability)
{
	int capPtr;
	int capId;
	
	capPtr = _cfg_read(0, dev, PCI_CAPLISTPTR_REG);
	while (capPtr !=0) {
		capId = _cfg_read(0, dev, capPtr);

        	if (dbg) printf("_get_capability_ptr, capId 0x%x, PCI_CAPLIST_CAP(capId) 0x%x, capPtr 0x%x\n",
				 capId, PCI_CAPLIST_CAP(capId), capPtr);
                                                                                                                
                if (PCI_CAPLIST_CAP(capId)==capability && !(capId & 0xe0000000)) break;
                capPtr = PCI_CAPLIST_NEXT(capId);
	}
	return capPtr;						
}												
										
/*****************************************************************************

	Function: _ht_reset
	Description: Assert HT reset
	Parameters:
		HT_WARM_RESET - Assert warm reset
		HT_COLD_RESET - Assert cold reset	
				
******************************************************************************/

static void
_ht_reset(int type)
{
	int regData;
	int i;

	/* 
	 *  Reset RM9000 secondary bus 
	 */
	regData = RM9150_HT_READ(RM9150_HT_CAPABILITY_ID_PTR_CMD);
	if (type==HT_WARM_RESET) {
		if (dbg) printf("Asserting HT warm reset\n");
		regData |= HT_BIT(HTLCC_WARMRST_OFFSET);
	}
	else if (type==HT_COLD_RESET) {
		if (dbg) printf("Asserting HT cold reset\n");
		regData = HT_BCLR(regData, HTLCC_WARMRST_OFFSET);
	}
	else {
		printf("Unknown Reset type. Reset not asserted.\n");	
		return;
	}
        RM9150_HT_WRITE(RM9150_HT_CAPABILITY_ID_PTR_CMD, regData);
	
        /* Reset the link */
	regData = RM9150_HT_READ(RM9150_HT_LINK_CONTROL) | (1<<4);
        RM9150_HT_WRITE(RM9150_HT_LINK_CONTROL, regData);
        RM9150_HT_READ(RM9150_HT_LINK_CONTROL);
	for (i = 0; i < 100000; i++) {
                RM9150_HT_READ(RM9150_HT_LINK_CONTROL_CONFIG);
	}

        /* Turn off reset */
	regData = RM9150_HT_READ(RM9150_HT_LINK_CONTROL) & 0xffffffef;
        RM9150_HT_WRITE(RM9150_HT_LINK_CONTROL, regData);
        RM9150_HT_READ(RM9150_HT_LINK_CONTROL);
        for (i = 0; i < 100000; i++)
                RM9150_HT_READ(RM9150_HT_LINK_CONTROL_CONFIG);

        /* Wait for HW init to complete */
        for (i = 0; i < 10000000; i++) {
                if (RM9150_HT_READ(RM9150_HT_LINK_CONTROL_CONFIG) & HT_BIT(HTLINK_INIT_OFFSET))
                        break;
	}

        /* Enable the master again */
        RM9150_HT_WRITE(RM9150_HT_STATUS_CMD, 6);
}


/*****************************************************************************

	Function: _ht_set_eoc	
	Description: terminate the HT chain by setting EOC and XmitOff	
		EOC bit has to be set first before turning off the transmit.
					
	Parameters: 
		dev - PCI/HT device number.
		link - HT link number.		
******************************************************************************/

static void
_ht_set_eoc(int dev, int link)
{
	int htCapPtr;
	int stat;
	
	htCapPtr = _get_capability_ptr(dev, PCI_CAP_ID_LDT);
	/* set EOC and XmitOff */
	if (dbg) printf("HT unitID %d: link %d end-of-chain and transmit off\n",dev, link);
	
	stat = _cfg_read(0, dev, htCapPtr + CAP1_LINK_CONTROL_0 + 4*link);	

        /* set end-of-chain bit */
        stat |= HT_BIT(HT_LNKCTRL_EOC);

	/* write and readback to force a posting */
	_cfg_write(0, dev, htCapPtr + CAP1_LINK_CONTROL_0 + 4*link,stat);
	stat = _cfg_read(0, dev, htCapPtr + CAP1_LINK_CONTROL_0 + 4*link);
		
        /* set 'transmit off' bit */
        stat |=HT_BIT(HT_LNKCTRL_TXOFF);

	/* write and readback to force a posting */
	_cfg_write(0, dev, htCapPtr + CAP1_LINK_CONTROL_0 + 4*link,stat);
	stat = _cfg_read(0, dev, htCapPtr + CAP1_LINK_CONTROL_0 + 4*link);
}		


/*****************************************************************************

	Function: _ht_get_freq_cap
	
	Description: read in the frequency capability bitmap value		
					
	Parameters: 
		dev - PCI/HT device number.
		link - HT link number.	
			
******************************************************************************/

static int
_ht_get_freq_cap(int dev, int link)
{
	int htCapPtr;
	int freqCapCsr;	
	
	htCapPtr = _get_capability_ptr(dev, PCI_CAP_ID_LDT);
	freqCapCsr = _cfg_read(0, dev, htCapPtr+HT_PRI_REV_ID_REG+link*4);
	return HT_LNKFREQCAP(freqCapCsr);	
}		


/*****************************************************************************

	Function: _ht_get_max_freq
	
	Description: converts freq bitmap to the maximum freq encode value		
					
	Parameters: 
		freqBitmap: frequency capability bit map
******************************************************************************/

static int
_ht_get_max_freq(int freqBitmap)
{
	int htFreq=0;
	//printf("_ht_get_max_freq: freqBitmap=0x%04x, ",freqBitmap);	
	freqBitmap &= 0x7F; /* only valid upto bit #6 */	
	freqBitmap |= HT_BIT(HT_LNKFREQ_200MHZ); /* always capable of 200MHz */
	
	while (freqBitmap!=1) {
		htFreq++;
		freqBitmap >>= 1;
	}
	return htFreq;						
}		

/*****************************************************************************

	Function: _ht_encode_freq	
	Description: get the encoded frequency value
		200MHz  = HT_LNKFREQ_200MHZ (0)
		300MHz  = HT_LNKFREQ_300MHZ (1)
		400MHz  = HT_LNKFREQ_400MHZ (2)
		500MHz  = HT_LNKFREQ_500MHZ (3)					
		600MHz  = HT_LNKFREQ_600MHZ (4)			
		800MHz  = HT_LNKFREQ_800MHZ (5)
		1000MHz = HT_LNKFREQ_1000MHZ (6)					
	Parameters: 
		frequency - frequency value to be encoded	
******************************************************************************/

static int
_ht_encode_freq(int frequency)
{
	if (frequency >= 1000) return HT_LNKFREQ_1000MHZ;	
	if (frequency >= 800) return HT_LNKFREQ_800MHZ;	
	if (frequency >= 600) return HT_LNKFREQ_600MHZ;	
	if (frequency >= 500) return HT_LNKFREQ_500MHZ;	
	if (frequency >= 400) return HT_LNKFREQ_400MHZ;	
	if (frequency >= 300) return HT_LNKFREQ_300MHZ;	
	return HT_LNKFREQ_200MHZ;
}		

/*****************************************************************************

	Function: _ht_decode_freq	
	Description: get the decoded frequency value
		HT_LNKFREQ_200MHZ (0) = 200.
		HT_LNKFREQ_300MHZ (1) = 300.
		HT_LNKFREQ_400MHZ (2) = 400.
		HT_LNKFREQ_500MHZ (3) = 500.
		HT_LNKFREQ_600MHZ (4) = 600.
		HT_LNKFREQ_800MHZ (5) = 800.
		HT_LNKFREQ_1000MHZ (6) = 1000.
			
	Parameters: 
		frequency - frequency code value	
******************************************************************************/

static int
_ht_decode_freq(int code)
{
	switch (code) {
		case HT_LNKFREQ_200MHZ: return 200;
		case HT_LNKFREQ_300MHZ: return 300;
		case HT_LNKFREQ_400MHZ: return 400;
		case HT_LNKFREQ_500MHZ: return 500;
		case HT_LNKFREQ_600MHZ: return 600;
		case HT_LNKFREQ_800MHZ: return 800;
		case HT_LNKFREQ_1000MHZ: return 1000;
	}
	return 200;		
}		

/*****************************************************************************

	Function: _ht_set_freq_cap
	
	Description: set the HT link frquency value		
					
	Parameters: 
		dev - PCI/HT device number.
		link - HT link number.	
		freq - frequency value to be programmed to	
******************************************************************************/

static void
_ht_set_freq_cap(int dev, int link, int freq)
{
	int freqCsr;
	int htCapPtr;

	if (dbg) printf("HT unitID %d: link %d, setting transmit link frequency to %d MHz\n",
			dev, link, 200 + freq*100);

	if (dev==0) {
		/* host interface */
		/* RM9000 transmit link */
		freqCsr = RM9150_HT_READ(RM9150_HT_REVISION_ID_LINK_FREQ);
		freqCsr &= 0xfffff0ff;
		freqCsr |= (freq<<8);
		printf("\n1: setting RILF 0x%08x\n\n", freqCsr);
		RM9150_HT_WRITE(RM9150_HT_REVISION_ID_LINK_FREQ, freqCsr);

		//printf("_ht_set_freq_cap: dev=%d, link=%d freq=%d, freqCsr=0x%08x\n",
		//	dev, link, freq, freqCsr);				
		return;
	}	
	htCapPtr = _get_capability_ptr(dev, PCI_CAP_ID_LDT);	
	freqCsr = _cfg_read(0, dev, htCapPtr+HT_LNK_FREQ_ERR_REG+link*4);
	freqCsr = HT_LNKFREQ_SET(freqCsr,freq);
	_cfg_write(0, dev, htCapPtr+HT_LNK_FREQ_ERR_REG+link*4, freqCsr);
	freqCsr = _cfg_read(0, dev, htCapPtr+HT_LNK_FREQ_ERR_REG+link*4);

	//printf("_ht_set_freq_cap: dev=%d, link=%d freq=%d, csr@[%2x]=0x%08x\n",
		//dev, link, freq, htCapPtr+HT_LNK_FREQ_ERR_REG+link*4, freqCsr);		
}		

									
/*****************************************************************************

	Function: _ht_chain_init
	
	Description: Initialize HT chain.			
					
	Algorithm:
		read Host's HT Link Status.
		while (!LinkFail & InitDone) {
			1. Read device/vendor ID of UnitID=0.
			2. Set the Master Host Direction.
			3. Check for error in the incoming link.
			4. Assign the UnitID.
			5. Read LinkStatus of downstream link.
		}
		set EOC and XmitOFF.

	Parameters: 
		freqResize: non-zero value causes HT link freq registers 
		            to be reprogrammed.

	Note: Does not implement double hosted chain support.

******************************************************************************/
static int 
_ht_chain_init(int freqResize)
{
	int nextFreeID=1;
	int linkControl;
	int linkNumber;
	int farLink;
	int deviceId;
	int revision;
	int command;
	int control;
	int htCapPtr;
	int updateUid;
	int unitId=nextFreeID;
	int stat;
	int regData;
	int i,j;
	/* freq Resize related variables */
	int prevFreqCap, currFreqCap;
	int prevUid, prevLink;
	int htFreqLimit, maxFreq, linkFreq;
	char *v;
	int newFreq;
	int freqValue;
	int tx_num, tx_den;

	printf("_ht_chain_init: freqResize %d\n", freqResize);

	/*
	 *  Determine maximum allowed link frequency
	 */ 
	if (freqResize) {
		prevLink=0;
		prevUid=0;
		regData = RM9150_HT_READ(RM9150_HT_REVISION_ID_LINK_FREQ);
		printf("_ht_chain_init: regData 0x%08x\n", regData);
		prevFreqCap= HT_LNKFREQCAP(regData);

		/* user control over link frequency */
		v = getenv("htfreq");
		if (v) {
			htFreqLimit = atol(v);
			if (htFreqLimit > 600)
				htFreqLimit = 600;	/* artificial limit */
			if (dbg) printf("             user limiting link frequency to %d MHz\n", htFreqLimit);
		}
		else
			htFreqLimit = 600;		/* artificial limit */
		/*htFreqLimit = 200;*/			/* xxx artificial limit */
		maxFreq = _ht_encode_freq(htFreqLimit);
	}

	/*
	 *  Configure each device in the chain
	 */	
	linkControl = RM9150_HT_READ(RM9150_HT_LINK_CONTROL_CONFIG);
	printf("_ht_chain_init: linkControl 0x%08x\n", linkControl);
	while (!(linkControl&HT_BIT(HT_LNKCTRL_LKFAIL)) && (linkControl&HT_BIT(HT_LNKCTRL_INIT)) )
	{
		deviceId = _cfg_read(0, 0, 0);
		printf("_ht_chain_init: deviceId 0x%08x\n", deviceId);

		/* 1. get the revision */
		revision = _cfg_read(0, 0, PCI_REVISION_REG) & PCI_REVISION_MASK;
			
		/* 2. Set the Master Host Direction */
		htCapPtr = _get_capability_ptr(0, PCI_CAP_ID_LDT);
		command = _cfg_read(0, 0, htCapPtr + CAP1_COMMAND);
		_cfg_write(0, 0, htCapPtr + CAP1_COMMAND, command);	/* sets master host bit */
		linkNumber = HT_MASTER_HOST(command);	
		if (dbg) printf("Cmd Reg 0x%08x, linkNumber=%d\n\r", command, linkNumber);
		
		/* 3. Check for Link Errors */
		control = _cfg_read(0, 0, (htCapPtr + CAP1_LINK_CONTROL_0 + linkNumber*4));
		printf("NearLink[%d] Control = 0x%08x\n\r", linkNumber, linkControl);

		if (control & HT_BIT(HT_LNKCTRL_LKFAIL)) {
			printf("hypertransport link error\n");
			break; 		/* Link is taking errors. Terminate link. */
		}

		if (control & HT_MASK(HT_LNKCTRL_CRCERROR, HT_LNKCTRL_CRCERROR_WIDTH)) {
			printf("hypertransport link crc errors\n");
			break; 		/* CRC errors */
		}

		/* 4. Set UnitID */
		unitId = nextFreeID;
		updateUid =  HT_SET_UID(command, nextFreeID);
		nextFreeID += HT_UNIT_CNT(command);
		_cfg_write(0, 0, htCapPtr + CAP1_COMMAND, updateUid);	/* updates unit ID */

		for (i=0; i<0x100000; i++);	/* give it some time */
		
		/* read back using the new UID */
		deviceId = _cfg_read(0, unitId, 0);

		/* check if unit ID got updated properly */
		revision = _cfg_read(0, unitId, PCI_REVISION_REG) & PCI_REVISION_MASK;

		if (dbg) printf("HT unitID %d: vendor 0x%04x, device 0x%04x, revision 0x%02x", 
				unitId, deviceId & 0xffff, (deviceId >> 16) & 0xffff, revision);
		
		/* Frequency Resize Handling */
		if (freqResize) {
			currFreqCap = _ht_get_freq_cap(unitId, linkNumber);
			if (dbg) printf(", capability 0x%04x\n", currFreqCap);

			linkFreq = _ht_get_max_freq(currFreqCap & prevFreqCap);
			newFreq = HT_MIN(maxFreq, linkFreq);
                        if (dbg) printf("_ht_chain_init: maxFreq 0x%08x, linkFreq 0x%08x, newFreq 0x%08x\n", 
					maxFreq, linkFreq, newFreq);
			_ht_set_freq_cap(prevUid, prevLink, newFreq);

			/* 
			 *  Set RM9150 transmit numerator and denominator
			 *  synchronization fifo parameters.
			 */
			if (unitId == 1) {

				int pllSetting = 0x7cd; /* default 200 1 8 2 */

				tx_den = RM9150_HT_READ(RM9150_HT_PLL_INFO);
				tx_den &= ~0x1f0000;	/* clear tx denominator, bits 20:16 */

				/* Use predefined pll setting based on frequency */
				switch (newFreq) {
                                        case HT_LNKFREQ_300MHZ: pllSetting = 0x7d5;     break;  /* 1 12 2 */
                                        case HT_LNKFREQ_400MHZ: pllSetting = 0x7cc;     break;  /* 1  8 1 */
                                        case HT_LNKFREQ_500MHZ: pllSetting = 0x7d0;     break;  /* 1 10 1 */
                                        case HT_LNKFREQ_600MHZ: pllSetting = 0x7d4;     break;  /* 1 12 1 */
                                        case HT_LNKFREQ_800MHZ: pllSetting = 0x7dc;     break;  /* 1 16 1 */
				}

                                /* Write setting to pll register. _ht_chain_init will modify this later */
                                printf("\n writting pll setting 0x%08x, freq 0x%08x\n\n", pllSetting, newFreq);
                                RM9150_HT_WRITE(RM9150_HT_PLL_INFO, pllSetting);

                                tx_den = RM9150_HT_READ(RM9150_HT_PLL_INFO);
                                tx_den &= ~0x1f0000;    /* clear tx denominator, bits 20:16 */
                                                                                                                
                                switch (newFreq) {
                                        case HT_LNKFREQ_200MHZ:
                                                tx_num = 0x1;
                                                tx_den |= (0x4 << 16);
                                                break;
                                                                                                                
                                        case HT_LNKFREQ_300MHZ:
                                                tx_num = 0x7;
                                                tx_den |= (0x8 << 16);
                                                break;
                                                                                                                
                                        case HT_LNKFREQ_400MHZ:
                                                tx_num = 0x1;
                                                tx_den |= (0x2 << 16);
                                                break;

                                        case HT_LNKFREQ_500MHZ:
                                                tx_num = 0x1f;
                                                tx_den |= (0x8 << 16);
                                                break;
                                                                                                                
                                        case HT_LNKFREQ_600MHZ:
                                                tx_num = 0x7;
                                                tx_den |= (0x4 << 16);
                                                break;
                                        case HT_LNKFREQ_800MHZ:
                                                tx_num = 0x1;
                                                tx_den |= (0x2 << 16);
                                                break;
                                }
                                RM9150_HT_WRITE(RM9150_HT_TRANSMIT_NUMERATOR, tx_num);
                                printf("Setting PLL info to 0x%08x\n\n", tx_den);
                                RM9150_HT_WRITE(RM9150_HT_PLL_INFO, tx_den);

				if (dbg) printf("             rm9000 tx numerator 0x%x, tx denominator 0x%x\n", 
						tx_num, (tx_den >> 5) & 0x1f);
			}
	
			_ht_set_freq_cap(unitId, linkNumber, newFreq);
		}
		else {
			if (dbg) printf("\n");
		}

		/* 5. read link status of the far link and remember previous link info */
		farLink = (linkNumber==0) ? 1 : 0;
		prevUid = unitId;
		prevLink = farLink;
		prevFreqCap = _ht_get_freq_cap(unitId, farLink);			
		if (dbg) printf("prevLinks: prevUid=%d, prevLink=%x, prevFreqCap=0x%08x\n",
				prevUid, prevLink, prevFreqCap);

		linkControl = _cfg_read(0, unitId, (htCapPtr + CAP1_LINK_CONTROL_0 + farLink*4));
		if (dbg) printf("FarLink[%d] Control = 0x%08x\n", farLink, linkControl);
	}
	
	_ht_set_eoc(unitId, farLink);
	return(1);	
}
#endif /* ENABLE_HT */


/*
 *  Called to initialise the bridge at the beginning of time
 */
int
_pci_hwinit (initialise, iot, memt)
	int initialise;
	bus_space_tag_t iot;
	bus_space_tag_t memt;
{
	pcireg_t stat;
	struct pci_device *pcidev;
	struct pci_bus *pcibus;
	int i;
	int regData;
	uint32_t deviceId;
	uint32_t revision;
	int master_host;
	int dev;
	uint32_t capability1_ptr;
	uint32_t link_width_in;
	uint32_t link_width_out;
	uint32_t link_tx_freq;
	uint32_t link_tx_freq_capability;
	uint32_t host_link_tx_freq;
	uint32_t host_link_tx_freq_capability;
	int rm9k_tx_ok;
	int rm9k_rx_ok;
	int max_freq;
	int freq;
	int freq_reg;
	int htfreq;
	char *v;
	int initDone;
	int linkFail;
	int next_link;
	int numDevs = 1;
	uint16_t regdata;	/* xxx */

	/*
	 *  PCI and local bus maps 1-1
	 */
	iot->bus_base = 0;
	iot->bus_reverse = 1;
	memt->bus_base = 0;
	memt->bus_reverse = 1;

	/*
	 *  Where local memory starts seen from PCI.
	 */
	pci_local_mem_pci_base = PCI_LOCAL_MEM_PCI_BASE;

	if (!initialise) {
		return(0);
	}

	/*
	 *  Allocate and initialize PCI bus heads
	 */

	/*
	 *  PCI Bus 0
	 */
	pcidev = pmalloc(sizeof(struct pci_device));
	pcibus = pmalloc(sizeof(struct pci_bus));
	if (pcidev == NULL || pcibus == NULL) {
		printf("pci: can't alloc memory. pci not initialized\n");
		return(-1);
	}
        pcidev->pa.pa_bus = 0;
	pcidev->pa.pa_flags = PCI_FLAGS_IO_ENABLED | PCI_FLAGS_MEM_ENABLED;
	pcidev->pa.pa_iot = iot;
	pcidev->pa.pa_memt = memt;
	pcidev->pa.pa_dmat = &bus_dmamap_tag;
	pcidev->bridge.secbus = pcibus;
	_pci_head = pcidev;

	pcibus->minpcimemaddr  = PCI0_MEM_SPACE_BASE;
	pcibus->nextpcimemaddr = PCI0_MEM_SPACE_BASE + PCI0_MEM_SPACE_SIZE;
	pcibus->minpciioaddr   = PCI0_IO_SPACE_BASE;
	pcibus->nextpciioaddr  = PCI0_IO_SPACE_BASE + PCI0_IO_SPACE_SIZE;
	pcibus->pci_mem_base   = PCI0_MEM_SPACE_BASE; 	/* Maps 1-1 */
	pcibus->pci_io_base    = PCI0_IO_SPACE_BASE; 	/* Maps 1-1 */
	pcibus->max_lat = 255;
	pcibus->fast_b2b = 1;
	pcibus->prefetch = 1;
	pcibus->bandwidth = 4000000;
	pcibus->ndev = 1;
	_pci_bushead = pcibus;
	_pci_bus[_max_pci_bus++] = pcibus;

	register_mem((void *)RM9150_CFLASH,
			(void *)RM9150_CFLASH +
			RM9150_CFLASH_SIZE + RM9150_PCMCIA_SIZE +
			PCI0_MEM_SPACE_SIZE + PCI0_IO_SPACE_SIZE + 
			PCI1_MEM_SPACE_SIZE + PCI1_IO_SPACE_SIZE +
			PCI2_MEM_SPACE_SIZE + PCI2_IO_SPACE_SIZE - 1, 
			MEM_IO, "PCI Memory and IO space");

	register_mem((void *)PCI0_MEM_SPACE_BASE,
			(void *)PCI0_MEM_SPACE_BASE + PCI0_MEM_SPACE_SIZE - 1,
			MEM_IO, "PCI 0 memory");

	register_mem((void *)PCI0_IO_SPACE_BASE,
			(void *)PCI0_IO_SPACE_BASE + PCI0_IO_SPACE_SIZE - 1,
			MEM_IO, "PCI 0 I/O");

        /*
         *  PCI Bus 1
         */
        pcidev = pmalloc(sizeof(struct pci_device));
        pcibus = pmalloc(sizeof(struct pci_bus));
        if (pcidev == NULL || pcibus == NULL) {
                printf("pci: can't alloc memory. pci 1 not initialized\n");
                return(-1);
        }

        *pcidev = *_pci_head;
        pcidev->pa.pa_bus = 1;
        pcidev->bridge.secbus = pcibus;
        _pci_head->next = pcidev;

        *pcibus = *_pci_bushead;
        pcibus->minpcimemaddr  	= PCI1_MEM_SPACE_BASE;
        pcibus->nextpcimemaddr 	= PCI1_MEM_SPACE_BASE + PCI1_MEM_SPACE_SIZE;
        pcibus->minpciioaddr  	= PCI1_IO_SPACE_BASE;
        pcibus->nextpciioaddr 	= PCI1_IO_SPACE_BASE + PCI1_IO_SPACE_SIZE;
        pcibus->pci_mem_base   	= PCI1_MEM_SPACE_BASE;	/* Maps 1-1 */
        pcibus->pci_io_base    	= PCI1_IO_SPACE_BASE;	/* Maps 1-1 */

        _pci_bushead->next = pcibus;
        _pci_bus[_max_pci_bus++] = pcibus;

        register_mem((void *)PCI1_MEM_SPACE_BASE,
                        (void *)PCI1_MEM_SPACE_BASE + PCI1_MEM_SPACE_SIZE - 1,
                        MEM_IO, "PCI 1 memory");
        register_mem((void *)PCI1_IO_SPACE_BASE,
                        (void *)PCI1_IO_SPACE_BASE + PCI1_IO_SPACE_SIZE - 1,
                        MEM_IO, "PCI 1 I/O");

        /*
         *  PCI Bus 2
         */
        pcidev = pmalloc(sizeof(struct pci_device));
        pcibus = pmalloc(sizeof(struct pci_bus));
        if (pcidev == NULL || pcibus == NULL) {
                printf("pci: can't alloc memory. pci 2 not initialized\n");
                return(-1);
        }

        *pcidev = *_pci_head->next;
        pcidev->pa.pa_bus = 2;
        pcidev->bridge.secbus = pcibus;
        _pci_head->next->next = pcidev;

        *pcibus = *_pci_bushead->next;
        pcibus->minpcimemaddr  	= PCI2_MEM_SPACE_BASE;
        pcibus->nextpcimemaddr 	= PCI2_MEM_SPACE_BASE + PCI2_MEM_SPACE_SIZE;
        pcibus->minpciioaddr  	= PCI2_IO_SPACE_BASE;
        pcibus->nextpciioaddr 	= PCI2_IO_SPACE_BASE + PCI2_IO_SPACE_SIZE;
        pcibus->pci_mem_base   	= PCI2_MEM_SPACE_BASE;	/* Maps 1-1 */
        pcibus->pci_io_base    	= PCI2_IO_SPACE_BASE;	/* Maps 1-1 */

        _pci_bushead->next->next = pcibus;
        _pci_bus[_max_pci_bus++] = pcibus;

        register_mem((void *)PCI2_MEM_SPACE_BASE,
                        (void *)PCI2_MEM_SPACE_BASE + PCI2_MEM_SPACE_SIZE - 1,
                        MEM_IO, "PCI 2 memory");
        register_mem((void *)PCI2_IO_SPACE_BASE,
                        (void *)PCI2_IO_SPACE_BASE + PCI2_IO_SPACE_SIZE - 1,
                        MEM_IO, "PCI 2 I/O");

	/*
	 *  Setup PCI holding registers prior to PCI reset 
	 *  for PCI 0/1 target memory and io space BARs.
	 *
	 *  At reset PCH2 is set to 4MB for the initial 
	 *  address map and PCH3 is set to 4MB and points to 
	 *  the mips boot vector.  Therefore we will use PCH 4/5 
	 *  for PCI memory/io space.  These work with TPBAR 2/3.
	 *
	 *  This controls the range of address accessible when
	 *  RM9150 is the target of a PCI device DMA into host 
	 *  memory.  PMON currently supports up to 512MB so
	 *  its set to that versus totalmemsize.
	 *
	 *  PCH4 sets size and attributes for TPBAR2
	 *  PCH5 sets size and attributes for TPBAR3
	 */

	/* PCI 0 memory space */
	stat = RM9150_PCI0_READ(RM9150_PCI_CONFIGURATION_HOLD_4);
	stat &= ~(0x7);				/* clear mem/io space indicator */
	stat |= (0x1 << 3);			/* enable prefetch */
	stat |= (RM9150_PCI_MEMSPACE_SIZE_512MB << 12);
	RM9150_PCI0_WRITE(RM9150_PCI_CONFIGURATION_HOLD_4, stat);

	/* PCI 1 memory space */
	stat = RM9150_PCI1_READ(RM9150_PCI_CONFIGURATION_HOLD_4);
	stat &= ~(0x7);				/* clear mem/io space indicator */
	stat |= (0x1 << 3);			/* enable prefetch */
	stat |= (RM9150_PCI_MEMSPACE_SIZE_512MB << 12);
	RM9150_PCI1_WRITE(RM9150_PCI_CONFIGURATION_HOLD_4, stat);

	stat = RM9150_PCI0_READ(RM9150_PCI_CONFIGURATION_HOLD_4);
	if (dbg) printf("pci0 mem, PCH4 0x%08x\n", stat);

	stat = RM9150_PCI1_READ(RM9150_PCI_CONFIGURATION_HOLD_4);
	if (dbg) printf("pci1 mem, PCH4 0x%08x\n", stat);

	/* PCI 0 io space */
	stat = RM9150_PCI0_READ(RM9150_PCI_CONFIGURATION_HOLD_5);
	stat &= ~(0x7);			/* clear mem/io space indicator */
	stat |= 1;			/* IO space */
	RM9150_PCI0_WRITE(RM9150_PCI_CONFIGURATION_HOLD_5, stat);

	/* PCI 1 io space */
	stat = RM9150_PCI1_READ(RM9150_PCI_CONFIGURATION_HOLD_5);
	stat &= ~(0x7);			/* clear mem/io space indicator */
	stat |= 1;			/* IO space */
	RM9150_PCI1_WRITE(RM9150_PCI_CONFIGURATION_HOLD_5, stat);

	stat = RM9150_PCI0_READ(RM9150_PCI_CONFIGURATION_HOLD_5);
	if (dbg) printf("pci0 io,  PCH5 0x%08x\n", stat);

	stat = RM9150_PCI1_READ(RM9150_PCI_CONFIGURATION_HOLD_5);
	if (dbg) printf("pci1 io,  PCH5 0x%08x\n", stat);

	/*
	 *  Set the cacheline size and latency timer
	 */
	stat = 8;		/* cacheline size, 8*32bits=32bytes */
	stat |= (0x8 << 8);	/* latency timer, minimum 8 pci clocks */
	RM9150_PCI0_WRITE(RM9150_PCI_BIST_HEADER_TYPE, stat);
	RM9150_PCI1_WRITE(RM9150_PCI_BIST_HEADER_TYPE, stat);

	stat = RM9150_PCI0_READ(RM9150_PCI_BIST_HEADER_TYPE);
	if (dbg) printf("pci0 BIST and Header type, PBHL 0x%08x\n", stat);

	stat = RM9150_PCI1_READ(RM9150_PCI_BIST_HEADER_TYPE);
	if (dbg) printf("pci1 BIST and Header type, PBHL 0x%08x\n", stat);

	/*
	 *  Set PCI configuration status 1 and 2 
	 */
        RM9150_PCI0_WRITE(RM9150_PCI_CONFIG_STATUS_1, 0x20000000);
        RM9150_PCI0_WRITE(RM9150_PCI_CONFIG_STATUS_2, 0x11000004);

        RM9150_PCI1_WRITE(RM9150_PCI_CONFIG_STATUS_1, 0x20000000);
        RM9150_PCI1_WRITE(RM9150_PCI_CONFIG_STATUS_2, 0x11000004);

        stat = RM9150_PCI0_READ(RM9150_PCI_CONFIG_STATUS_1);
        if (dbg) printf("pci0 config status 1 0x%08x\n", stat); 

        stat = RM9150_PCI0_READ(RM9150_PCI_CONFIG_STATUS_2);
        if (dbg) printf("pci0 config status 2 0x%08x\n", stat); 

        stat = RM9150_PCI1_READ(RM9150_PCI_CONFIG_STATUS_1);
        if (dbg) printf("pci1 config status 1 0x%08x\n", stat); 

        stat = RM9150_PCI1_READ(RM9150_PCI_CONFIG_STATUS_2);
        if (dbg) printf("pci1 config status 2 0x%08x\n", stat); 

        /*
         *  Setup PCI Master GDI Virtual Prefetchable Memory Base Address (MGMBAR).
	 *  This controls 36-bit GDI address translation to 32-bit PCI
	 *  addresses when the RM9150 is the Master.
         */
        stat = RM9150_PCI0_READ(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_BASE);
        stat |= (PCI0_MEM_SPACE_BASE >> 4);     /* set virtual base address */
        stat |= 0x1;				/* enable region */
        RM9150_PCI0_WRITE(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_BASE, stat);

        stat = RM9150_PCI1_READ(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_BASE);
        stat |= (PCI1_MEM_SPACE_BASE >> 4);     /* set virtual base address */
        stat |= 0x1;				/* enable region */
        RM9150_PCI1_WRITE(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_BASE, stat);
                                                                                               
        stat = RM9150_PCI0_READ(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_BASE);
        if (dbg) printf("pci0 mem, MGMBAR 0x%08x\n", stat);

        stat = RM9150_PCI1_READ(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_BASE);
        if (dbg) printf("pci1 mem, MGMBAR 0x%08x\n", stat);

        /*
         *  Setup PCI Master GDI Virtual Prefetchable Memory Base (MGMMASK)
         */
        stat = RM9150_PCI0_READ(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_SIZE);
	stat &= ~(0xfffff << 8);	/* clear size field */
        stat |= (0x7fff << 8);		/* set GDI size to 128 MB */
        RM9150_PCI0_WRITE(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_SIZE, stat);
                                                                                               
        stat = RM9150_PCI1_READ(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_SIZE);
	stat &= ~(0xfffff << 8);	/* clear size field */
        stat |= (0x7fff << 8);		/* set GDI size to 128 MB */
        RM9150_PCI1_WRITE(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_SIZE, stat);
                                                                                               
        stat = RM9150_PCI0_READ(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_SIZE);
        if (dbg) printf("pci0 mem, MGMSIZE 0x%08x\n", stat);

        stat = RM9150_PCI1_READ(RM9150_PCI_MASTER_GDI_VIRT_PREFETCH_SIZE);
        if (dbg) printf("pci1 mem, MGMSIZE 0x%08x\n", stat);

        /*
         *  Setup PCI Master GDI Virtual Prefetchable IO Base Address (MGIOBAR)
         */
        stat = RM9150_PCI0_READ(RM9150_PCI_MASTER_GDI_VIRT_IOBASE);
        stat |= (PCI0_IO_SPACE_BASE >> 4);	/* set virtual base address */
        stat |= 0x1;				/* enable region */
        RM9150_PCI0_WRITE(RM9150_PCI_MASTER_GDI_VIRT_IOBASE, stat);

        stat = RM9150_PCI1_READ(RM9150_PCI_MASTER_GDI_VIRT_IOBASE);
        stat |= (PCI1_IO_SPACE_BASE >> 4);	/* set virtual base address */
        stat |= 0x1;				/* enable region */
        RM9150_PCI1_WRITE(RM9150_PCI_MASTER_GDI_VIRT_IOBASE, stat);
                                                                                               
        stat = RM9150_PCI0_READ(RM9150_PCI_MASTER_GDI_VIRT_IOBASE);
        if (dbg) printf("pci0 mem, MGIOBAR 0x%08x\n", stat);

        stat = RM9150_PCI1_READ(RM9150_PCI_MASTER_GDI_VIRT_IOBASE);
        if (dbg) printf("pci1 mem, MGIOBAR 0x%08x\n", stat);

        /*
         *  Setup PCI Master GDI Virtual IO Mask (MGMIOMASK)
         */
        stat = RM9150_PCI0_READ(RM9150_PCI_MASTER_GDI_VIRT_IOMASK);
	stat &= ~(0xfffff << 8);	/* clear size field */
        stat |= (0xfff << 8);		/* set GDI size to 16 MB */
        RM9150_PCI0_WRITE(RM9150_PCI_MASTER_GDI_VIRT_IOMASK, stat);
                                                                                               
        stat = RM9150_PCI1_READ(RM9150_PCI_MASTER_GDI_VIRT_IOMASK);
	stat &= ~(0xfffff << 8);	/* clear size field */
        stat |= (0xfff << 8);		/* set GDI size to 16 MB */
        RM9150_PCI1_WRITE(RM9150_PCI_MASTER_GDI_VIRT_IOMASK, stat);
                                                                                               
        stat = RM9150_PCI0_READ(RM9150_PCI_MASTER_GDI_VIRT_IOMASK);
        if (dbg) printf("pci0 io, MGIOMASK 0x%08x\n", stat);

        stat = RM9150_PCI1_READ(RM9150_PCI_MASTER_GDI_VIRT_IOMASK);
        if (dbg) printf("pci1 io, MGIOMASK 0x%08x\n", stat);

	/* PCI 0 priority on FDB to 10b */
        stat = RM9150_PCI0_READ(RM9150_PCI_IAM_CONFIG_STATUS);
	stat |= 0x10;
	stat &= ~0x1;
	RM9150_PCI0_WRITE(RM9150_PCI_IAM_CONFIG_STATUS, stat);

	/* PCI 1 priority on FDB to 10b */
        stat = RM9150_PCI1_READ(RM9150_PCI_IAM_CONFIG_STATUS);
	stat |= 0x10;
	stat &= ~0x1;
	RM9150_PCI1_WRITE(RM9150_PCI_IAM_CONFIG_STATUS, stat);

	/* PCI 0 memory space attributes */
	for (i = 0; i < 6; i++) {
		stat = RM9150_PCI0_READ(RM9150_PCI_GDI_BAR0_ATTRIBUTES + i*0x8);
		stat |= (1 << 9);	/* read byte swap */
		stat |= (1 << 11);	/* write byte swap */
		stat |= (1 << 14);	/* prefetch enable */
		stat |= 0x7;		/* burst size 4KB */
		RM9150_PCI0_WRITE(RM9150_PCI_GDI_BAR0_ATTRIBUTES + i*0x8, stat);
		stat = RM9150_PCI0_READ(RM9150_PCI_GDI_BAR0_ATTRIBUTES + i*0x8);
		if (dbg) printf("pci0 mem, TGBAR%d attributes 0x%08x\n", i, stat);
	}
	
	/* PCI 1 memory space attributes */
	for (i = 0; i < 6; i++) {
		stat = RM9150_PCI1_READ(RM9150_PCI_GDI_BAR0_ATTRIBUTES + i*0x8);
		stat |= (1 << 9);	/* read byte swap */
		stat |= (1 << 11);	/* write byte swap */
		stat |= (1 << 14);	/* prefetch enable */
		stat |= 0x7;		/* burst size 4KB */
		RM9150_PCI1_WRITE(RM9150_PCI_GDI_BAR0_ATTRIBUTES + i*0x8, stat);
		stat = RM9150_PCI1_READ(RM9150_PCI_GDI_BAR0_ATTRIBUTES + i*0x8);
		if (dbg) printf("pci1 mem, TGBAR%d attributes 0x%08x\n", i, stat);
	}

	/* Set pci master write request data swap (PMWS) */
	RM9150_PCI0_WRITE(RM9150_PCI_MASTER_WRITE_REQ_DATA_SWAP, 0xffff);
	RM9150_PCI1_WRITE(RM9150_PCI_MASTER_WRITE_REQ_DATA_SWAP, 0xffff);

	stat = RM9150_PCI0_READ(RM9150_PCI_MASTER_WRITE_REQ_DATA_SWAP);
	if (dbg) printf("pci0 mem, master write swap, PMWS 0x%08x\n", stat);

	stat = RM9150_PCI1_READ(RM9150_PCI_MASTER_WRITE_REQ_DATA_SWAP);
	if (dbg) printf("pci1 mem, master write swap, PMWS 0x%08x\n", stat);

	/* Set pci master read request data swap (PMRS) */
	RM9150_PCI0_WRITE(RM9150_PCI_MASTER_READ_RESP_DATA_SWAP, 0xffff);
	RM9150_PCI1_WRITE(RM9150_PCI_MASTER_READ_RESP_DATA_SWAP, 0xffff);

	stat = RM9150_PCI0_READ(RM9150_PCI_MASTER_READ_RESP_DATA_SWAP);
	if (dbg) printf("pci0 mem, master read swap, PMWS 0x%08x\n", stat);

	stat = RM9150_PCI1_READ(RM9150_PCI_MASTER_READ_RESP_DATA_SWAP);
	if (dbg) printf("pci1 mem, master read swap, PMWS 0x%08x\n", stat);

	/*
	 *  Reset PCI 0
	 */
	stat = RM9150_PCI0_READ(RM9150_PCI_RESET);
	stat |= (1 << 8);		/* software pci reset */
	RM9150_PCI0_WRITE(RM9150_PCI_RESET, stat);
	stat &= ~(1 << 8);		/* clear reset */
	_DELAY(tgt_pipefreq()/1000*3);	/* hold in reset for ~3ms */
	RM9150_PCI0_WRITE(RM9150_PCI_RESET, stat);
	_DELAY(tgt_pipefreq()/1000*3);	/* delay for pci holding register load */

	/*
	 *  Reset PCI 1
	 */
	stat = RM9150_PCI1_READ(RM9150_PCI_RESET);
	stat |= (1 << 8);		/* software pci reset */
	RM9150_PCI1_WRITE(RM9150_PCI_RESET, stat);
	stat &= ~(1 << 8);		/* clear reset */
	_DELAY(tgt_pipefreq()/1000*3);	/* hold in reset for ~3ms */
	RM9150_PCI1_WRITE(RM9150_PCI_RESET, stat);
	_DELAY(tgt_pipefreq()/1000*3);	/* delay for pci holding register load */

	/*
	 *  Setup for proper byte enables for non 32-bit accesses.  Register
	 *  offset not documented yet.
	 */
	RM9150_PCI0_WRITE(0x140, 0x20000000);
	RM9150_PCI0_WRITE(0x14c, 0x11000004);
	RM9150_PCI1_WRITE(0x140, 0x20000000);
	RM9150_PCI1_WRITE(0x14c, 0x11000004);

	/*
	 *  Setup PCI Target PCI/Virtual Base Address
	 */

	/* PCI 0 memory space (TPBAR2) to start at local memory address 0 */
	stat = RM9150_PCI0_READ(RM9150_PCI_TARGET_BAR2);
	stat &= ~(0x7);			/* clear type and memory space indicator */
	RM9150_PCI0_WRITE(RM9150_PCI_TARGET_BAR2, stat);

	stat = RM9150_PCI0_READ(RM9150_PCI_TARGET_BAR2);
	if (dbg) printf("pci0 mem, TPBAR2 0x%08x\n", stat);

	/* PCI 1 memory space (TPBAR2) to start at local memory address 0 */
	stat = RM9150_PCI1_READ(RM9150_PCI_TARGET_BAR2);
	stat &= ~(0x7);			/* clear type and memory space indicator */
	RM9150_PCI1_WRITE(RM9150_PCI_TARGET_BAR2, stat);

	stat = RM9150_PCI1_READ(RM9150_PCI_TARGET_BAR2);
	if (dbg) printf("pci1 mem, TPBAR2 0x%08x\n", stat);

	/* PCI 0 io space (TPBAR3) to start at local memory address 0 */
	stat = RM9150_PCI0_READ(RM9150_PCI_TARGET_BAR3);
	stat &= ~(0x7);				/* clear type and memory space indicator */
	stat |= 1;				/* io space */
	RM9150_PCI0_WRITE(RM9150_PCI_TARGET_BAR3, stat);

	stat = RM9150_PCI0_READ(RM9150_PCI_TARGET_BAR3);
	if (dbg) printf("pci0 io,  TPBAR3 0x%08x\n", stat);

	/* PCI 1 io space (TPBAR3) to start at local memory address 0 */
	stat = RM9150_PCI1_READ(RM9150_PCI_TARGET_BAR3);
	stat &= ~(0x7);				/* clear type and memory space indicator */
	stat |= 1;				/* io space */
	RM9150_PCI1_WRITE(RM9150_PCI_TARGET_BAR3, stat);

	stat = RM9150_PCI1_READ(RM9150_PCI_TARGET_BAR3);
	if (dbg) printf("pci1 io,  TPBAR3 0x%08x\n", stat);

	/*
	 *  Setup PCI 32-bit to GDI 36-bit address translation.  This 
	 *  controls 32-bit PCI address translation to 36-bit GDI 
	 *  addresses when the RM9150 is the Target.  Since PMON locates
	 *  memory starting at 0x0 the default for these registers is ok.
	 */

	/* PCI 0 memory space (TGBAR2) */
	//RM9150_PCI0_WRITE(RM9150_PCI_GDI_BAR2, (PCI0_MEM_SPACE_BASE >> 4));

	stat = RM9150_PCI0_READ(RM9150_PCI_GDI_BAR2);
	if (dbg) printf("pci0 mem, TGBAR2 0x%08x\n", stat);

	stat = RM9150_PCI1_READ(RM9150_PCI_GDI_BAR2);
	if (dbg) printf("pci1 mem, TGBAR2 0x%08x\n", stat);

	/* PCI 0 io space (TGBAR3) */
	//RM9150_PCI0_WRITE(RM9150_PCI_GDI_BAR2, (PCI0_IO_SPACE_BASE >> 4));

	stat = RM9150_PCI0_READ(RM9150_PCI_GDI_BAR3);
	if (dbg) printf("pci0 io,  TGBAR3 0x%08x\n", stat);

	stat = RM9150_PCI1_READ(RM9150_PCI_GDI_BAR3);
	if (dbg) printf("pci1 io,  TGBAR3 0x%08x\n", stat);

	/*
	 *  Enable PCI 0 as master for config cycles
	 */
	stat = RM9150_PCI0_READ(RM9150_PCI_COMMAND_STATUS);
	stat |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_MEM_ENABLE;
	stat |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_INVALIDATE_ENABLE;
	RM9150_PCI0_WRITE(RM9150_PCI_COMMAND_STATUS, stat);

	stat = RM9150_PCI0_READ(RM9150_PCI_COMMAND_STATUS);
	if (dbg) printf("pci0 status/command 0x%08x\n", stat);

	/*
	 *  Enable PCI 1 as master for config cycles
	 */
	stat = RM9150_PCI1_READ(RM9150_PCI_COMMAND_STATUS);
	stat |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_MEM_ENABLE;
	stat |= PCI_COMMAND_IO_ENABLE | PCI_COMMAND_INVALIDATE_ENABLE;
	RM9150_PCI1_WRITE(RM9150_PCI_COMMAND_STATUS, stat);

	stat = RM9150_PCI1_READ(RM9150_PCI_COMMAND_STATUS);
	if (dbg) printf("pci1 status/command 0x%08x\n", stat);

#ifdef DUMP_PCI0_SETUP
	printf("PCI 0 setup:\n");
	for (i=0; i<180; i++) {
		if (i==4 || i==5) continue;
		stat = RM9150_PCI0_READ(4*i);
		printf("\t0x%08x\t0x%08x\n", 0xb0020000 + 4*i, stat);
	}
#endif

#ifdef DUMP_PCI1_SETUP
	printf("PCI 1 setup:\n");
	for (i=0; i<180; i++) {
		if (i==4 || i==5) continue;
		stat = RM9150_PCI1_READ(4*i);
		printf("\t0x%08x\t0x%08x\n", 0xb0080000 + 4*i, stat);
	}
#endif

#if 0
	/* 
	 *  xxx USB debug
	 */
	printf("USB register base 0x%08x\n", RM9150_USB);
#define ISP1362_CMD_OFFSET	0x08

	/* Read register offset 0x0 10 times */
	for (i=0; i<10; i++) {
		/* command phase, A0 high */
		*((volatile uint16_t *) (RM9150_USB + ISP1362_CMD_OFFSET)) = 0;
		DELAY(100);
		
		/* data phase, A0 low */
		regdata = *((volatile uint16_t *) RM9150_USB);
		DELAY(100);

		printf("\tUSB HC reg 0x0000 : 0x%08x\n", regdata);
	}

	/* Read more registers */
	printf("\n");
	for (i=0; i<20; i++) {
		/* command phase, A0 high */
		*((volatile uint16_t *) (RM9150_USB + ISP1362_CMD_OFFSET)) = i;
		DELAY(100);
		
		/* data phase, A0 low */
		regdata = *((volatile uint16_t *) RM9150_USB);
		DELAY(100);

		printf("\tUSB HC reg 0x%04x : 0x%08x\n", i, regdata);
	}

	/* Register 0x34 */
	i = 0x34;
	*((volatile uint16_t *) (RM9150_USB + ISP1362_CMD_OFFSET)) = i;
	DELAY(100);
	regdata = *((volatile uint16_t *) RM9150_USB);
	DELAY(100);
	printf("\tUSB HC reg 0x%04x : 0x%08x\n", i, regdata);
	printf("\n\n");
#endif

	/* 
	 *  Set debug flag based on Switch S4:4 
	 */	
#if 0			/* xxx */
	if ((FPGA_READ(0x2) & USERSWITCH_S5_4) == 0) {
		dbg = 1;
	}
#else
	dbg = 1;
#endif

        /*
         *  Enable HT interface as master to do config cycles
	 *  but first check if HT is enabled in configuration.
         */
        hypertransport_enable = 0;
#if defined(ENABLE_HT)
	printf("hypertransport link status: ");
	/* Wait for HW init to complete */
	for (i = 0; i < 1000; i++) {
		if (RM9150_HT_READ(RM9150_HT_LINK_CONTROL_CONFIG) & (1<<5)) {
			hypertransport_enable = 1;
			break;
		}
	}
	if (hypertransport_enable)
		printf("good\n");
	else
		printf("failed\n");

        if (!getenv("noht") && hypertransport_enable) {

                RM9150_HT_WRITE(RM9150_HT_STATUS_CMD, 4);           /* enable master */

		regData = RM9150_HT_READ(RM9150_HT_REVISION_ID_LINK_FREQ);
		host_link_tx_freq = (regData >> 8) & 0x7;

		regData = RM9150_HT_READ(RM9150_HT_REVISION_ID_LINK_FREQ);
		host_link_tx_freq_capability = (regData >> 16) & 0xff;

		if (dbg) printf("master host: width in/out 8/8-bit, tx freq ");
		if (host_link_tx_freq == 0)
			if (dbg) printf("200 MHz");
		else if (host_link_tx_freq == 1)
			if (dbg) printf("300 MHz");
		else if (host_link_tx_freq == 2)
			if (dbg) printf("400 MHz");
		else if (host_link_tx_freq == 3)
			if (dbg) printf("500 MHz");
		else if (host_link_tx_freq == 4)
			if (dbg) printf("600 MHz");
		else if (host_link_tx_freq == 5)
			if (dbg) printf("800 MHz");
		else if (host_link_tx_freq == 6)
			if (dbg) printf("1000 MHz");
		else
			if (dbg) printf("unknown");

		if (dbg) printf(", capability 0x%04x\n", host_link_tx_freq_capability);

#ifdef HT_DEBUG
		if (dbg) {
			printf("ht: pll info (PLLINFO) = 0x%08x\n", 
				RM9150_HT_READ(RM9150_HT_PLL_INFO));
			printf("ht: tx numerator (TXNUM) = 0x%08x\n", 
				RM9150_HT_READ(RM9150_HT_TRANSMIT_NUMERATOR));
			printf("ht: rx numerator (RXNUM) = 0x%08x\n", 
				RM9150_HT_READ(RM9150_HT_RECEIVE_NUMERATOR));
			printf("ht: revision id and link frequency (RILF) = 0x%08x\n", 
				RM9150_HT_READ(RM9150_HT_REVISION_ID_LINK_FREQ));
		}
#endif

                /* Reset the link */
		rm9k_ht_reset();

                /* If INIT is off, leave bus disabled */
                if (!(RM9150_HT_READ(RM9150_HT_LINK_CONTROL_CONFIG) & (1<<5))) {
			printf("ht: reset failed\n");
                        hypertransport_enable = 0;
		}
        }

        /* 
	 *  PCI 2 -- Hypertransport interface 
	 */
	if (hypertransport_enable) {

		/* 
		 *  Set our bus number
		 */
		regData = RM9150_HT_READ(RM9150_HT_GHTC_CONFIG);
		regData |= (0x2 << 14);			/* HT bus number */
		RM9150_HT_WRITE(RM9150_HT_GHTC_CONFIG, regData);

#ifdef HT_DEBUG
		if (dbg) {
			regData = RM9150_HT_READ(RM9150_HT_GHTC_CONFIG);
			printf("ht: GHTC config 0x%08x\n", regData);
			printf("    receive margin (RXMARGIN) = %d\n",
				regData & 0x3f);
			printf("    transmit initial offset (TXOSET) = %d\n",
				(regData & 0x3c0) >> 6);
			printf("    receive clock mode (RXSYNC) = %d\n",
				(regData & 0x400) >> 10);
			printf("    sync zero length (SYNCZL) = %d\n",
				(regData & 0x800) >> 11);
			printf("    non-posted write response passPW (NPWRPPW) = %d\n",
				(regData & 0x1000) >> 12);
			printf("    hypertransport bus number (HTBUS[7:0]) = %d\n",
				(regData & 0x3fc000) >> 14);
		}
#endif

                /*
                 *  PCI 2 memory space BAR, target translation,
		 *  target translation size, and attributes
                 */

		/* Using BAR 0 (override defaults) */ 
		regData = (0x1 << 1);			/* type 1, 32 bit BAR mapped in 40 bit space */
		//regData |= (PCI2_MEM_SPACE_BASE >> 8);	/* 40 bit address */
		regData |= (0x1 << 3);			/* enable prefetch */
                RM9150_HT_WRITE(RM9150_HT_BAR0, regData);

		/* Translation address */
                //RM9150_HT_WRITE(RM9150_HT_TARGET_TRANSLATE_GDI_ADDR_0, (PCI2_MEM_SPACE_BASE >> 4));
                RM9150_HT_WRITE(RM9150_HT_TARGET_TRANSLATE_GDI_ADDR_0, 0);

		/* Translation size */
                RM9150_HT_WRITE(RM9150_HT_TARGET_TRANSLATE_SIZE_0, 0xff800000);	/* 128 MB */

		/* Attributes */
		regData = (0x1 << 31);			/* enable BAR matching and translation */
		regData |= (0x1 << 2);			/* target read byte swap */
		regData |= (0x1);			/* target write byte swap */
                RM9150_HT_WRITE(RM9150_HT_TARGET_TRANSLATE_ATTR_0, regData);

#ifdef HT_DEBUG 
		if (dbg) {
			regData = RM9150_HT_READ(RM9150_HT_BAR0);
                	printf("ht: bar 0 base (HTBAR0) = 0x%08x\n", regData);
 			printf("    %s space mapping\n", (regData & 0x1) ? "io" : "mem");
 			printf("    bar type %s\n", ((regData & 0x2) >> 1) ? "40-bit mapping" : "32-bit mapping");
 			printf("    prefetchable: %s\n", ((regData & 0x1) >> 3) ? "yes" : "no");
 			printf("    base address 0x%08x\n", (regData & 0xfffffff0) << 4);

                	printf("ht: bar 0 size (TMASK0) = 0x%08x\n", 
				RM9150_HT_READ(RM9150_HT_TARGET_TRANSLATE_SIZE_0));
                	printf("ht: bar 0 attr (TATTR0) = 0x%08x\n", 
				RM9150_HT_READ(RM9150_HT_TARGET_TRANSLATE_ATTR_0));
                	printf("ht: tgt translation GDI addr 0 (TGADDR0) 0x%08x\n", 
				RM9150_HT_READ(RM9150_HT_TARGET_TRANSLATE_GDI_ADDR_0));
		}
#endif

                /*
                 *  PCI 2 IO space BAR, target translation,
		 *  target translation size, and attributes
                 */

		/* Using BAR 1 (overriding defaults) */ 
		regData = (0x1 << 1);			/* type 1, 32 bit BAR mapped in 40 bit space */
		regData |= (PCI2_IO_SPACE_BASE >> 8);	/* 40 bit address */
		regData |= 0x1;				/* IO space */
                RM9150_HT_WRITE(RM9150_HT_BAR1, regData);

		/* Translation address */
                //RM9150_HT_WRITE(RM9150_HT_TARGET_TRANSLATE_GDI_ADDR_1, (PCI2_IO_SPACE_BASE >> 4));

		/* Translation size */
                RM9150_HT_WRITE(RM9150_HT_TARGET_TRANSLATE_SIZE_1, 0xfff00000);	/* 16 MB */

		/* Attributes */
		regData = (0x1 << 31);			/* enable BAR matching and translation */
		regData |= (0x1 << 2);			/* target read byte swap */
		regData |= (0x1);			/* target write byte swap */
                RM9150_HT_WRITE(RM9150_HT_TARGET_TRANSLATE_ATTR_1, regData);

#ifdef HT_DEBUG
                if (dbg) {
			regData = RM9150_HT_READ(RM9150_HT_BAR1);
			printf("ht: bar 1 base (HTBAR1) = 0x%08x\n", regData);
 			printf("    %s space mapping\n", (regData & 0x1) ? "io" : "mem");
 			printf("    bar type %s\n", ((regData & 0x2) >> 1) ? "40-bit mapping" : "32-bit mapping");
 			printf("    prefetchable: %s\n", ((regData & 0x1) >> 3) ? "yes" : "no");
 			printf("    base address 0x%08x\n", (regData & 0xfffffff0) << 4);

                	printf("ht: bar 1 size (TMASK1) = 0x%08x\n", 
				RM9150_HT_READ(RM9150_HT_TARGET_TRANSLATE_SIZE_1));
                	printf("ht: bar 1 attr (TATTR1) = 0x%08x\n", 
				RM9150_HT_READ(RM9150_HT_TARGET_TRANSLATE_ATTR_1));
                	printf("ht: tgt translation GDI addr 1 (TGADDR1) 0x%08x\n", 
				RM9150_HT_READ(RM9150_HT_TARGET_TRANSLATE_GDI_ADDR_1));
		}
#endif

		RM9150_HT_WRITE(RM9150_HT_SOURCE_READ_DATA_SWAP, 0xffff);
		RM9150_HT_WRITE(RM9150_HT_SOURCE_WRITE_DATA_SWAP, 0xffff);
#ifdef HT_DEBUG
		if (dbg) {
			printf("ht: source read data swap (HSRDS) = 0x%x\n", 
				RM9150_HT_READ(RM9150_HT_SOURCE_READ_DATA_SWAP));
			printf("ht: source write data swap (HSWDS) = 0x%x\n", 
				RM9150_HT_READ(RM9150_HT_SOURCE_WRITE_DATA_SWAP));
		}
#endif

		/*
		 *  HT link frequency changes after the warm reset
		 */
#if 0
		regData = RM9150_HT_READ(RM9150_HT_GHTC_CONFIG);
		if (dbg) printf("GHTC 0x%x\n", regData);
		regData |= (0x1 << 10);		/* rx clock synchronous */
		RM9150_HT_WRITE(RM9150_HT_GHTC_CONFIG, regData);
#endif

		/*
		 *  Enumerate and resize link frequency
		 */
		_ht_chain_init(1);

		/*
		 *  HT link frequency changes after the warm reset
		 */
#if 0
		regData = RM9150_HT_READ(RM9150_HT_GHTC_CONFIG);
		if (dbg) printf("GHTC 0x%x\n", regData);
		regData |= (0x1 << 10);		/* rx clock synchronous */
		RM9150_HT_WRITE(RM9150_HT_GHTC_CONFIG, regData);
#endif

		/*
		 *  HT link frequency changes after the warm reset
		 */
		printf("calling _ht_reset(HT_WARM_RESET)...");
		_ht_reset(HT_WARM_RESET);
		printf("done\n");

		/* 
		 *  Re-enumerate the chain 
		 */
		printf("calling _ht_chain_init...");
		_ht_chain_init(0);
		printf("done\n");
	}
#endif

	if (hypertransport_enable)
		return 2;	/* Two PCI busses to configure */
	else
		return 3;	/* Three PCI busses to configure */
}


int
rm9k_ht_reset(void)
{
	int regData;
	int i;

        /* Reset the link */
	regData = RM9150_HT_READ(RM9150_HT_LINK_CONTROL) | (1<<4);
        RM9150_HT_WRITE(RM9150_HT_LINK_CONTROL, regData);
        RM9150_HT_READ(RM9150_HT_LINK_CONTROL);
	for (i = 0; i < 100000; i++) {
                RM9150_HT_READ(RM9150_HT_LINK_CONTROL_CONFIG);
	}

        /* Turn off reset */
	regData = RM9150_HT_READ(RM9150_HT_LINK_CONTROL) & 0xffffffef;
        RM9150_HT_WRITE(RM9150_HT_LINK_CONTROL, regData);
        RM9150_HT_READ(RM9150_HT_LINK_CONTROL);
        for (i = 0; i < 100000; i++)
                RM9150_HT_READ(RM9150_HT_LINK_CONTROL_CONFIG);

        /* Wait for HW init to complete */
        for (i = 0; i < 10000000; i++) {
                if (RM9150_HT_READ(RM9150_HT_LINK_CONTROL_CONFIG) & (1<<5))
                        break;
	}

        /* Enable the master again */
        RM9150_HT_WRITE(RM9150_HT_STATUS_CMD, 6);

	return (0);
}


/*
 *  Called to reinitialise the bridge after we've scanned each PCI device
 *  and know what is possible. We also set up the interrupt controller
 *  routing and level control registers.
 */
void
_pci_hwreinit (void)
{
}

void
_pci_flush (void)
{
}


/*
 *  Map the CPU virtual address of an area of local memory to a PCI
 *  address that can be used by a PCI bus master to access it.
 */
vm_offset_t
_pci_dmamap(va, len)
	vm_offset_t va;
	unsigned int len;
{
	return(pci_local_mem_pci_base + VA_TO_PA (va));
}

/*
 *  Map the PCI address of an area of local memory to a CPU physical
 *  address.
 */
vm_offset_t
_pci_cpumap(pcia, len)
	vm_offset_t pcia;
	unsigned int len;
{
	return PA_TO_VA(pcia - pci_local_mem_pci_base);
}


/*
 *  Make pci tag from bus, device and function data.
 */
pcitag_t
_pci_make_tag(bus, device, function)
	int bus;
	int device;
	int function;
{
	pcitag_t tag;
 
	tag = (bus << 16) | (device << 11) | (function << 8);
	return(tag);
}

/*
 *  Break up a pci tag to bus, device function components.
 */
void
_pci_break_tag(tag, busp, devicep, functionp)
	pcitag_t tag;
	int *busp;
	int *devicep;
	int *functionp;
{
	if (busp) {
		*busp = (tag >> 16) & 255;
	}
	if (devicep) {
		*devicep = (tag >> 11) & 31;
	}
	if (functionp) {
		*functionp = (tag >> 8) & 7;
	}
}

int
_pci_canscan (pcitag_t tag)
{
	int bus, device, function;

	_pci_break_tag (tag, &bus, &device, &function); 
	if (bus == 0 || bus == 1) {
		if (device == 0 || device == 1) {
			return 0;	/* Ignore host itself */
		}
	}
	else if (bus == 2) {
		if (hypertransport_enable == 0)
			return 0;
		if (device == 0) {
			return 0;
		}
	}
	return 1;
}

/*
 *  Read a value form PCI configuration space. Support for
 *  all three data sizes (byte, halfword and word) is provided.
 */
static pcireg_t
_pci_conf_readn(tag, reg, width)
	pcitag_t tag;
	int reg;
	int width;
{
	pcireg_t data;
	u_int32_t adr;
	int bus, device, function;

	if (reg & (width-1) || reg < 0 || reg >= 0x100) {
		if (_pcidebug >= 1) {
			_pci_tagprintf (tag, "_pci_conf_read: bad reg 0x%x\r\n", reg);
		}
		return ~0;
	}

	_pci_break_tag (tag, &bus, &device, &function); 

	/* Type 0 configuration for onboard PCI bus */
	if (device > 29 || function > 7) {
		return ~0;		/* device out of range */
	}

	/* 
	 *  Logical bus number is zero for the FDB PCI controllers connected to
	 *  physical bus 0 and 1.
	 */
	adr = (device << 11) | (function << 8) | reg | PCI_CONFIG_ENABLE;

	/*
	 *  Execute config cycle over pci or HT interface
	 */
	if (bus == 0) {
		RM9150_PCI0_WRITE(RM9150_PCI_CONFIG_ADDR, adr);

		/* Wait for config status bit */
		while (RM9150_PCI0_READ(RM9150_PCI_CONFIG_STATUS) != (0x1 << 31))
			DELAY(5);

		/* 
	 	 *  Config read to a device that doesn't respond results
	 	 *  in a bus error instead of expected master abort bit being
	 	 *  set and data = ~0.
	 	 */
		if (load_word((register_t) (PHYS_TO_UNCACHED(RM9150_PCI0_DCR) + 
			 	RM9150_PCI_CONFIG_DATA), &data))
			data = ~0;
	}
	else if (bus == 1) {
		RM9150_PCI1_WRITE(RM9150_PCI_CONFIG_ADDR, adr);

		/* Wait for config status bit */
		while (RM9150_PCI1_READ(RM9150_PCI_CONFIG_STATUS) != (0x1 << 31))
			DELAY(5);

		/* 
	 	 *  Config read to a device that doesn't respond results
	 	 *  in a bus error instead of expected master abort bit being
	 	 *  set and data = ~0.
	 	 */
		if (load_word((register_t)(PHYS_TO_UNCACHED(RM9150_PCI1_DCR) + 
			 	RM9150_PCI_CONFIG_DATA), &data))
			data = ~0;
	}
	else if (bus > 1) {
                if (!hypertransport_enable)
                        return ~0;
		adr |= (bus << 16) ;	/* hypertransport responds to bus number */
		if (bus > 8)		/* first subordinate bus is 9 */
                	adr |= 1;	/* type 1 config cycle */
                RM9150_HT_WRITE(RM9150_HT_CONFIG_ADDR, adr);
		if (load_word((register_t) (PHYS_TO_UNCACHED(RM9150_HT_DCR) + 
				RM9150_HT_CONFIG_DATA), &data))
			data = ~0;
	}

	if (0 && data != 0xffffffff)
		printf("cfg read: bus %d dev %d func %d reg 0x%x data 0x%x\n", 
			bus, device, function, reg, data);

	return data;
}


pcireg_t
_pci_conf_read(pcitag_t tag, int reg)
{
	return _pci_conf_readn(tag, reg, 4);
}

/*
 *  Write a value to PCI configuration space.
 */
static void
_pci_conf_writen(tag, reg, data, width)
	pcitag_t tag;
	int reg;
	pcireg_t data;
	int width;
{
	u_int32_t adr;
	int bus, device, function;

	if (reg & (width-1) || reg < 0 || reg >= 0x100) {
		if (_pcidebug >= 1) { 
			_pci_tagprintf(tag, "_pci_conf_write: bad reg 0x%x\r\n", reg);
		}
		return;
	}

	_pci_break_tag (tag, &bus, &device, &function);

	/* Type 0 configuration for onboard PCI buses */
	if (device > 29 || function > 7) {
		return;		/* device out of range */
	}

	/* 
	 *  Logical bus number is zero for the FDB PCI controllers connected to
	 *  physical bus 0 and 1.
	 */
	adr = (device << 11) | (function << 8) | reg | PCI_CONFIG_ENABLE;

	//printf("cfg write: bus %d dev %d func %d reg 0x%x  data 0x%08x\n", 
		//bus, device, function, reg, data);

        /*
         *  Execute config cycle over pci or HT interface
         */
	if (bus == 0) {
		RM9150_PCI0_WRITE(RM9150_PCI_CONFIG_ADDR, adr);
		RM9150_PCI0_WRITE(RM9150_PCI_CONFIG_DATA, data);
	}
	else if (bus == 1) {
		RM9150_PCI1_WRITE(RM9150_PCI_CONFIG_ADDR, adr);
		RM9150_PCI1_WRITE(RM9150_PCI_CONFIG_DATA, data);
	}
	else if (bus > 1) {
                if (!hypertransport_enable)
                        return;
		adr |= (bus << 16) ;	/* hypertransport responds to bus number */
		if (bus > 8)		/* first subordinate bus is 9 */
                	adr |= 1;	/* type 1 config cycle */
                RM9150_HT_WRITE(RM9150_HT_CONFIG_ADDR, adr);
                RM9150_HT_WRITE(RM9150_HT_CONFIG_DATA, data);
	}
}


void
_pci_conf_write(pcitag_t tag, int reg, pcireg_t data)
{
    _pci_conf_writen (tag, reg, data, 4);
}


/*
 *  Get contents of PCI Mapping register and do any machine
 *  dependent mapping setup.
 */
int
_pci_map_port(tag, reg, port)
	pcitag_t tag;
	int reg;
	unsigned int *port;
{
	pcireg_t address;
    
	if (reg < PCI_MAPREG_START || reg >= PCI_MAPREG_END || (reg & 3)) {
		if (_pcidebug >= 1) {
			_pci_tagprintf(tag, "_pci_map_port: bad request\r\n");
		}
		return -1;
	}
    
	address = _pci_conf_read(tag, reg);
    
	if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_IO) {
		if (_pcidebug >= 1) {
			_pci_tagprintf (tag, "_pci_map_port: attempt to i/o map a memory region\r\n");
		}
		return(-1);
	}

	*port = (address & PCI_MAPREG_IO_ADDR_MASK) - PCI_IO_SPACE_PCI_BASE;
	return(0);
}


void *
_pci_map_int(tag, level, func, arg)
	pcitag_t tag;
	int level;
	int (*func) (void *);
	void *arg;
{
	pcireg_t data;
	int pin, bus, device;

	data = _pci_conf_read(tag, PCI_INTERRUPT_REG);

	pin = PCI_INTERRUPT_PIN(data);

	if (pin == 0) {
		/* No IRQ used. */
		return NULL;
	}

	if (pin > 4) {
		if (_pcidebug >= 1) {
			_pci_tagprintf (tag, "_pci_map_int: bad interrupt pin %d\r\n", pin);
		}
		return(NULL);
	}

	_pci_break_tag (tag, &bus, &device, NULL);

	if (bus != 0 || device > 5) {
		return(NULL);
	}

	/* XXX need to work this out based on device number etc. */
	_pci_tagprintf(tag, "_pci_map_int: attempt to map device %d pin %c\n", 
		   device, '@' + pin);
	return(NULL);
}


void
pci_sync_cache(p, adr, size, rw)
	void *p;
	vm_offset_t adr;
	size_t size;
	int rw;
{
	CPU_IOFlushDCache(adr, size, rw);
}

