/*
 * Copyright 2011 Allied Telesis Labs
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <command.h>
#include <asm/addrspace.h>
#include <epi3.h>
#include <bootmenu.h>
#include <pci.h>

#ifdef CONFIG_PCI

#define CONFIG_PCI_SYS_MEM_BUS		CONFIG_SYS_SDRAM_BASE
#define CONFIG_PCI_SYS_MEM_PHYS		CONFIG_SYS_SDRAM_BASE

#define K1BASE32		(0xa0000000)
#define PHYS_TO_K1(x)	((x) | K1BASE32) /* 32-bit compat k1 */


/* Functions for accessing external PCI configuration space, Assume one-hot slot wiring */
#define PCI_SLOT_MAX    16  /* Max. PCI Slots */

#define PCIE_CONFIGREGS 	1		/* Access to config space */
#define PCIE_PCIEREGS 		2		/* Access to pcie registers */
#define	BCM53000_CHIP_ID	0x5300		/* 53000 chipcommon chipid */

typedef struct sbpcieregs {
	u32 control;		/* host mode only */
	u32 pad1[2];
	u32 biststatus;		/* bist Status: 0x00C */
	u32 gpiosel;		/* PCIE gpio sel: 0x010 */
	u32 gpioouten;		/* PCIE gpio outen: 0x14 */
	u32 pad2[2];
	u32 intstatus;		/* Interrupt status: 0x20 */
	u32 intmask;		/* Interrupt mask: 0x24 */
	u32 sbtopcimailbox;	/* sb to pcie mailbox: 0x028 */
	u32 pad3[53];
	u32 sbtopcie0;		/* sb to pcie translation 0: 0x100 */
	u32 sbtopcie1;		/* sb to pcie translation 1: 0x104 */
	u32 sbtopcie2;		/* sb to pcie translation 2: 0x108 */
	u32 pad4[5];

	/* pcie core supports in direct access to config space */
	u32 configaddr;		/* pcie config space access: Address field: 0x120 */
	u32 configdata;		/* pcie config space access: Data field: 0x124 */

	/* mdio access to serdes */
	u32 mdiocontrol;	/* controls the mdio access: 0x128 */
	u32 mdiodata;		/* Data to the mdio access: 0x12c */

	/* pcie protocol phy/dllp/tlp register indirect access mechanism */
	u32 pcieindaddr;	/* indirect access to the internal register: 0x130 */
	u32 pcieinddata;	/* Data to/from the internal regsiter: 0x134 */

	u32 clkreqenctrl;	/* >= rev 6, Clkreq rdma control : 0x138 */
	u32 pad5[177];
	u32 pciecfg[4][64];	/* 0x400 - 0x7FF, PCIE Cfg Space */
	u16 sprom[36];		/* SPROM shadow Area */
} pcieregs_t;



/* PCI control */
#define PCIE_RST_OE	0x01	/* When set, drives PCI_RESET out to pin */
#define PCIE_RST	0x02	/* Value driven out to pin */

/* PCI arbiter control */
#define PCI_INT_ARB	0x01	/* When set, use an internal arbiter */

#define PCI_INTA	0x01	/* PCI INTA# is asserted */

#define	PCIECFG_BUS_SHIFT	24	/* Bus shift */
#define	PCIECFG_SLOT_SHIFT	19	/* Slot/Device shift */
#define	PCIECFG_FUN_SHIFT	16	/* Function shift */
#define	PCIECFG_OFF_SHIFT	0	/* Register shift */

#define	PCIECFG_BUS_MASK	0xff	/* Bus mask */
#define	PCIECFG_SLOT_MASK	0x1f	/* Slot/Device mask */
#define	PCIECFG_FUN_MASK	7	/* Function mask */
#define	PCIECFG_OFF_MASK	0xfff	/* Register mask */

#define	PCI_MAPREG_MEM_ADDR_MASK		0xfffffff0

#define	PCIE_CONFIG_ADDR(b, s, f, o)					\
		((((b) & PCIECFG_BUS_MASK) << PCIECFG_BUS_SHIFT)		\
		 | (((s) & PCIECFG_SLOT_MASK) << PCIECFG_SLOT_SHIFT)	\
		 | (((f) & PCIECFG_FUN_MASK) << PCIECFG_FUN_SHIFT)	\
		 | (((o) & PCIECFG_OFF_MASK) << PCIECFG_OFF_SHIFT))

