/*
 * Copyright (c) 2004 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.
 * 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.
 *
 */
 
/* This file has been modified by Alliance Semiconductor */

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

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

#include <machine/bus.h>

#include "include/yosemite.h"
#include <machine/rm9000_reg.h>
#include <machine/rm9000_ht.h>
#include <machine/rm9000_pci.h>

#include <pmon.h>

#include "htreg.h"

extern void *pmalloc (size_t);

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

/* 
 *  Hypertransport definitions 
 */
#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 rm9000_pci = 0;
static int dbg = 1;
struct pci_bus *_pci_bus[16];
int _max_pci_bus = 0;
int hypertransport_enable;

#define HT_WARM_RESET 1
#define HT_COLD_RESET 0

/* 0x0640: HTLCC 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_SBR_OFFSET	15
#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 uid, int reg);
static void _cfg_write(int bus, int uid, int reg, int data);

static int  _get_capability_ptr(int uid, int capability);

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


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

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

static int
_cfg_read(int bus, int uid, int reg)
{	
        RM9K_WRITE(RM9000_HTCFGA, 
		(bus<<16) | (uid<<11) | PCI_CONFIG_ENABLE | reg);
        return RM9K_READ(RM9000_HTCFGD);
}

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

	Function: _cfg_write
	Description: CSR write access
					
	Parameters: 
		bus - PCI/HT bus number
		uid - 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 uid, int reg, int data)
{	
        RM9K_WRITE(RM9000_HTCFGA, 
		(bus<<16) | (uid<<11) | PCI_CONFIG_ENABLE | reg);
        RM9K_WRITE(RM9000_HTCFGD, data);

}

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

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

static int
_get_capability_ptr(int uid, int capability)
{
	int capPtr;
	int capId;
	
	capPtr = _cfg_read(0, uid, PCI_CAPLISTPTR_REG);
	while (capPtr !=0) {
		capId = _cfg_read(0, uid, capPtr);
		if (PCI_CAPLIST_CAP(capId)==capability) 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 = RM9K_READ(RM9000_HTLCC);
	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;
	}
        RM9K_WRITE(RM9000_HTLCC, regData);
	
        /* Reset the link */
	regData = RM9K_READ(RM9000_HTLINK);
	regData |= HT_BIT(HTLINK_SBR_OFFSET);
        RM9K_WRITE(RM9000_HTLINK, regData);
        RM9K_READ(RM9000_HTLINK);
	for (i = 0; i < 100000; i++) {
                RM9K_READ(RM9000_HTLINK);
	}

        /* Turn off reset */
	regData = RM9K_READ(RM9000_HTLINK);
	regData = HT_BCLR(regData, HTLINK_SBR_OFFSET);
        RM9K_WRITE(RM9000_HTLINK, regData);
        RM9K_READ(RM9000_HTLINK);
        for (i = 0; i < 100000; i++)
                RM9K_READ(RM9000_HTLINK);

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

        /* Enable the master again */
        RM9K_WRITE(RM9000_HTSC, 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: 
		uid - PCI/HT device number.
		link - HT link number.		
******************************************************************************/

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


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

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

static int
_ht_get_freq_cap(int uid, int link)
{
	int htCapPtr;
	int freqCapCsr;	
	
	htCapPtr = _get_capability_ptr(uid, PCI_CAP_ID_LDT);
	freqCapCsr = _cfg_read(0, uid, 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: 
		uid - PCI/HT device number.
		link - HT link number.	
		freq - frequency value to be programmed to	
******************************************************************************/

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

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

	if (uid==0) {
		/* host interface */
		/* RM9000 transmit link */
		freqCsr = RM9K_READ(RM9000_HTFQREV);
		freqCsr &= 0xfffff0ff;
		freqCsr |= (freq<<8);
		RM9K_WRITE(RM9000_HTFQREV, freqCsr);

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

	//printf("_ht_set_freq_cap: uid=%d, link=%d freq=%d, csr@[%2x]=0x%08x\n",
		//uid, 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;

	/*
	 *  Set bit 15 (undocumented) of HTPLL register
	 *  so that the HT link frequency is set by the
	 *  HTFQREV register setting upon warm reset.
	 */	
	regData = RM9K_READ(RM9000_HTPLL) | (1 << 15);
	RM9K_WRITE(RM9000_HTPLL, regData);

	/*
	 *  Determine maximum allowed link frequency
	 */ 
	if (freqResize) {
		prevLink=0;
		prevUid=0;
		regData = RM9K_READ(RM9000_HTFQREV);
		prevFreqCap= HT_LNKFREQCAP(regData);

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

	/* Set cacheline size */
	regData = _cfg_read(0, 0, PCI_BHLC_REG);
	regData |= 8;					/* cacheline size 8 bytes */
	_cfg_write(0, 0, PCI_BHLC_REG, regData);
	
	if (dbg) printf("cacheline size %d bytes\n", (_cfg_read(0, 0, PCI_BHLC_REG) & 0xff));

	/* Set prefetch related configuration */
	_cfg_write(0, 0, 0x60, 0xcff);

	if (dbg) printf("read control 1 0x%08x\n", (_cfg_read(0, 0, 0x60) & 0x00ffffff));

	/* Set bridge command register */
	_cfg_write(0, 0, 0x4, 0xcff);

	if (dbg) printf("bridge command reg 0x%04x\n", (_cfg_read(0, 0, 0x4) & 0x0000ffff));

	/*
	 *  Configure each device in the chain
	 */	
	linkControl = RM9K_READ(RM9000_HTLINK);
	while (!(linkControl&HT_BIT(HT_LNKCTRL_LKFAIL)) && (linkControl&HT_BIT(HT_LNKCTRL_INIT)) )
	{
		deviceId = _cfg_read(0, 0, 0);

		/* 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));

		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);
			_ht_set_freq_cap(prevUid, prevLink, newFreq);

			/* 
			 *  Set RM9000 transmit numerator and denominator
			 *  synchronization fifo parameters.
			 */
			if (unitId == 1) {
				tx_den = RM9K_READ(RM9000_HTSRI);
				tx_den &= ~0x3e0;	/* clear tx denominator */
				switch (newFreq) {
					case HT_LNKFREQ_200MHZ:
						tx_num = 0x1;
						tx_den |= (0x4 << 5);
						break;
	
					case HT_LNKFREQ_300MHZ:
						tx_num = 0x7;
						tx_den |= (0x8 << 5);
						break;
	
					case HT_LNKFREQ_400MHZ:
						tx_num = 0x1;
						tx_den |= (0x2 << 5);
						break;
	
					case HT_LNKFREQ_500MHZ:
						tx_num = 0x1f;
						tx_den |= (0x8 << 5);
						break;
	
					case HT_LNKFREQ_600MHZ:
						tx_num = 0x7;
						tx_den |= (0x4 << 5);
						break;
	
					case HT_LNKFREQ_800MHZ:
						tx_num = 0x1;
						tx_den |= (0x2 << 5);
						break;
				}
				RM9K_WRITE(RM9000_HTTXNUM, tx_num);
				RM9K_WRITE(RM9000_HTSRI, 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);	
}


/*
 *  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;

	/*
	 *  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
	 */

	/*
	 *  Use the modebits to determine if pci or hypertransport
	 *  interface is enabled.
	 */
	rm9000_pci = ((RM9K_READ(RM9K_CONF032) >> (47-32)) & 0x1);

	/*
	 *  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;
	if (rm9000_pci) {
		pcidev->pa.pa_iot = iot;
		pcidev->pa.pa_memt = memt;
	}
	else {
	        pcidev->pa.pa_iot = pmalloc(sizeof(struct tgt_bus_space));
        	pcidev->pa.pa_memt = pmalloc(sizeof(struct tgt_bus_space));
        	pcidev->pa.pa_iot->bus_reverse = 1;
        	pcidev->pa.pa_memt->bus_reverse = 1;
	}
	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 *)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");

	/*
	 *  Enable PCI 0 as master for config cycles
	 */
	stat = RM9K_READ(RM9000_PCI_COMMAND_STATUS);
	stat |= PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_MEM_ENABLE;
	RM9K_WRITE(RM9000_PCI_COMMAND_STATUS, stat);

	/*
	 *  Setup host IO and Memory space
	 */
        RM9K_WRITE(RM9000_LKB7, (PCI0_IO_SPACE_BASE >> 4) | 1);
        RM9K_WRITE(RM9000_LKM7, ((PCI0_IO_SPACE_SIZE >> 4) - 1) & 0xffffff00);
        RM9K_WRITE(RM9000_LKB8, (PCI0_MEM_SPACE_BASE >> 4) | 1);
        RM9K_WRITE(RM9000_LKM8, ((PCI0_MEM_SPACE_SIZE >> 4) - 1) & 0xffffff00);

	printf("pci0 io space mask 0x%08x\n", ((PCI0_IO_SPACE_SIZE >> 4) - 1) & 0xffffff00);
	printf("pci0 mem space mask 0x%08x\n", ((PCI0_MEM_SPACE_SIZE >> 4) - 1) & 0xffffff00);

	/*
	 *  Setup PCI BARs for target access to up to 1 GB of host memory.
	 */
	RM9K_WRITE(RM9000_PCI_BAR0, 0x0);
	RM9K_WRITE(RM9000_PCI_BAR0_PHYS, 0x0);
	RM9K_WRITE(RM9000_PCI_BAR0_MASK, 0x0ffff029);	/* 0 - 256 MB */

	RM9K_WRITE(RM9000_PCI_BAR1, 0x10000000);
	RM9K_WRITE(RM9000_PCI_BAR1_PHYS, (0x10000000 >> 4));
	RM9K_WRITE(RM9000_PCI_BAR1_MASK, 0x0ffff029);	/* 256 - 512 MB */

	RM9K_WRITE(RM9000_PCI_BAR2, 0x20000000);
	RM9K_WRITE(RM9000_PCI_BAR2_PHYS, (0x20000000 >> 4));
	RM9K_WRITE(RM9000_PCI_BAR2_MASK, 0x0ffff029);	/* 256 - 512 MB */

	RM9K_WRITE(RM9000_PCI_BAR3, 0x30000000);
	RM9K_WRITE(RM9000_PCI_BAR3_PHYS, (0x30000000 >> 4));
	RM9K_WRITE(RM9000_PCI_BAR3_MASK, 0x0ffff029);	/* 256 - 512 MB */

	/*
	 *  Drivers expect to have to byte swap so configure
	 *  the PCI/HT controller appropriately.
	 */
#if BYTE_ORDER == BIG_ENDIAN
                /* Enable CPU to PCI/HT byte swap */
                RM9K_WRITE(RM9000_PCI_HT_DATA_CTRL, (0x1 << 16));	/* byte swap */
                //RM9K_WRITE(RM9000_PCI_HT_DATA_CTRL, (0x1 << 17));	/* word swap */
                //RM9K_WRITE(RM9000_PCI_HT_DATA_CTRL, (0x3 << 16));	/* byte + word swap */
                //RM9K_WRITE(RM9000_PCI_HT_DATA_CTRL, 0);		/* no swap */
#endif


	/* 
	 *  Set debug flag based on Switch S4:4 
	 */	
	if ((FPGA_READ(0x2) & USERSWITCH_S5_4) == 0) {
		dbg = 1;
	}
			
        /*
         *  Enable HT interface as master to do config cycles.
         */
        hypertransport_enable = 0;

        /* Wait for HW init to complete */
        for (i = 0; i < 10000; i++) {
                if (RM9K_READ(RM9000_HTLINK) & (1<<5))
                        break;
	}

        if (!getenv("noht") && RM9K_READ(RM9000_HTLINK) & (1<<5)) {

		printf("hypertransport link configuration\n");

                /* Assume for the moment that this works... */
                hypertransport_enable = 1;

                RM9K_WRITE(RM9000_HTSC, 6);		/* enable master */

		/*
		 *  Setup Hypertransport BARs
		 */
		RM9K_WRITE(RM9000_HTBAR0, 6);		/* shifted match */
		//RM9K_WRITE(RM9000_HTMASK0, 0x0fffffff);
		//RM9K_WRITE(RM9000_HTMASK0, 0xfffffff1);
		/* E9000 responds to HT transactions from 0-128MB */
		RM9K_WRITE(RM9000_HTMASK0, 0x7fff1);	/* E9000 respond to HT transactions

		RM9K_WRITE(RM9000_HTBAA30, 1);

		printf("ht bar 0 0x%08x\n", RM9K_READ(RM9000_HTBAR0));
		printf("ht size 0 0x%08x\n", RM9K_READ(RM9000_HTMASK0));
		printf("ht bar 1 0x%08x\n", RM9K_READ(RM9000_HTBAR1));
		printf("ht size 1 0x%08x\n", RM9K_READ(RM9000_HTMASK1));
		printf("ht bar 0-3 attribute 0x%08x\n", RM9K_READ(RM9000_HTBAA30));
		printf("ht rx buffer ctrl/tx ctrl 0x%08x\n", RM9K_READ(RM9000_HTRXDB));
		printf("data swap control 0x%08x\n", RM9K_READ(RM9000_PCI_HT_DATA_CTRL));

                /*  SW workaround for RM9000x2 1.0 errata
                 *  Limit how many tranx can be accepted by softmacro on RX
                 *  link.  This will not be necessary for 2.0
                 */
                RM9K_WRITE(RM9000_HTRXDB, (1<<12) | (1<<10) | (1<<8) | 1);

		regData = RM9K_READ(RM9000_HTFQREV);
		host_link_tx_freq = (regData >> 8) & 0x7;

		regData = RM9K_READ(RM9000_HTFQREV);
		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);

		/* Reset the link */
		rm9k_ht_reset();

                /* If INIT is off, leave bus disabled */
                if (!(RM9K_READ(RM9000_HTLINK) & (1<<5)))
                        hypertransport_enable = 0;
        }

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

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

		/*
		 *  HT link frequency changes after the warm reset
		 */
		_ht_reset(HT_WARM_RESET);

		/* 
		 *  Re-enumerate the chain 
		 */
		_ht_chain_init(0);
	}

	return(1);	/* One bus */
}


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

        /* Reset the link */
	regData = RM9K_READ(RM9000_HTLINK) | (1<<15);
        RM9K_WRITE(RM9000_HTLINK, regData);
        RM9K_READ(RM9000_HTLINK);
	for (i = 0; i < 100000; i++) {
                RM9K_READ(RM9000_HTLINK);
	}

        /* Turn off reset */
	regData = RM9K_READ(RM9000_HTLINK) & 0xffff7fff;
        RM9K_WRITE(RM9000_HTLINK, regData);
        RM9K_READ(RM9000_HTLINK);
        for (i = 0; i < 100000; i++)
                RM9K_READ(RM9000_HTLINK);

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

        /* Enable the master again */
        RM9K_WRITE(RM9000_HTSC, 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 (device == 0) {
		return(0);	/* Ignore the host */
	}
	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 on onboard PCI bus */
	if (device > 29 || function > 7) {
		return ~0;		/* device out of range */
	}
	adr = (bus << 16) | (device << 11) | (function << 8) | reg | PCI_CONFIG_ENABLE;

	/*
	 *  Execute config cycle over pci or HT interface
	 */
	if (rm9000_pci) {
		RM9K_WRITE(PCI0_CONFIG_ADDR, adr);
		data = RM9K_READ(PCI0_CONFIG_DATA);
	}
	else {
                /* HyperTransport */
                if (!hypertransport_enable)
                        return ~0;

                if (bus != 0) {
                        adr |= 1;	/* type 1 cycle */
                }
                RM9K_WRITE(RM9000_HTCFGA, adr);
                data = RM9K_READ(RM9000_HTCFGD);
	}

	if (data != 0xffffffff)
		//printf("cfg read: bus %d dev %d func %d reg 0x%x data 0x%08x\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 on onboard PCI buses */
	if (device > 29 || function > 7) {
		return;		/* device out of range */
	}
	adr = (bus << 16) | (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 (rm9000_pci) {
		RM9K_WRITE(PCI0_CONFIG_ADDR, adr);
		RM9K_WRITE(PCI0_CONFIG_DATA, data);
	}
	else {
                /* HyperTransport */
                if (!hypertransport_enable)
                        return;

                if (bus != 0) {
                        adr |= 1;	/* type 1 cycle */
                }
                RM9K_WRITE(RM9000_HTCFGA, adr);
                RM9K_WRITE(RM9000_HTCFGD, 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);
}

