diff options
Diffstat (limited to 'arch/mips/pci/pci-xlp.c')
| -rw-r--r-- | arch/mips/pci/pci-xlp.c | 124 | 
1 files changed, 80 insertions, 44 deletions
diff --git a/arch/mips/pci/pci-xlp.c b/arch/mips/pci/pci-xlp.c index ad55f2cfeec..653d2db9e0c 100644 --- a/arch/mips/pci/pci-xlp.c +++ b/arch/mips/pci/pci-xlp.c @@ -46,6 +46,7 @@  #include <asm/netlogic/interrupt.h>  #include <asm/netlogic/haldefs.h> +#include <asm/netlogic/common.h>  #include <asm/netlogic/xlp-hal/iomap.h>  #include <asm/netlogic/xlp-hal/pic.h> @@ -64,8 +65,12 @@ static inline u32 pci_cfg_read_32bit(struct pci_bus *bus, unsigned int devfn,  	u32 data;  	u32 *cfgaddr; +	where &= ~3; +	if (bus->number == 0 && PCI_SLOT(devfn) == 1 && where == 0x954) +		return 0xffffffff; +  	cfgaddr = (u32 *)(pci_config_base + -			pci_cfg_addr(bus->number, devfn, where & ~3)); +			pci_cfg_addr(bus->number, devfn, where));  	data = *cfgaddr;  	return data;  } @@ -157,32 +162,38 @@ struct pci_controller nlm_pci_controller = {  	.io_offset	= 0x00000000UL,  }; -static int get_irq_vector(const struct pci_dev *dev) +static struct pci_dev *xlp_get_pcie_link(const struct pci_dev *dev)  { -	/* -	 * For XLP PCIe, there is an IRQ per Link, find out which -	 * link the device is on to assign interrupts -	*/ -	if (dev->bus->self == NULL) -		return 0; +	struct pci_bus *bus, *p; -	switch	(dev->bus->self->devfn) { -	case 0x8: -		return PIC_PCIE_LINK_0_IRQ; -	case 0x9: -		return PIC_PCIE_LINK_1_IRQ; -	case 0xa: -		return PIC_PCIE_LINK_2_IRQ; -	case 0xb: -		return PIC_PCIE_LINK_3_IRQ; -	} -	WARN(1, "Unexpected devfn %d\n", dev->bus->self->devfn); -	return 0; +	/* Find the bridge on bus 0 */ +	bus = dev->bus; +	for (p = bus->parent; p && p->number != 0; p = p->parent) +		bus = p; + +	return p ? bus->self : NULL; +} + +static inline int nlm_pci_link_to_irq(int link) +{ +	return PIC_PCIE_LINK_0_IRQ + link;  }  int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)  { -	return get_irq_vector(dev); +	struct pci_dev *lnkdev; +	int lnkslot, lnkfunc; + +	/* +	 * For XLP PCIe, there is an IRQ per Link, find out which +	 * link the device is on to assign interrupts +	*/ +	lnkdev = xlp_get_pcie_link(dev); +	if (lnkdev == NULL) +		return 0; +	lnkfunc = PCI_FUNC(lnkdev->devfn); +	lnkslot = PCI_SLOT(lnkdev->devfn); +	return nlm_irq_to_xirq(lnkslot / 8, nlm_pci_link_to_irq(lnkfunc));  }  /* Do platform specific device initialization at pci_enable_device() time */ @@ -191,42 +202,48 @@ int pcibios_plat_dev_init(struct pci_dev *dev)  	return 0;  } -static int xlp_enable_pci_bswap(void) +/* + * If big-endian, enable hardware byteswap on the PCIe bridges. + * This will make both the SoC and PCIe devices behave consistently with + * readl/writel. + */ +#ifdef __BIG_ENDIAN +static void xlp_config_pci_bswap(int node, int link)  { -	uint64_t pciebase, sysbase; -	int node, i; +	uint64_t nbubase, lnkbase;  	u32 reg; -	/* Chip-0 so node set to 0 */ -	node = 0; -	sysbase = nlm_get_bridge_regbase(node); +	nbubase = nlm_get_bridge_regbase(node); +	lnkbase = nlm_get_pcie_base(node, link); +  	/*  	 *  Enable byte swap in hardware. Program each link's PCIe SWAP regions  	 * from the link's address ranges.  	 */ -	for (i = 0; i < 4; i++) { -		pciebase = nlm_pcicfg_base(XLP_IO_PCIE_OFFSET(node, i)); -		if (nlm_read_pci_reg(pciebase, 0) == 0xffffffff) -			continue; +	reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_BASE0 + link); +	nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_BASE, reg); -		reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEMEM_BASE0 + i); -		nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_MEM_BASE, reg); +	reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEMEM_LIMIT0 + link); +	nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_MEM_LIM, reg | 0xfff); -		reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEMEM_LIMIT0 + i); -		nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_MEM_LIM, -			reg | 0xfff); +	reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_BASE0 + link); +	nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_BASE, reg); -		reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_BASE0 + i); -		nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_BASE, reg); - -		reg = nlm_read_bridge_reg(sysbase, BRIDGE_PCIEIO_LIMIT0 + i); -		nlm_write_pci_reg(pciebase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff); -	} -	return 0; +	reg = nlm_read_bridge_reg(nbubase, BRIDGE_PCIEIO_LIMIT0 + link); +	nlm_write_pci_reg(lnkbase, PCIE_BYTE_SWAP_IO_LIM, reg | 0xfff);  } +#else +/* Swap configuration not needed in little-endian mode */ +static inline void xlp_config_pci_bswap(int node, int link) {} +#endif /* __BIG_ENDIAN */  static int __init pcibios_init(void)  { +	struct nlm_soc_info *nodep; +	uint64_t pciebase; +	int link, n; +	u32 reg; +  	/* Firmware assigns PCI resources */  	pci_set_flags(PCI_PROBE_ONLY);  	pci_config_base = ioremap(XLP_DEFAULT_PCI_ECFG_BASE, 64 << 20); @@ -235,7 +252,26 @@ static int __init pcibios_init(void)  	ioport_resource.start =	 0;  	ioport_resource.end   = ~0; -	xlp_enable_pci_bswap(); +	for (n = 0; n < NLM_NR_NODES; n++) { +		nodep = nlm_get_node(n); +		if (!nodep->coremask) +			continue;	/* node does not exist */ + +		for (link = 0; link < 4; link++) { +			pciebase = nlm_get_pcie_base(n, link); +			if (nlm_read_pci_reg(pciebase, 0) == 0xffffffff) +				continue; +			xlp_config_pci_bswap(n, link); + +			/* put in intpin and irq - u-boot does not */ +			reg = nlm_read_pci_reg(pciebase, 0xf); +			reg &= ~0x1fu; +			reg |= (1 << 8) | nlm_pci_link_to_irq(link); +			nlm_write_pci_reg(pciebase, 0xf, reg); +			pr_info("XLP PCIe: Link %d-%d initialized.\n", n, link); +		} +	} +  	set_io_port_base(CKSEG1);  	nlm_pci_controller.io_map_base = CKSEG1;  |