#define	PCIE_CFGADDR	0x120	/* offsetof(configaddr) */
#define	PCIE_CFGDATA	0x124	/* offsetof(configdata) */

/* SB to PCIE translation masks */
#define SBTOPCIE0_MASK	0xfc000000
#define SBTOPCIE1_MASK	0xfc000000
#define SBTOPCIE2_MASK	0xc0000000

/* Access type bits (0:1) */
#define SBTOPCIE_MEM	0
#define SBTOPCIE_IO	1
#define SBTOPCIE_CFG0	2
#define SBTOPCIE_CFG1	3

/* Prefetch enable bit 2 */
#define SBTOPCIE_PF		4

/* Write Burst enable for memory write bit 3 */
#define SBTOPCIE_WR_BURST	8

/* config access */
#define CONFIGADDR_FUNC_MASK	0x7000
#define CONFIGADDR_FUNC_SHF	12
#define CONFIGADDR_REG_MASK	0x0FFF
#define CONFIGADDR_REG_SHF	0

/* PCIE protocol regs Indirect Address */
#define PCIEADDR_PROT_MASK	0x300
#define PCIEADDR_PROT_SHF	8
#define PCIEADDR_PL_TLP		0
#define PCIEADDR_PL_DLLP	1
#define PCIEADDR_PL_PLP		2

/* PCIE protocol PHY diagnostic registers */
#define	PCIE_PLP_MODEREG		0x200 /* Mode */
#define	PCIE_PLP_STATUSREG		0x204 /* Status */
#define PCIE_PLP_LTSSMCTRLREG		0x208 /* LTSSM control */
#define PCIE_PLP_LTLINKNUMREG		0x20c /* Link Training Link number */
#define PCIE_PLP_LTLANENUMREG		0x210 /* Link Training Lane number */
#define PCIE_PLP_LTNFTSREG		0x214 /* Link Training N_FTS */
#define PCIE_PLP_ATTNREG		0x218 /* Attention */
#define PCIE_PLP_ATTNMASKREG		0x21C /* Attention Mask */
#define PCIE_PLP_RXERRCTR		0x220 /* Rx Error */
#define PCIE_PLP_RXFRMERRCTR		0x224 /* Rx Framing Error */
#define PCIE_PLP_RXERRTHRESHREG		0x228 /* Rx Error threshold */
#define PCIE_PLP_TESTCTRLREG		0x22C /* Test Control reg */
#define PCIE_PLP_SERDESCTRLOVRDREG	0x230 /* SERDES Control Override */
#define PCIE_PLP_TIMINGOVRDREG		0x234 /* Timing param override */
#define PCIE_PLP_RXTXSMDIAGREG		0x238 /* RXTX State Machine Diag */
#define PCIE_PLP_LTSSMDIAGREG		0x23C /* LTSSM State Machine Diag */

/* PCIE protocol DLLP diagnostic registers */
#define PCIE_DLLP_LCREG			0x100 /* Link Control */
#define PCIE_DLLP_LSREG			0x104 /* Link Status */
#define PCIE_DLLP_LAREG			0x108 /* Link Attention */
#define PCIE_DLLP_LAMASKREG		0x10C /* Link Attention Mask */
#define PCIE_DLLP_NEXTTXSEQNUMREG	0x110 /* Next Tx Seq Num */
#define PCIE_DLLP_ACKEDTXSEQNUMREG	0x114 /* Acked Tx Seq Num */
#define PCIE_DLLP_PURGEDTXSEQNUMREG	0x118 /* Purged Tx Seq Num */
#define PCIE_DLLP_RXSEQNUMREG		0x11C /* Rx Sequence Number */
#define PCIE_DLLP_LRREG			0x120 /* Link Replay */
#define PCIE_DLLP_LACKTOREG		0x124 /* Link Ack Timeout */
#define PCIE_DLLP_PMTHRESHREG		0x128 /* Power Management Threshold */
#define PCIE_DLLP_RTRYWPREG		0x12C /* Retry buffer write ptr */
#define PCIE_DLLP_RTRYRPREG		0x130 /* Retry buffer Read ptr */
#define PCIE_DLLP_RTRYPPREG		0x134 /* Retry buffer Purged ptr */
#define PCIE_DLLP_RTRRWREG		0x138 /* Retry buffer Read/Write */
#define PCIE_DLLP_ECTHRESHREG		0x13C /* Error Count Threshold */
#define PCIE_DLLP_TLPERRCTRREG		0x140 /* TLP Error Counter */
#define PCIE_DLLP_ERRCTRREG		0x144 /* Error Counter */
#define PCIE_DLLP_NAKRXCTRREG		0x148 /* NAK Received Counter */
#define PCIE_DLLP_TESTREG		0x14C /* Test */
#define PCIE_DLLP_PKTBIST		0x150 /* Packet BIST */
#define PCIE_DLLP_PCIE11		0x154 /* DLLP PCIE 1.1 reg */

#define PCIE_DLLP_LSREG_LINKUP		(1 << 16)

/* PCIE protocol TLP diagnostic registers */
#define PCIE_TLP_CONFIGREG		0x000 /* Configuration */
#define PCIE_TLP_WORKAROUNDSREG		0x004 /* TLP Workarounds */
#define PCIE_TLP_WRDMAUPPER		0x010 /* Write DMA Upper Address */
#define PCIE_TLP_WRDMALOWER		0x014 /* Write DMA Lower Address */
#define PCIE_TLP_WRDMAREQ_LBEREG	0x018 /* Write DMA Len/ByteEn Req */
#define PCIE_TLP_RDDMAUPPER		0x01C /* Read DMA Upper Address */
#define PCIE_TLP_RDDMALOWER		0x020 /* Read DMA Lower Address */
#define PCIE_TLP_RDDMALENREG		0x024 /* Read DMA Len Req */
#define PCIE_TLP_MSIDMAUPPER		0x028 /* MSI DMA Upper Address */
#define PCIE_TLP_MSIDMALOWER		0x02C /* MSI DMA Lower Address */
#define PCIE_TLP_MSIDMALENREG		0x030 /* MSI DMA Len Req */
#define PCIE_TLP_SLVREQLENREG		0x034 /* Slave Request Len */
#define PCIE_TLP_FCINPUTSREQ		0x038 /* Flow Control Inputs */
#define PCIE_TLP_TXSMGRSREQ		0x03C /* Tx StateMachine and Gated Req */
#define PCIE_TLP_ADRACKCNTARBLEN	0x040 /* Address Ack XferCnt and ARB Len */
#define PCIE_TLP_DMACPLHDR0		0x044 /* DMA Completion Hdr 0 */
#define PCIE_TLP_DMACPLHDR1		0x048 /* DMA Completion Hdr 1 */
#define PCIE_TLP_DMACPLHDR2		0x04C /* DMA Completion Hdr 2 */
#define PCIE_TLP_DMACPLMISC0		0x050 /* DMA Completion Misc0 */
#define PCIE_TLP_DMACPLMISC1		0x054 /* DMA Completion Misc1 */
#define PCIE_TLP_DMACPLMISC2		0x058 /* DMA Completion Misc2 */
#define PCIE_TLP_SPTCTRLLEN		0x05C /* Split Controller Req len */
#define PCIE_TLP_SPTCTRLMSIC0		0x060 /* Split Controller Misc 0 */
#define PCIE_TLP_SPTCTRLMSIC1		0x064 /* Split Controller Misc 1 */
#define PCIE_TLP_BUSDEVFUNC		0x068 /* Bus/Device/Func */
#define PCIE_TLP_RESETCTR		0x06C /* Reset Counter */
#define PCIE_TLP_RTRYBUF		0x070 /* Retry Buffer value */
#define PCIE_TLP_TGTDEBUG1		0x074 /* Target Debug Reg1 */
#define PCIE_TLP_TGTDEBUG2		0x078 /* Target Debug Reg2 */
#define PCIE_TLP_TGTDEBUG3		0x07C /* Target Debug Reg3 */
#define PCIE_TLP_TGTDEBUG4		0x080 /* Target Debug Reg4 */

/* MDIO control */
#define MDIOCTL_DIVISOR_MASK		0x7f	/* clock to be used on MDIO */
#define MDIOCTL_DIVISOR_VAL		0x2
#define MDIOCTL_PREAM_EN		0x80	/* Enable preamble sequnce */
#define MDIOCTL_ACCESS_DONE		0x100   /* Tranaction complete */

/* MDIO Data */
#define MDIODATA_MASK			0x0000ffff	/* data 2 bytes */
#define MDIODATA_TA			0x00020000	/* Turnaround */
#define MDIODATA_REGADDR_SHF_OLD	18		/* Regaddr shift (rev < 10) */
#define MDIODATA_REGADDR_MASK_OLD	0x003c0000	/* Regaddr Mask (rev < 10) */
#define MDIODATA_DEVADDR_SHF_OLD	22		/* Physmedia devaddr shift (rev < 10) */
#define MDIODATA_DEVADDR_MASK_OLD	0x0fc00000	/* Physmedia devaddr Mask (rev < 10) */
#define MDIODATA_REGADDR_SHF		18		/* Regaddr shift */
#define MDIODATA_REGADDR_MASK		0x007c0000	/* Regaddr Mask */
#define MDIODATA_DEVADDR_SHF		23		/* Physmedia devaddr shift */
#define MDIODATA_DEVADDR_MASK		0x0f800000	/* Physmedia devaddr Mask */
#define MDIODATA_WRITE			0x10000000	/* write Transaction */
#define MDIODATA_READ			0x20000000	/* Read Transaction */
#define MDIODATA_START			0x40000000	/* start of Transaction */

#define MDIODATA_DEV_ADDR		0x0		/* dev address for serdes */
#define	MDIODATA_BLK_ADDR		0x1F		/* blk address for serdes */

/* Sonics to PCI translation types */
#define SBTOPCI0_MASK	0xfc000000
#define SBTOPCI1_MASK	0xfc000000
#define SBTOPCI2_MASK	0xc0000000
#define SBTOPCI_MEM	0
#define SBTOPCI_IO	1
#define SBTOPCI_CFG0	2
#define SBTOPCI_CFG1	3
#define	SBTOPCI_PREF	0x4		/* prefetch enable */
#define	SBTOPCI_BURST	0x8		/* burst enable */
#define	SBTOPCI_RC_MASK		0x30	/* read command (>= rev11) */
#define	SBTOPCI_RC_READ		0x00	/* memory read */
#define	SBTOPCI_RC_READLINE	0x10	/* memory read line */
#define	SBTOPCI_RC_READMULTI	0x20	/* memory read multiple */

#define CHIPSTAT_PCIE0_MODE_MASK	0x2000 /* PCIE0 mode */
#define CHIPSTAT_PCIE1_DIS_MASK		0x20 /* PCIE1 disable */

#define MAXFUNCS		2

#define PCIE_BUS_0_ADDRESS	((pcieregs_t *)0xb8005000)
#define PCIE_BUS_1_ADDRESS	((pcieregs_t *)0xb800E000)
#define CHIP_STATUS		0xb800002c
#define CHIP_ID			0xb8000000

static struct pci_config_table pcie_x510_config_table[] =
{
	{
		0x14E4, 0xB321, PCI_ANY_ID, 0, /* Broadcom BCM56321 */
		1, PCI_ANY_ID, pci_cfgfunc_config_device,
		{ PHYS_TO_K1(SI_PCI_MEM), PHYS_TO_K1(SI_PCI_MEM), PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER },
	},
	{
		0x14E4, 0xB321, PCI_ANY_ID, 1, /* Broadcom BCM56321 */
		1, PCI_ANY_ID, pci_cfgfunc_config_device,
		{ TLB_PCIE1_VIRT_UNCACHED, TLB_PCIE1_VIRT_UNCACHED, PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER },
	}
};

static struct pci_controller pcie_hose[] = {
       {
       config_table:pcie_x510_config_table,
       },
       {
       config_table:pcie_x510_config_table,
       }
};

/* Disable PCI host core */
static u32 pci_disabled[] = {
	0,
	0
};


static u32
pcie_config_cmd(u32 pci_core, u32 bus, u32 dev, u32 func, u32 off)
{
	pcieregs_t *pcie_regs = (pci_core == 0) ? PCIE_BUS_0_ADDRESS : PCIE_BUS_1_ADDRESS;
	u32 addr = 0, *sbtopci1;
	u32 retval = 0xFFFFFFFF;

	/* Issue config commands only when the data link is up (atleast
	 * one external pcie device is present).
	 */
	sbtopci1 = NULL;
	if ( dev < 2 ) {
		*(volatile u32 *)&pcie_regs->pcieindaddr = PCIE_DLLP_LSREG;
		retval = *(volatile u32 *)&pcie_regs->pcieinddata;
		if ((retval  & PCIE_DLLP_LSREG_LINKUP)) {
			sbtopci1 = &pcie_regs->sbtopcie1;
		}
		else {
			*(volatile u32 *)&pcie_regs->pcieindaddr = PCIE_DLLP_LSREG;
			retval = *(volatile u32 *)&pcie_regs->pcieinddata;
			return 0;
		}
	} else {
		return 0;
	}

	/* Type 0 transaction */
	if (bus == 0) {
		/* Skip unwired slots */
		if (dev < PCI_SLOT_MAX) {
			/* Slide the PCI window to the appropriate slot */
			*(volatile u32 *)sbtopci1 = SBTOPCI_CFG0;
			if (pci_core == 0) {
				addr = (CONFIG_PCIE_CFG |
				(dev << PCIECFG_SLOT_SHIFT) |
				(func << PCIECFG_FUN_SHIFT) |
				(off & ~3));
			} else { /* pci_core == 1 */
				addr = (CONFIG_PCIE1_CFG |
				(dev << PCIECFG_SLOT_SHIFT) |
				(func << PCIECFG_FUN_SHIFT) |
				(off & ~3));
			}
		}
	} else {
		/* Type 1 transaction */
		addr = PCIE_CONFIG_ADDR(bus, dev, func, (off & ~3));

		/*
		 * Higher bus bits (which lets the address exceed the 64MB space)
		 * should be specified in sbtopci1's UpperAddress.
		*/
		*(volatile u32 *)sbtopci1 = (SBTOPCI_CFG1 | (addr & SBTOPCIE1_MASK));

		/* To not exceed the 64MB space (by using UA instead) */
		addr &= ~SBTOPCIE1_MASK;
	}

	if (pci_core == 0) {
		addr |= CONFIG_PCIE_CFG;
	} else { /* pci_core == 1 */
		addr |= CONFIG_PCIE1_CFG;
	}

	return addr;
}


static int
bcm_internal_pcie_read_config(u32 pci_core, u32 bus, u32 dev, u32 func, u32 off, void *buf, int len)
{
	u32		*addr;
	pcieregs_t	*pcie_regs = (pci_core == 0) ? PCIE_BUS_0_ADDRESS : PCIE_BUS_1_ADDRESS;
	u32		val;

	if ( func >= MAXFUNCS || (off + len) > 0x100)
		return -1;

	addr = (u32 *)&pcie_regs->pciecfg[func][off >> 2];

	val = *(volatile u32 *)(addr);
	val >>= 8 * (off & 3);
	if (len == 4)
		*((u32 *) buf) = val;
	else if (len == 2)
		*((u16 *) buf) = (u16) val;
	else if (len == 1)
		*((u8 *) buf) = (u8) val;
	else
		return -1;

	return 0;
}


static int
bcm_external_pcie_read_config(u32 pci_core, u32 bus, u32 dev, u32 func, u32 off, void *buf, int len)
{
	pcieregs_t *pcie_regs = (pci_core == 0) ? PCIE_BUS_0_ADDRESS : PCIE_BUS_1_ADDRESS;
	u32 addr = 0, *reg = NULL, val;
	int ret = 0;


	if (pci_disabled[pci_core]) {
		val = 0xffffffff;
		ret = -1;
	} else if (bus == 0 && dev == 0) {
		reg = (u32 *)&pcie_regs->pciecfg[func][off >> 2];
	} else if (((addr = pcie_config_cmd(pci_core, bus, dev, func, off)) == 0) ||
		((reg = (u32 *)PHYS_TO_K1(addr)) == 0)) {
		val = 0xffffffff;
		ret = -1;
	}

	if ( ret == 0) {
		val = *(volatile u32 *)reg;
	}

	val >>= 8 * (off & 3);
	if (len == 4)
		*((u32 *) buf) = val;
	else if (len == 2)
		*((u16 *) buf) = (u16) val;
	else if (len == 1)
		*((u8 *) buf) = (u8) val;
	else
		ret = -1;

	return ret;
}


static int
bcm_internal_pcie_write_config(u32 pci_core, u32 func, u32 off, void *buf, int len)
{
	u32		val;
	u32		*addr;
	pcieregs_t	*pcie_regs = (pci_core == 0) ? PCIE_BUS_0_ADDRESS : PCIE_BUS_1_ADDRESS;

	if ( func >= MAXFUNCS || (off + len) > 0x100)
		return -1;

	addr = (u32 *)&pcie_regs->pciecfg[func][off >> 2];

	val = *(volatile u32 *)addr;
	if (len == 4)
		val = *((u32 *) buf);
	else if (len == 2) {
		val &= ~(0xffff << (8 * (off & 3)));
		val |= *((u16 *) buf) << (8 * (off & 3));
	} else if (len == 1) {
		val &= ~(0xff << (8 * (off & 3)));
		val |= *((u8 *) buf) << (8 * (off & 3));
	} else {
		return -1;
	}

	*(volatile u32 *)addr = val;

	/*
	 * Workaround for PCIE transaction ordering issue
	 *
	 * For PCIE config write: write then read back
	 * For PCIE memory write: write then sync (with Config7.ES set)
	*/
	val = *(volatile u32 *)addr;

	return 0;
}

static int
bcm_external_pcie_write_config(u32 pci_core, u32 bus, u32 dev, u32 func, u32 off, void *buf, int len)
{
	pcieregs_t *pcie_regs = (pci_core == 0) ? PCIE_BUS_0_ADDRESS : PCIE_BUS_1_ADDRESS;
	u32 addr = 0, *reg = NULL, val;
	int ret = 0;

	if (pci_disabled[pci_core]) {
		return 0;
	} else if (bus == 0 && dev == 0) {
		reg = (u32 *)&pcie_regs->pciecfg[func][off >> 2];
	} else if (((addr = pcie_config_cmd(pci_core, bus, dev, func, off)) == 0) ||
		((reg = (u32 *)PHYS_TO_K1(addr)) == 0) ) {
		printf("%s: Failed to write!\n", __FUNCTION__);
		return -1;
	}

	val = *(volatile u32 *)reg;
	if (len == 4)
		val = *((u32 *) buf);
	else if (len == 2) {
		val &= ~(0xffff << (8 * (off & 3)));
		val |= *((u16 *) buf) << (8 * (off & 3));
	} else if (len == 1) {
		val &= ~(0xff << (8 * (off & 3)));
		val |= *((u8 *) buf) << (8 * (off & 3));
	} else {
		ret = -1;
		return -1;
	}

	*(volatile u32 *)reg = val;

	/*
	 * Workaround for PCIE transaction ordering issue
	 *
	 * For PCIE config write: write then read back
	 * For PCIE memory write: write then sync (with Config7.ES set)
	*/
	val = *(volatile u32 *)reg;


	return ret;
}

int
bcm_pcie_read_config_byte(struct pci_controller *hose, pci_dev_t pci_dev, int offset, u8 *value)
{
	u32 bus = PCI_BUS (pci_dev);
	u32 dev = PCI_DEV (pci_dev);
	u32 func = PCI_FUNC (pci_dev);
	u32 pci_core = 0;

	if (bus)
	{
		pci_core = 1;
		bus = 0;
	}

	if (dev == 0)
		return bcm_internal_pcie_read_config(pci_core, bus, dev, func, offset, value, 1);
	else
		return bcm_external_pcie_read_config(pci_core, bus, dev, func, offset, value, 1);
}

int
bcm_pcie_read_config_word(struct pci_controller *hose, pci_dev_t pci_dev, int offset, u16 *value)
{
	u32 bus = PCI_BUS (pci_dev);
	u32 dev = PCI_DEV (pci_dev);
	u32 func = PCI_FUNC (pci_dev);
	u32 pci_core = 0;

	if (bus)
	{
		pci_core = 1;
		bus = 0;
	}

	if (dev == 0)
		return bcm_internal_pcie_read_config(pci_core, bus, dev, func, offset, value, 2);
	else
		return bcm_external_pcie_read_config(pci_core, bus, dev, func, offset, value, 2);
}


int
bcm_pcie_read_config_dword(struct pci_controller *hose, pci_dev_t pci_dev, int offset, u32 *value)
{
	u32 bus = PCI_BUS (pci_dev);
	u32 dev = PCI_DEV (pci_dev);
	u32 func = PCI_FUNC (pci_dev);
	u32 pci_core = 0;

	if (bus)
	{
		pci_core = 1;
		bus = 0;
	}

	if (dev == 0)
		return bcm_internal_pcie_read_config(pci_core, bus, dev, func, offset, value, 4);
	else
		return bcm_external_pcie_read_config(pci_core, bus, dev, func, offset, value, 4);
}

int
bcm_pcie_write_config_byte(struct pci_controller *hose, pci_dev_t pci_dev, int offset, u8 value)
{
	u32 bus = PCI_BUS (pci_dev);
	u32 dev = PCI_DEV (pci_dev);
	u32 func = PCI_FUNC (pci_dev);
	u32 pci_core = 0;

	if (bus)
	{
		pci_core = 1;
		bus = 0;
	}
	if (dev == 0)
		return bcm_internal_pcie_write_config(pci_core, func, offset, &value, 1);
	else
		return bcm_external_pcie_write_config(pci_core, bus, dev, func, offset, &value, 1);
}

int
bcm_pcie_write_config_word(struct pci_controller *hose, pci_dev_t pci_dev, int offset, u16 value)
{
	u32 bus = PCI_BUS (pci_dev);
	u32 dev = PCI_DEV (pci_dev);
	u32 func = PCI_FUNC (pci_dev);
	u32 pci_core = 0;

	if (bus)
	{
		pci_core = 1;
		bus = 0;
	}
	if (dev == 0)
		return bcm_internal_pcie_write_config(pci_core, func, offset, &value, 2);
	else
		return bcm_external_pcie_write_config(pci_core, bus, dev, func, offset, &value, 2);
}

int
bcm_pcie_write_config_dword(struct pci_controller *hose, pci_dev_t pci_dev, int offset, u32 value)
{
	u32 bus = PCI_BUS (pci_dev);
	u32 dev = PCI_DEV (pci_dev);
	u32 func = PCI_FUNC (pci_dev);
	u32 pci_core = 0;

	if (bus)
	{
		pci_core = 1;
		bus = 0;
	}
	if (dev == 0)
		return bcm_internal_pcie_write_config(pci_core, func, offset, &value, 4);
	else
		return bcm_external_pcie_write_config(pci_core, bus, dev, func, offset, &value, 4);
}


int
pcie_init_pci(u32 pci_core)
{
	pcieregs_t *pcie_regs = pci_core == 0 ? PCIE_BUS_0_ADDRESS : PCIE_BUS_1_ADDRESS;
	u32 w, chipid, retval;
	u16 val;
	u32 base;
	int i, ret = 0;

	chipid = *(volatile u32 *)CHIP_ID;
	w = *(volatile u32 *)CHIP_STATUS;

	if ((pci_core == 1) && ((chipid & 0xffff) == BCM53000_CHIP_ID)) {
		if (w & CHIPSTAT_PCIE1_DIS_MASK) {
			pci_disabled[pci_core] = 1;
			return -1;
		}
	}

	/* check operation mode of PCIe port */
	if (((chipid & 0xffff) == BCM53000_CHIP_ID)) {
		if (w & CHIPSTAT_PCIE0_MODE_MASK) {
			/* EP mode */
			pci_disabled[pci_core] = 1;
			return -1;
		}
	}

	if (pci_core == 0) {
		*(volatile u32 *)(0xb8105408) = 0x1;
		*(volatile u32 *)(0xb8105800) = 0x0;
		*(volatile u32 *)(0xb8005000) = 0x3;
		*(volatile u32 *)(0xb8005128) = 0x84;
		*(volatile u32 *)(0xb800512c) = 0x507e8020;
	} else {
		*(volatile u32 *)(0xb8201408) = 0x1;
		*(volatile u32 *)(0xb8201800) = 0x0;
		*(volatile u32 *)(0xb800F000) = 0x3;
		*(volatile u32 *)(0xb800F128) = 0x84;
		*(volatile u32 *)(0xb800F12c) = 0x507e8020;
	}


	udelay(3000);
	*(volatile u32 *)&pcie_regs->control = PCIE_RST_OE;
	udelay(3000);		/* delay 1 ms */
	*(volatile u32 *)&pcie_regs->control = PCIE_RST | PCIE_RST_OE;
	udelay(3000);		/* delay 1 ms */

	/* 64 MB I/O access window. On 4716 rev 0, use
	 * sbtopcie0 to access the device registers. We
	 * can't use address match 2 (1 GB window) region
	 * as mips can't generate 64-bit address on the
	 * backplane.
	*/
	if (((chipid & 0xffff) == BCM53000_CHIP_ID)) {
		if (pci_core == 0) {
			*(u32 *)&pcie_regs->sbtopcie0 = (SBTOPCIE_MEM |
				SBTOPCIE_WR_BURST | PHYS_TO_K1(CONFIG_PCIE_MEM));
		} else { /* pci_core == 1 */
			*(volatile u32 *)&pcie_regs->sbtopcie0 = (SBTOPCIE_MEM |
				SBTOPCIE_PF | SBTOPCIE_WR_BURST | PHYS_TO_K1(CONFIG_PCIE1_MEM));
		}
	} else {
		*(volatile u32 *)&pcie_regs->sbtopcie0 = SBTOPCIE_IO;
	}

	if (pci_core == 0) {
		/* 64 MB configuration access window */
		*(volatile u32 *)&pcie_regs->sbtopcie1 = SBTOPCIE_CFG0;

		/* 1 GB memory access window */
		*(volatile u32 *)&pcie_regs->sbtopcie2 = (SBTOPCIE_MEM | SBTOPCIE_PF | SBTOPCIE_WR_BURST | CONFIG_PCIE_DMA_H32);
	} else { /* pci_core == 1 */
		/* 64 MB configuration access window */
		*(volatile u32 *)&pcie_regs->sbtopcie1 = SBTOPCIE_CFG0;

		/* 1 GB memory access window */
		*(volatile u32 *)&pcie_regs->sbtopcie2 = (SBTOPCIE_MEM | SBTOPCIE_PF | SBTOPCIE_WR_BURST | CONFIG_PCIE1_DMA_H32);
	}

	/* Enable PCI bridge BAR0 memory & master access */
	/* XXX: Old comment said enable prefetch & burst, but that's not
	* what the code did/does. Do we have to enable PF & burst?
	*/
	val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
	bcm_internal_pcie_write_config(pci_core, 0, PCI_COMMAND, &val, sizeof(val));

	/* setup inbound access to RAM */
	bcm_internal_pcie_read_config(pci_core, 0, 0, 0, 0x18, &base, sizeof(base));
	base &= ~PCI_MAPREG_MEM_ADDR_MASK;
	bcm_internal_pcie_write_config(pci_core, 0, 0x18, &base, sizeof(base));
	base = 0;
	bcm_internal_pcie_write_config(pci_core, 0, 0x1c, &base, sizeof(base));

	/* Enable PCI interrupts */
	*(volatile u32 *)&pcie_regs->intmask = PCI_INTA;

	for ( i = 0; i < 50; i++ ) {
		udelay(1000);
		*(volatile u32 *)&pcie_regs->pcieindaddr = PCIE_DLLP_LSREG;
		retval = *(volatile u32 *)&pcie_regs->pcieinddata;
		if ((retval  & PCIE_DLLP_LSREG_LINKUP)) {
			break;
		}
	}

	return ret;
}


void pci_init_board(void)
{
	struct pci_controller *hose = (struct pci_controller *)&pcie_hose[0];
	int last_busno;

	hose->first_busno = 0;
	hose->last_busno = 0xff;

	pcie_init_pci(0);

	/* PCI memory space */
	pci_set_region (hose->regions + 0,
			PHYS_TO_K1(CONFIG_PCIE_MEM),
			CONFIG_PCIE_MEM,
			CONFIG_PCIE_MEM_SZ, PCI_REGION_MEM);

	/* PCI I/O space */
	pci_set_region (hose->regions + 1,
			PHYS_TO_K1(CONFIG_PCIE_CFG),
			CONFIG_PCIE_CFG,
			CONFIG_PCIE_MEM_SZ, PCI_REGION_IO);

	hose->region_count = 2;

	pci_set_ops (hose,
			bcm_pcie_read_config_byte,
			bcm_pcie_read_config_word,
			bcm_pcie_read_config_dword,
			bcm_pcie_write_config_byte,
			bcm_pcie_write_config_word,
			bcm_pcie_write_config_dword);

	pci_register_hose (hose);

	hose->last_busno = pci_hose_scan (hose);
	last_busno = hose->last_busno;
#ifdef CONFIG_PCIE1
	hose = (struct pci_controller *)&pcie_hose[1];

	hose->first_busno = last_busno + 1;
	hose->last_busno = 0xff;

	if (pcie_init_pci(1) == 0) {

		/* PCI memory space */
		pci_set_region (hose->regions + 0,
			TLB_PCIE1_VIRT_UNCACHED,
			CONFIG_PCIE1_MEM,
			CONFIG_PCIE_MEM_SZ, PCI_REGION_MEM);

		/* PCI I/O space */
		pci_set_region (hose->regions + 1,
			(TLB_PCIE1_VIRT_UNCACHED + 0x04000000),
			CONFIG_PCIE1_CFG,
			CONFIG_PCIE_MEM_SZ, PCI_REGION_IO);

		hose->region_count = 2;

		pci_set_ops (hose,
			bcm_pcie_read_config_byte,
			bcm_pcie_read_config_word,
			bcm_pcie_read_config_dword,
			bcm_pcie_write_config_byte,
			bcm_pcie_write_config_word,
			bcm_pcie_write_config_dword);

		pci_register_hose (hose);

		hose->last_busno = pci_hose_scan (hose);
	}
#endif


	debug ("Done PCI initialization\n");
	return;
}

#endif /* #ifdef CONFIG_PCI */

