xref: /openbmc/linux/arch/arm/mach-orion5x/pci.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
10fdebc5eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29dd0b194SLennert Buytenhek /*
39dd0b194SLennert Buytenhek  * arch/arm/mach-orion5x/pci.c
49dd0b194SLennert Buytenhek  *
59dd0b194SLennert Buytenhek  * PCI and PCIe functions for Marvell Orion System On Chip
69dd0b194SLennert Buytenhek  *
79dd0b194SLennert Buytenhek  * Maintainer: Tzachi Perelstein <tzachi@marvell.com>
89dd0b194SLennert Buytenhek  */
99dd0b194SLennert Buytenhek 
109dd0b194SLennert Buytenhek #include <linux/kernel.h>
119dd0b194SLennert Buytenhek #include <linux/pci.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
139dd0b194SLennert Buytenhek #include <linux/mbus.h>
14158c0c62SBryan Wu #include <video/vga.h>
15ff89c462SNicolas Pitre #include <asm/irq.h>
169dd0b194SLennert Buytenhek #include <asm/mach/pci.h>
176f088f1dSLennert Buytenhek #include <plat/pcie.h>
1845173d5eSAndrew Lunn #include <plat/addr-map.h>
199dd0b194SLennert Buytenhek #include "common.h"
20c22c2c60SArnd Bergmann #include "orion5x.h"
219dd0b194SLennert Buytenhek 
229dd0b194SLennert Buytenhek /*****************************************************************************
239dd0b194SLennert Buytenhek  * Orion has one PCIe controller and one PCI controller.
249dd0b194SLennert Buytenhek  *
259dd0b194SLennert Buytenhek  * Note1: The local PCIe bus number is '0'. The local PCI bus number
269dd0b194SLennert Buytenhek  * follows the scanned PCIe bridged busses, if any.
279dd0b194SLennert Buytenhek  *
289dd0b194SLennert Buytenhek  * Note2: It is possible for PCI/PCIe agents to access many subsystem's
299dd0b194SLennert Buytenhek  * space, by configuring BARs and Address Decode Windows, e.g. flashes on
309dd0b194SLennert Buytenhek  * device bus, Orion registers, etc. However this code only enable the
319dd0b194SLennert Buytenhek  * access to DDR banks.
329dd0b194SLennert Buytenhek  ****************************************************************************/
339dd0b194SLennert Buytenhek 
349dd0b194SLennert Buytenhek 
359dd0b194SLennert Buytenhek /*****************************************************************************
369dd0b194SLennert Buytenhek  * PCIe controller
379dd0b194SLennert Buytenhek  ****************************************************************************/
383904a393SThomas Petazzoni #define PCIE_BASE	(ORION5X_PCIE_VIRT_BASE)
399dd0b194SLennert Buytenhek 
orion5x_pcie_id(u32 * dev,u32 * rev)409dd0b194SLennert Buytenhek void __init orion5x_pcie_id(u32 *dev, u32 *rev)
419dd0b194SLennert Buytenhek {
429dd0b194SLennert Buytenhek 	*dev = orion_pcie_dev_id(PCIE_BASE);
439dd0b194SLennert Buytenhek 	*rev = orion_pcie_rev(PCIE_BASE);
449dd0b194SLennert Buytenhek }
459dd0b194SLennert Buytenhek 
pcie_valid_config(int bus,int dev)469dd0b194SLennert Buytenhek static int pcie_valid_config(int bus, int dev)
479dd0b194SLennert Buytenhek {
489dd0b194SLennert Buytenhek 	/*
499dd0b194SLennert Buytenhek 	 * Don't go out when trying to access --
509dd0b194SLennert Buytenhek 	 * 1. nonexisting device on local bus
519dd0b194SLennert Buytenhek 	 * 2. where there's no device connected (no link)
529dd0b194SLennert Buytenhek 	 */
539dd0b194SLennert Buytenhek 	if (bus == 0 && dev == 0)
549dd0b194SLennert Buytenhek 		return 1;
559dd0b194SLennert Buytenhek 
569dd0b194SLennert Buytenhek 	if (!orion_pcie_link_up(PCIE_BASE))
579dd0b194SLennert Buytenhek 		return 0;
589dd0b194SLennert Buytenhek 
599dd0b194SLennert Buytenhek 	if (bus == 0 && dev != 1)
609dd0b194SLennert Buytenhek 		return 0;
619dd0b194SLennert Buytenhek 
629dd0b194SLennert Buytenhek 	return 1;
639dd0b194SLennert Buytenhek }
649dd0b194SLennert Buytenhek 
659dd0b194SLennert Buytenhek 
669dd0b194SLennert Buytenhek /*
679dd0b194SLennert Buytenhek  * PCIe config cycles are done by programming the PCIE_CONF_ADDR register
689dd0b194SLennert Buytenhek  * and then reading the PCIE_CONF_DATA register. Need to make sure these
699dd0b194SLennert Buytenhek  * transactions are atomic.
709dd0b194SLennert Buytenhek  */
719dd0b194SLennert Buytenhek static DEFINE_SPINLOCK(orion5x_pcie_lock);
729dd0b194SLennert Buytenhek 
pcie_rd_conf(struct pci_bus * bus,u32 devfn,int where,int size,u32 * val)739dd0b194SLennert Buytenhek static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
749dd0b194SLennert Buytenhek 			int size, u32 *val)
759dd0b194SLennert Buytenhek {
769dd0b194SLennert Buytenhek 	unsigned long flags;
779dd0b194SLennert Buytenhek 	int ret;
789dd0b194SLennert Buytenhek 
799dd0b194SLennert Buytenhek 	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) {
809dd0b194SLennert Buytenhek 		*val = 0xffffffff;
819dd0b194SLennert Buytenhek 		return PCIBIOS_DEVICE_NOT_FOUND;
829dd0b194SLennert Buytenhek 	}
839dd0b194SLennert Buytenhek 
849dd0b194SLennert Buytenhek 	spin_lock_irqsave(&orion5x_pcie_lock, flags);
859dd0b194SLennert Buytenhek 	ret = orion_pcie_rd_conf(PCIE_BASE, bus, devfn, where, size, val);
869dd0b194SLennert Buytenhek 	spin_unlock_irqrestore(&orion5x_pcie_lock, flags);
879dd0b194SLennert Buytenhek 
889dd0b194SLennert Buytenhek 	return ret;
899dd0b194SLennert Buytenhek }
909dd0b194SLennert Buytenhek 
pcie_rd_conf_wa(struct pci_bus * bus,u32 devfn,int where,int size,u32 * val)919dd0b194SLennert Buytenhek static int pcie_rd_conf_wa(struct pci_bus *bus, u32 devfn,
929dd0b194SLennert Buytenhek 			   int where, int size, u32 *val)
939dd0b194SLennert Buytenhek {
949dd0b194SLennert Buytenhek 	int ret;
959dd0b194SLennert Buytenhek 
969dd0b194SLennert Buytenhek 	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) {
979dd0b194SLennert Buytenhek 		*val = 0xffffffff;
989dd0b194SLennert Buytenhek 		return PCIBIOS_DEVICE_NOT_FOUND;
999dd0b194SLennert Buytenhek 	}
1009dd0b194SLennert Buytenhek 
1019dd0b194SLennert Buytenhek 	/*
1029dd0b194SLennert Buytenhek 	 * We only support access to the non-extended configuration
1039dd0b194SLennert Buytenhek 	 * space when using the WA access method (or we would have to
1049dd0b194SLennert Buytenhek 	 * sacrifice 256M of CPU virtual address space.)
1059dd0b194SLennert Buytenhek 	 */
1069dd0b194SLennert Buytenhek 	if (where >= 0x100) {
1079dd0b194SLennert Buytenhek 		*val = 0xffffffff;
1089dd0b194SLennert Buytenhek 		return PCIBIOS_DEVICE_NOT_FOUND;
1099dd0b194SLennert Buytenhek 	}
1109dd0b194SLennert Buytenhek 
1113904a393SThomas Petazzoni 	ret = orion_pcie_rd_conf_wa(ORION5X_PCIE_WA_VIRT_BASE,
1129dd0b194SLennert Buytenhek 				    bus, devfn, where, size, val);
1139dd0b194SLennert Buytenhek 
1149dd0b194SLennert Buytenhek 	return ret;
1159dd0b194SLennert Buytenhek }
1169dd0b194SLennert Buytenhek 
pcie_wr_conf(struct pci_bus * bus,u32 devfn,int where,int size,u32 val)1179dd0b194SLennert Buytenhek static int pcie_wr_conf(struct pci_bus *bus, u32 devfn,
1189dd0b194SLennert Buytenhek 			int where, int size, u32 val)
1199dd0b194SLennert Buytenhek {
1209dd0b194SLennert Buytenhek 	unsigned long flags;
1219dd0b194SLennert Buytenhek 	int ret;
1229dd0b194SLennert Buytenhek 
1239dd0b194SLennert Buytenhek 	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0)
1249dd0b194SLennert Buytenhek 		return PCIBIOS_DEVICE_NOT_FOUND;
1259dd0b194SLennert Buytenhek 
1269dd0b194SLennert Buytenhek 	spin_lock_irqsave(&orion5x_pcie_lock, flags);
1279dd0b194SLennert Buytenhek 	ret = orion_pcie_wr_conf(PCIE_BASE, bus, devfn, where, size, val);
1289dd0b194SLennert Buytenhek 	spin_unlock_irqrestore(&orion5x_pcie_lock, flags);
1299dd0b194SLennert Buytenhek 
1309dd0b194SLennert Buytenhek 	return ret;
1319dd0b194SLennert Buytenhek }
1329dd0b194SLennert Buytenhek 
1339dd0b194SLennert Buytenhek static struct pci_ops pcie_ops = {
1349dd0b194SLennert Buytenhek 	.read = pcie_rd_conf,
1359dd0b194SLennert Buytenhek 	.write = pcie_wr_conf,
1369dd0b194SLennert Buytenhek };
1379dd0b194SLennert Buytenhek 
1389dd0b194SLennert Buytenhek 
pcie_setup(struct pci_sys_data * sys)1399dd0b194SLennert Buytenhek static int __init pcie_setup(struct pci_sys_data *sys)
1409dd0b194SLennert Buytenhek {
1419dd0b194SLennert Buytenhek 	struct resource *res;
1426198461eSPali Rohár 	struct resource realio;
1439dd0b194SLennert Buytenhek 	int dev;
1449dd0b194SLennert Buytenhek 
1459dd0b194SLennert Buytenhek 	/*
1469dd0b194SLennert Buytenhek 	 * Generic PCIe unit setup.
1479dd0b194SLennert Buytenhek 	 */
14863a9332bSAndrew Lunn 	orion_pcie_setup(PCIE_BASE);
1499dd0b194SLennert Buytenhek 
1509dd0b194SLennert Buytenhek 	/*
1519dd0b194SLennert Buytenhek 	 * Check whether to apply Orion-1/Orion-NAS PCIe config
1529dd0b194SLennert Buytenhek 	 * read transaction workaround.
1539dd0b194SLennert Buytenhek 	 */
1549dd0b194SLennert Buytenhek 	dev = orion_pcie_dev_id(PCIE_BASE);
1559dd0b194SLennert Buytenhek 	if (dev == MV88F5181_DEV_ID || dev == MV88F5182_DEV_ID) {
1569dd0b194SLennert Buytenhek 		printk(KERN_NOTICE "Applying Orion-1/Orion-NAS PCIe config "
1579dd0b194SLennert Buytenhek 				   "read transaction workaround\n");
1584ca2c040SThomas Petazzoni 		mvebu_mbus_add_window_by_id(ORION_MBUS_PCIE_WA_TARGET,
1594ca2c040SThomas Petazzoni 					    ORION_MBUS_PCIE_WA_ATTR,
1605d1190eaSThomas Petazzoni 					    ORION5X_PCIE_WA_PHYS_BASE,
1614ca2c040SThomas Petazzoni 					    ORION5X_PCIE_WA_SIZE);
1629dd0b194SLennert Buytenhek 		pcie_ops.read = pcie_rd_conf_wa;
1639dd0b194SLennert Buytenhek 	}
1649dd0b194SLennert Buytenhek 
1656198461eSPali Rohár 	realio.start = sys->busnr * SZ_64K;
1666198461eSPali Rohár 	realio.end = realio.start + SZ_64K - 1;
1676198461eSPali Rohár 	pci_remap_iospace(&realio, ORION5X_PCIE_IO_PHYS_BASE);
1680a4b8c65SRob Herring 
1699dd0b194SLennert Buytenhek 	/*
1709dd0b194SLennert Buytenhek 	 * Request resources.
1719dd0b194SLennert Buytenhek 	 */
1720a4b8c65SRob Herring 	res = kzalloc(sizeof(struct resource), GFP_KERNEL);
1739dd0b194SLennert Buytenhek 	if (!res)
1749dd0b194SLennert Buytenhek 		panic("pcie_setup unable to alloc resources");
1759dd0b194SLennert Buytenhek 
1769dd0b194SLennert Buytenhek 	/*
1779dd0b194SLennert Buytenhek 	 * IORESOURCE_MEM
1789dd0b194SLennert Buytenhek 	 */
1790a4b8c65SRob Herring 	res->name = "PCIe Memory Space";
1800a4b8c65SRob Herring 	res->flags = IORESOURCE_MEM;
1810a4b8c65SRob Herring 	res->start = ORION5X_PCIE_MEM_PHYS_BASE;
1820a4b8c65SRob Herring 	res->end = res->start + ORION5X_PCIE_MEM_SIZE - 1;
1830a4b8c65SRob Herring 	if (request_resource(&iomem_resource, res))
1849dd0b194SLennert Buytenhek 		panic("Request PCIe Memory resource failed\n");
1850a4b8c65SRob Herring 	pci_add_resource_offset(&sys->resources, res, sys->mem_offset);
1869dd0b194SLennert Buytenhek 
1879dd0b194SLennert Buytenhek 	return 1;
1889dd0b194SLennert Buytenhek }
1899dd0b194SLennert Buytenhek 
1909dd0b194SLennert Buytenhek /*****************************************************************************
1919dd0b194SLennert Buytenhek  * PCI controller
1929dd0b194SLennert Buytenhek  ****************************************************************************/
1932332656aSThomas Petazzoni #define ORION5X_PCI_REG(x)	(ORION5X_PCI_VIRT_BASE + (x))
1949dd0b194SLennert Buytenhek #define PCI_MODE		ORION5X_PCI_REG(0xd00)
1959dd0b194SLennert Buytenhek #define PCI_CMD			ORION5X_PCI_REG(0xc00)
1969dd0b194SLennert Buytenhek #define PCI_P2P_CONF		ORION5X_PCI_REG(0x1d14)
1979dd0b194SLennert Buytenhek #define PCI_CONF_ADDR		ORION5X_PCI_REG(0xc78)
1989dd0b194SLennert Buytenhek #define PCI_CONF_DATA		ORION5X_PCI_REG(0xc7c)
1999dd0b194SLennert Buytenhek 
2009dd0b194SLennert Buytenhek /*
2019dd0b194SLennert Buytenhek  * PCI_MODE bits
2029dd0b194SLennert Buytenhek  */
2039dd0b194SLennert Buytenhek #define PCI_MODE_64BIT			(1 << 2)
2049dd0b194SLennert Buytenhek #define PCI_MODE_PCIX			((1 << 4) | (1 << 5))
2059dd0b194SLennert Buytenhek 
2069dd0b194SLennert Buytenhek /*
2079dd0b194SLennert Buytenhek  * PCI_CMD bits
2089dd0b194SLennert Buytenhek  */
2099dd0b194SLennert Buytenhek #define PCI_CMD_HOST_REORDER		(1 << 29)
2109dd0b194SLennert Buytenhek 
2119dd0b194SLennert Buytenhek /*
2129dd0b194SLennert Buytenhek  * PCI_P2P_CONF bits
2139dd0b194SLennert Buytenhek  */
2149dd0b194SLennert Buytenhek #define PCI_P2P_BUS_OFFS		16
2159dd0b194SLennert Buytenhek #define PCI_P2P_BUS_MASK		(0xff << PCI_P2P_BUS_OFFS)
2169dd0b194SLennert Buytenhek #define PCI_P2P_DEV_OFFS		24
2179dd0b194SLennert Buytenhek #define PCI_P2P_DEV_MASK		(0x1f << PCI_P2P_DEV_OFFS)
2189dd0b194SLennert Buytenhek 
2199dd0b194SLennert Buytenhek /*
2209dd0b194SLennert Buytenhek  * PCI_CONF_ADDR bits
2219dd0b194SLennert Buytenhek  */
2229dd0b194SLennert Buytenhek #define PCI_CONF_REG(reg)		((reg) & 0xfc)
2239dd0b194SLennert Buytenhek #define PCI_CONF_FUNC(func)		(((func) & 0x3) << 8)
2249dd0b194SLennert Buytenhek #define PCI_CONF_DEV(dev)		(((dev) & 0x1f) << 11)
2259dd0b194SLennert Buytenhek #define PCI_CONF_BUS(bus)		(((bus) & 0xff) << 16)
2269dd0b194SLennert Buytenhek #define PCI_CONF_ADDR_EN		(1 << 31)
2279dd0b194SLennert Buytenhek 
2289dd0b194SLennert Buytenhek /*
2299dd0b194SLennert Buytenhek  * Internal configuration space
2309dd0b194SLennert Buytenhek  */
2319dd0b194SLennert Buytenhek #define PCI_CONF_FUNC_STAT_CMD		0
2329dd0b194SLennert Buytenhek #define PCI_CONF_REG_STAT_CMD		4
2339dd0b194SLennert Buytenhek #define PCIX_STAT			0x64
2349dd0b194SLennert Buytenhek #define PCIX_STAT_BUS_OFFS		8
2359dd0b194SLennert Buytenhek #define PCIX_STAT_BUS_MASK		(0xff << PCIX_STAT_BUS_OFFS)
2369dd0b194SLennert Buytenhek 
2379dd0b194SLennert Buytenhek /*
2389dd0b194SLennert Buytenhek  * PCI Address Decode Windows registers
2399dd0b194SLennert Buytenhek  */
2409dd0b194SLennert Buytenhek #define PCI_BAR_SIZE_DDR_CS(n)	(((n) == 0) ? ORION5X_PCI_REG(0xc08) : \
2419dd0b194SLennert Buytenhek 				 ((n) == 1) ? ORION5X_PCI_REG(0xd08) : \
2429dd0b194SLennert Buytenhek 				 ((n) == 2) ? ORION5X_PCI_REG(0xc0c) : \
24342366666SAndrew Lunn 				 ((n) == 3) ? ORION5X_PCI_REG(0xd0c) : NULL)
2449dd0b194SLennert Buytenhek #define PCI_BAR_REMAP_DDR_CS(n)	(((n) == 0) ? ORION5X_PCI_REG(0xc48) : \
2459dd0b194SLennert Buytenhek 				 ((n) == 1) ? ORION5X_PCI_REG(0xd48) : \
2469dd0b194SLennert Buytenhek 				 ((n) == 2) ? ORION5X_PCI_REG(0xc4c) : \
24742366666SAndrew Lunn 				 ((n) == 3) ? ORION5X_PCI_REG(0xd4c) : NULL)
2489dd0b194SLennert Buytenhek #define PCI_BAR_ENABLE		ORION5X_PCI_REG(0xc3c)
2499dd0b194SLennert Buytenhek #define PCI_ADDR_DECODE_CTRL	ORION5X_PCI_REG(0xd3c)
2509dd0b194SLennert Buytenhek 
2519dd0b194SLennert Buytenhek /*
2529dd0b194SLennert Buytenhek  * PCI configuration helpers for BAR settings
2539dd0b194SLennert Buytenhek  */
2549dd0b194SLennert Buytenhek #define PCI_CONF_FUNC_BAR_CS(n)		((n) >> 1)
2559dd0b194SLennert Buytenhek #define PCI_CONF_REG_BAR_LO_CS(n)	(((n) & 1) ? 0x18 : 0x10)
2569dd0b194SLennert Buytenhek #define PCI_CONF_REG_BAR_HI_CS(n)	(((n) & 1) ? 0x1c : 0x14)
2579dd0b194SLennert Buytenhek 
2589dd0b194SLennert Buytenhek /*
2599dd0b194SLennert Buytenhek  * PCI config cycles are done by programming the PCI_CONF_ADDR register
2609dd0b194SLennert Buytenhek  * and then reading the PCI_CONF_DATA register. Need to make sure these
2619dd0b194SLennert Buytenhek  * transactions are atomic.
2629dd0b194SLennert Buytenhek  */
2639dd0b194SLennert Buytenhek static DEFINE_SPINLOCK(orion5x_pci_lock);
2649dd0b194SLennert Buytenhek 
265da01bba3SLennert Buytenhek static int orion5x_pci_cardbus_mode;
266da01bba3SLennert Buytenhek 
orion5x_pci_local_bus_nr(void)26792b913b0SLennert Buytenhek static int orion5x_pci_local_bus_nr(void)
2689dd0b194SLennert Buytenhek {
26979e90dd5SLennert Buytenhek 	u32 conf = readl(PCI_P2P_CONF);
2709dd0b194SLennert Buytenhek 	return((conf & PCI_P2P_BUS_MASK) >> PCI_P2P_BUS_OFFS);
2719dd0b194SLennert Buytenhek }
2729dd0b194SLennert Buytenhek 
orion5x_pci_hw_rd_conf(int bus,int dev,u32 func,u32 where,u32 size,u32 * val)2739dd0b194SLennert Buytenhek static int orion5x_pci_hw_rd_conf(int bus, int dev, u32 func,
2749dd0b194SLennert Buytenhek 					u32 where, u32 size, u32 *val)
2759dd0b194SLennert Buytenhek {
2769dd0b194SLennert Buytenhek 	unsigned long flags;
2779dd0b194SLennert Buytenhek 	spin_lock_irqsave(&orion5x_pci_lock, flags);
2789dd0b194SLennert Buytenhek 
27979e90dd5SLennert Buytenhek 	writel(PCI_CONF_BUS(bus) |
2809dd0b194SLennert Buytenhek 		PCI_CONF_DEV(dev) | PCI_CONF_REG(where) |
28179e90dd5SLennert Buytenhek 		PCI_CONF_FUNC(func) | PCI_CONF_ADDR_EN, PCI_CONF_ADDR);
2829dd0b194SLennert Buytenhek 
28379e90dd5SLennert Buytenhek 	*val = readl(PCI_CONF_DATA);
2849dd0b194SLennert Buytenhek 
2859dd0b194SLennert Buytenhek 	if (size == 1)
2869dd0b194SLennert Buytenhek 		*val = (*val >> (8*(where & 0x3))) & 0xff;
2879dd0b194SLennert Buytenhek 	else if (size == 2)
2889dd0b194SLennert Buytenhek 		*val = (*val >> (8*(where & 0x3))) & 0xffff;
2899dd0b194SLennert Buytenhek 
2909dd0b194SLennert Buytenhek 	spin_unlock_irqrestore(&orion5x_pci_lock, flags);
2919dd0b194SLennert Buytenhek 
2929dd0b194SLennert Buytenhek 	return PCIBIOS_SUCCESSFUL;
2939dd0b194SLennert Buytenhek }
2949dd0b194SLennert Buytenhek 
orion5x_pci_hw_wr_conf(int bus,int dev,u32 func,u32 where,u32 size,u32 val)2959dd0b194SLennert Buytenhek static int orion5x_pci_hw_wr_conf(int bus, int dev, u32 func,
2969dd0b194SLennert Buytenhek 					u32 where, u32 size, u32 val)
2979dd0b194SLennert Buytenhek {
2989dd0b194SLennert Buytenhek 	unsigned long flags;
2999dd0b194SLennert Buytenhek 	int ret = PCIBIOS_SUCCESSFUL;
3009dd0b194SLennert Buytenhek 
3019dd0b194SLennert Buytenhek 	spin_lock_irqsave(&orion5x_pci_lock, flags);
3029dd0b194SLennert Buytenhek 
30379e90dd5SLennert Buytenhek 	writel(PCI_CONF_BUS(bus) |
3049dd0b194SLennert Buytenhek 		PCI_CONF_DEV(dev) | PCI_CONF_REG(where) |
30579e90dd5SLennert Buytenhek 		PCI_CONF_FUNC(func) | PCI_CONF_ADDR_EN, PCI_CONF_ADDR);
3069dd0b194SLennert Buytenhek 
3079dd0b194SLennert Buytenhek 	if (size == 4) {
3089dd0b194SLennert Buytenhek 		__raw_writel(val, PCI_CONF_DATA);
3099dd0b194SLennert Buytenhek 	} else if (size == 2) {
3109dd0b194SLennert Buytenhek 		__raw_writew(val, PCI_CONF_DATA + (where & 0x3));
3119dd0b194SLennert Buytenhek 	} else if (size == 1) {
3129dd0b194SLennert Buytenhek 		__raw_writeb(val, PCI_CONF_DATA + (where & 0x3));
3139dd0b194SLennert Buytenhek 	} else {
3149dd0b194SLennert Buytenhek 		ret = PCIBIOS_BAD_REGISTER_NUMBER;
3159dd0b194SLennert Buytenhek 	}
3169dd0b194SLennert Buytenhek 
3179dd0b194SLennert Buytenhek 	spin_unlock_irqrestore(&orion5x_pci_lock, flags);
3189dd0b194SLennert Buytenhek 
3199dd0b194SLennert Buytenhek 	return ret;
3209dd0b194SLennert Buytenhek }
3219dd0b194SLennert Buytenhek 
orion5x_pci_valid_config(int bus,u32 devfn)322da01bba3SLennert Buytenhek static int orion5x_pci_valid_config(int bus, u32 devfn)
3239dd0b194SLennert Buytenhek {
324da01bba3SLennert Buytenhek 	if (bus == orion5x_pci_local_bus_nr()) {
3259dd0b194SLennert Buytenhek 		/*
3269dd0b194SLennert Buytenhek 		 * Don't go out for local device
3279dd0b194SLennert Buytenhek 		 */
328da01bba3SLennert Buytenhek 		if (PCI_SLOT(devfn) == 0 && PCI_FUNC(devfn) != 0)
329da01bba3SLennert Buytenhek 			return 0;
330da01bba3SLennert Buytenhek 
331da01bba3SLennert Buytenhek 		/*
332da01bba3SLennert Buytenhek 		 * When the PCI signals are directly connected to a
333da01bba3SLennert Buytenhek 		 * Cardbus slot, ignore all but device IDs 0 and 1.
334da01bba3SLennert Buytenhek 		 */
335da01bba3SLennert Buytenhek 		if (orion5x_pci_cardbus_mode && PCI_SLOT(devfn) > 1)
336da01bba3SLennert Buytenhek 			return 0;
337da01bba3SLennert Buytenhek 	}
338da01bba3SLennert Buytenhek 
339da01bba3SLennert Buytenhek 	return 1;
340da01bba3SLennert Buytenhek }
341da01bba3SLennert Buytenhek 
orion5x_pci_rd_conf(struct pci_bus * bus,u32 devfn,int where,int size,u32 * val)342da01bba3SLennert Buytenhek static int orion5x_pci_rd_conf(struct pci_bus *bus, u32 devfn,
343da01bba3SLennert Buytenhek 				int where, int size, u32 *val)
344da01bba3SLennert Buytenhek {
345da01bba3SLennert Buytenhek 	if (!orion5x_pci_valid_config(bus->number, devfn)) {
3469dd0b194SLennert Buytenhek 		*val = 0xffffffff;
3479dd0b194SLennert Buytenhek 		return PCIBIOS_DEVICE_NOT_FOUND;
3489dd0b194SLennert Buytenhek 	}
3499dd0b194SLennert Buytenhek 
3509dd0b194SLennert Buytenhek 	return orion5x_pci_hw_rd_conf(bus->number, PCI_SLOT(devfn),
3519dd0b194SLennert Buytenhek 					PCI_FUNC(devfn), where, size, val);
3529dd0b194SLennert Buytenhek }
3539dd0b194SLennert Buytenhek 
orion5x_pci_wr_conf(struct pci_bus * bus,u32 devfn,int where,int size,u32 val)3549dd0b194SLennert Buytenhek static int orion5x_pci_wr_conf(struct pci_bus *bus, u32 devfn,
3559dd0b194SLennert Buytenhek 				int where, int size, u32 val)
3569dd0b194SLennert Buytenhek {
357da01bba3SLennert Buytenhek 	if (!orion5x_pci_valid_config(bus->number, devfn))
3589dd0b194SLennert Buytenhek 		return PCIBIOS_DEVICE_NOT_FOUND;
3599dd0b194SLennert Buytenhek 
3609dd0b194SLennert Buytenhek 	return orion5x_pci_hw_wr_conf(bus->number, PCI_SLOT(devfn),
3619dd0b194SLennert Buytenhek 					PCI_FUNC(devfn), where, size, val);
3629dd0b194SLennert Buytenhek }
3639dd0b194SLennert Buytenhek 
3649dd0b194SLennert Buytenhek static struct pci_ops pci_ops = {
3659dd0b194SLennert Buytenhek 	.read = orion5x_pci_rd_conf,
3669dd0b194SLennert Buytenhek 	.write = orion5x_pci_wr_conf,
3679dd0b194SLennert Buytenhek };
3689dd0b194SLennert Buytenhek 
orion5x_pci_set_bus_nr(int nr)3699dd0b194SLennert Buytenhek static void __init orion5x_pci_set_bus_nr(int nr)
3709dd0b194SLennert Buytenhek {
37179e90dd5SLennert Buytenhek 	u32 p2p = readl(PCI_P2P_CONF);
3729dd0b194SLennert Buytenhek 
37379e90dd5SLennert Buytenhek 	if (readl(PCI_MODE) & PCI_MODE_PCIX) {
3749dd0b194SLennert Buytenhek 		/*
3759dd0b194SLennert Buytenhek 		 * PCI-X mode
3769dd0b194SLennert Buytenhek 		 */
3779dd0b194SLennert Buytenhek 		u32 pcix_status, bus, dev;
3789dd0b194SLennert Buytenhek 		bus = (p2p & PCI_P2P_BUS_MASK) >> PCI_P2P_BUS_OFFS;
3799dd0b194SLennert Buytenhek 		dev = (p2p & PCI_P2P_DEV_MASK) >> PCI_P2P_DEV_OFFS;
3809dd0b194SLennert Buytenhek 		orion5x_pci_hw_rd_conf(bus, dev, 0, PCIX_STAT, 4, &pcix_status);
3819dd0b194SLennert Buytenhek 		pcix_status &= ~PCIX_STAT_BUS_MASK;
3829dd0b194SLennert Buytenhek 		pcix_status |= (nr << PCIX_STAT_BUS_OFFS);
3839dd0b194SLennert Buytenhek 		orion5x_pci_hw_wr_conf(bus, dev, 0, PCIX_STAT, 4, pcix_status);
3849dd0b194SLennert Buytenhek 	} else {
3859dd0b194SLennert Buytenhek 		/*
3869dd0b194SLennert Buytenhek 		 * PCI Conventional mode
3879dd0b194SLennert Buytenhek 		 */
3889dd0b194SLennert Buytenhek 		p2p &= ~PCI_P2P_BUS_MASK;
3899dd0b194SLennert Buytenhek 		p2p |= (nr << PCI_P2P_BUS_OFFS);
39079e90dd5SLennert Buytenhek 		writel(p2p, PCI_P2P_CONF);
3919dd0b194SLennert Buytenhek 	}
3929dd0b194SLennert Buytenhek }
3939dd0b194SLennert Buytenhek 
orion5x_pci_master_slave_enable(void)3949dd0b194SLennert Buytenhek static void __init orion5x_pci_master_slave_enable(void)
3959dd0b194SLennert Buytenhek {
3969dd0b194SLennert Buytenhek 	int bus_nr, func, reg;
3979dd0b194SLennert Buytenhek 	u32 val;
3989dd0b194SLennert Buytenhek 
3999dd0b194SLennert Buytenhek 	bus_nr = orion5x_pci_local_bus_nr();
4009dd0b194SLennert Buytenhek 	func = PCI_CONF_FUNC_STAT_CMD;
4019dd0b194SLennert Buytenhek 	reg = PCI_CONF_REG_STAT_CMD;
4029dd0b194SLennert Buytenhek 	orion5x_pci_hw_rd_conf(bus_nr, 0, func, reg, 4, &val);
4039dd0b194SLennert Buytenhek 	val |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
4049dd0b194SLennert Buytenhek 	orion5x_pci_hw_wr_conf(bus_nr, 0, func, reg, 4, val | 0x7);
4059dd0b194SLennert Buytenhek }
4069dd0b194SLennert Buytenhek 
orion5x_setup_pci_wins(void)4073e762c86SThomas Petazzoni static void __init orion5x_setup_pci_wins(void)
4089dd0b194SLennert Buytenhek {
4093e762c86SThomas Petazzoni 	const struct mbus_dram_target_info *dram = mv_mbus_dram_info();
4109dd0b194SLennert Buytenhek 	u32 win_enable;
4119dd0b194SLennert Buytenhek 	int bus;
4129dd0b194SLennert Buytenhek 	int i;
4139dd0b194SLennert Buytenhek 
4149dd0b194SLennert Buytenhek 	/*
4159dd0b194SLennert Buytenhek 	 * First, disable windows.
4169dd0b194SLennert Buytenhek 	 */
4179dd0b194SLennert Buytenhek 	win_enable = 0xffffffff;
41879e90dd5SLennert Buytenhek 	writel(win_enable, PCI_BAR_ENABLE);
4199dd0b194SLennert Buytenhek 
4209dd0b194SLennert Buytenhek 	/*
4219dd0b194SLennert Buytenhek 	 * Setup windows for DDR banks.
4229dd0b194SLennert Buytenhek 	 */
4239dd0b194SLennert Buytenhek 	bus = orion5x_pci_local_bus_nr();
4249dd0b194SLennert Buytenhek 
4259dd0b194SLennert Buytenhek 	for (i = 0; i < dram->num_cs; i++) {
4263e762c86SThomas Petazzoni 		const struct mbus_dram_window *cs = dram->cs + i;
4279dd0b194SLennert Buytenhek 		u32 func = PCI_CONF_FUNC_BAR_CS(cs->cs_index);
4289dd0b194SLennert Buytenhek 		u32 reg;
4299dd0b194SLennert Buytenhek 		u32 val;
4309dd0b194SLennert Buytenhek 
4319dd0b194SLennert Buytenhek 		/*
4329dd0b194SLennert Buytenhek 		 * Write DRAM bank base address register.
4339dd0b194SLennert Buytenhek 		 */
4349dd0b194SLennert Buytenhek 		reg = PCI_CONF_REG_BAR_LO_CS(cs->cs_index);
4359dd0b194SLennert Buytenhek 		orion5x_pci_hw_rd_conf(bus, 0, func, reg, 4, &val);
4369dd0b194SLennert Buytenhek 		val = (cs->base & 0xfffff000) | (val & 0xfff);
4379dd0b194SLennert Buytenhek 		orion5x_pci_hw_wr_conf(bus, 0, func, reg, 4, val);
4389dd0b194SLennert Buytenhek 
4399dd0b194SLennert Buytenhek 		/*
4409dd0b194SLennert Buytenhek 		 * Write DRAM bank size register.
4419dd0b194SLennert Buytenhek 		 */
4429dd0b194SLennert Buytenhek 		reg = PCI_CONF_REG_BAR_HI_CS(cs->cs_index);
4439dd0b194SLennert Buytenhek 		orion5x_pci_hw_wr_conf(bus, 0, func, reg, 4, 0);
44479e90dd5SLennert Buytenhek 		writel((cs->size - 1) & 0xfffff000,
44579e90dd5SLennert Buytenhek 			PCI_BAR_SIZE_DDR_CS(cs->cs_index));
44679e90dd5SLennert Buytenhek 		writel(cs->base & 0xfffff000,
44779e90dd5SLennert Buytenhek 			PCI_BAR_REMAP_DDR_CS(cs->cs_index));
4489dd0b194SLennert Buytenhek 
4499dd0b194SLennert Buytenhek 		/*
4509dd0b194SLennert Buytenhek 		 * Enable decode window for this chip select.
4519dd0b194SLennert Buytenhek 		 */
4529dd0b194SLennert Buytenhek 		win_enable &= ~(1 << cs->cs_index);
4539dd0b194SLennert Buytenhek 	}
4549dd0b194SLennert Buytenhek 
4559dd0b194SLennert Buytenhek 	/*
4569dd0b194SLennert Buytenhek 	 * Re-enable decode windows.
4579dd0b194SLennert Buytenhek 	 */
45879e90dd5SLennert Buytenhek 	writel(win_enable, PCI_BAR_ENABLE);
4599dd0b194SLennert Buytenhek 
4609dd0b194SLennert Buytenhek 	/*
461af901ca1SAndré Goddard Rosa 	 * Disable automatic update of address remapping when writing to BARs.
4629dd0b194SLennert Buytenhek 	 */
4639dd0b194SLennert Buytenhek 	orion5x_setbits(PCI_ADDR_DECODE_CTRL, 1);
4649dd0b194SLennert Buytenhek }
4659dd0b194SLennert Buytenhek 
pci_setup(struct pci_sys_data * sys)4669dd0b194SLennert Buytenhek static int __init pci_setup(struct pci_sys_data *sys)
4679dd0b194SLennert Buytenhek {
4689dd0b194SLennert Buytenhek 	struct resource *res;
4696198461eSPali Rohár 	struct resource realio;
4709dd0b194SLennert Buytenhek 
4719dd0b194SLennert Buytenhek 	/*
4729dd0b194SLennert Buytenhek 	 * Point PCI unit MBUS decode windows to DRAM space.
4739dd0b194SLennert Buytenhek 	 */
4743e762c86SThomas Petazzoni 	orion5x_setup_pci_wins();
4759dd0b194SLennert Buytenhek 
4769dd0b194SLennert Buytenhek 	/*
4779dd0b194SLennert Buytenhek 	 * Master + Slave enable
4789dd0b194SLennert Buytenhek 	 */
4799dd0b194SLennert Buytenhek 	orion5x_pci_master_slave_enable();
4809dd0b194SLennert Buytenhek 
4819dd0b194SLennert Buytenhek 	/*
4829dd0b194SLennert Buytenhek 	 * Force ordering
4839dd0b194SLennert Buytenhek 	 */
4849dd0b194SLennert Buytenhek 	orion5x_setbits(PCI_CMD, PCI_CMD_HOST_REORDER);
4859dd0b194SLennert Buytenhek 
4866198461eSPali Rohár 	realio.start = sys->busnr * SZ_64K;
4876198461eSPali Rohár 	realio.end = realio.start + SZ_64K - 1;
4886198461eSPali Rohár 	pci_remap_iospace(&realio, ORION5X_PCI_IO_PHYS_BASE);
4890a4b8c65SRob Herring 
4909dd0b194SLennert Buytenhek 	/*
4919dd0b194SLennert Buytenhek 	 * Request resources
4929dd0b194SLennert Buytenhek 	 */
4930a4b8c65SRob Herring 	res = kzalloc(sizeof(struct resource), GFP_KERNEL);
4949dd0b194SLennert Buytenhek 	if (!res)
4959dd0b194SLennert Buytenhek 		panic("pci_setup unable to alloc resources");
4969dd0b194SLennert Buytenhek 
4979dd0b194SLennert Buytenhek 	/*
4989dd0b194SLennert Buytenhek 	 * IORESOURCE_MEM
4999dd0b194SLennert Buytenhek 	 */
5000a4b8c65SRob Herring 	res->name = "PCI Memory Space";
5010a4b8c65SRob Herring 	res->flags = IORESOURCE_MEM;
5020a4b8c65SRob Herring 	res->start = ORION5X_PCI_MEM_PHYS_BASE;
5030a4b8c65SRob Herring 	res->end = res->start + ORION5X_PCI_MEM_SIZE - 1;
5040a4b8c65SRob Herring 	if (request_resource(&iomem_resource, res))
5059dd0b194SLennert Buytenhek 		panic("Request PCI Memory resource failed\n");
5060a4b8c65SRob Herring 	pci_add_resource_offset(&sys->resources, res, sys->mem_offset);
5079dd0b194SLennert Buytenhek 
5089dd0b194SLennert Buytenhek 	return 1;
5099dd0b194SLennert Buytenhek }
5109dd0b194SLennert Buytenhek 
5119dd0b194SLennert Buytenhek 
5129dd0b194SLennert Buytenhek /*****************************************************************************
5139dd0b194SLennert Buytenhek  * General PCIe + PCI
5149dd0b194SLennert Buytenhek  ****************************************************************************/
515fdaa3725SPali Rohár 
516fdaa3725SPali Rohár /*
517fdaa3725SPali Rohár  * The root complex has a hardwired class of PCI_CLASS_MEMORY_OTHER, when it
518fdaa3725SPali Rohár  * is operating as a root complex this needs to be switched to
519fdaa3725SPali Rohár  * PCI_CLASS_BRIDGE_HOST or Linux will errantly try to process the BAR's on
520fdaa3725SPali Rohár  * the device. Decoding setup is handled by the orion code.
521fdaa3725SPali Rohár  */
rc_pci_fixup(struct pci_dev * dev)522351a102dSGreg Kroah-Hartman static void rc_pci_fixup(struct pci_dev *dev)
5239dd0b194SLennert Buytenhek {
5249dd0b194SLennert Buytenhek 	if (dev->bus->parent == NULL && dev->devfn == 0) {
525*09cc9006SMika Westerberg 		struct resource *r;
5269dd0b194SLennert Buytenhek 
527fdaa3725SPali Rohár 		dev->class &= 0xff;
528fdaa3725SPali Rohár 		dev->class |= PCI_CLASS_BRIDGE_HOST << 8;
529*09cc9006SMika Westerberg 		pci_dev_for_each_resource(dev, r) {
530*09cc9006SMika Westerberg 			r->start	= 0;
531*09cc9006SMika Westerberg 			r->end		= 0;
532*09cc9006SMika Westerberg 			r->flags	= 0;
5339dd0b194SLennert Buytenhek 		}
5349dd0b194SLennert Buytenhek 	}
5359dd0b194SLennert Buytenhek }
5369dd0b194SLennert Buytenhek DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup);
5379dd0b194SLennert Buytenhek 
5387a6bb262SPer Andersson static int orion5x_pci_disabled __initdata;
5397a6bb262SPer Andersson 
orion5x_pci_disable(void)5407a6bb262SPer Andersson void __init orion5x_pci_disable(void)
5417a6bb262SPer Andersson {
5427a6bb262SPer Andersson 	orion5x_pci_disabled = 1;
5437a6bb262SPer Andersson }
5447a6bb262SPer Andersson 
orion5x_pci_set_cardbus_mode(void)545da01bba3SLennert Buytenhek void __init orion5x_pci_set_cardbus_mode(void)
546da01bba3SLennert Buytenhek {
547da01bba3SLennert Buytenhek 	orion5x_pci_cardbus_mode = 1;
548da01bba3SLennert Buytenhek }
549da01bba3SLennert Buytenhek 
orion5x_pci_sys_setup(int nr,struct pci_sys_data * sys)5509dd0b194SLennert Buytenhek int __init orion5x_pci_sys_setup(int nr, struct pci_sys_data *sys)
5519dd0b194SLennert Buytenhek {
552cc22b4c1SRob Herring 	vga_base = ORION5X_PCIE_MEM_PHYS_BASE;
553cc22b4c1SRob Herring 
5549dd0b194SLennert Buytenhek 	if (nr == 0) {
5559dd0b194SLennert Buytenhek 		orion_pcie_set_local_bus_nr(PCIE_BASE, sys->busnr);
5569e808eb6SBjorn Helgaas 		return pcie_setup(sys);
5579dd0b194SLennert Buytenhek 	}
5589dd0b194SLennert Buytenhek 
5599e808eb6SBjorn Helgaas 	if (nr == 1 && !orion5x_pci_disabled) {
5609e808eb6SBjorn Helgaas 		orion5x_pci_set_bus_nr(sys->busnr);
5619e808eb6SBjorn Helgaas 		return pci_setup(sys);
5629e808eb6SBjorn Helgaas 	}
5639e808eb6SBjorn Helgaas 
5649e808eb6SBjorn Helgaas 	return 0;
5659dd0b194SLennert Buytenhek }
5669dd0b194SLennert Buytenhek 
orion5x_pci_sys_scan_bus(int nr,struct pci_host_bridge * bridge)56797ad2bdcSLorenzo Pieralisi int __init orion5x_pci_sys_scan_bus(int nr, struct pci_host_bridge *bridge)
5689dd0b194SLennert Buytenhek {
56997ad2bdcSLorenzo Pieralisi 	struct pci_sys_data *sys = pci_host_bridge_priv(bridge);
5709dd0b194SLennert Buytenhek 
57197ad2bdcSLorenzo Pieralisi 	list_splice_init(&sys->resources, &bridge->windows);
57297ad2bdcSLorenzo Pieralisi 	bridge->dev.parent = NULL;
57397ad2bdcSLorenzo Pieralisi 	bridge->sysdata = sys;
57497ad2bdcSLorenzo Pieralisi 	bridge->busnr = sys->busnr;
57597ad2bdcSLorenzo Pieralisi 
57697ad2bdcSLorenzo Pieralisi 	if (nr == 0) {
57797ad2bdcSLorenzo Pieralisi 		bridge->ops = &pcie_ops;
57897ad2bdcSLorenzo Pieralisi 		return pci_scan_root_bus_bridge(bridge);
57997ad2bdcSLorenzo Pieralisi 	}
58097ad2bdcSLorenzo Pieralisi 
58197ad2bdcSLorenzo Pieralisi 	if (nr == 1 && !orion5x_pci_disabled) {
58297ad2bdcSLorenzo Pieralisi 		bridge->ops = &pci_ops;
58397ad2bdcSLorenzo Pieralisi 		return pci_scan_root_bus_bridge(bridge);
58497ad2bdcSLorenzo Pieralisi 	}
5859e808eb6SBjorn Helgaas 
5869dd0b194SLennert Buytenhek 	BUG();
58797ad2bdcSLorenzo Pieralisi 	return -ENODEV;
5889dd0b194SLennert Buytenhek }
58992b913b0SLennert Buytenhek 
orion5x_pci_map_irq(const struct pci_dev * dev,u8 slot,u8 pin)590d5341942SRalf Baechle int __init orion5x_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
59192b913b0SLennert Buytenhek {
59292b913b0SLennert Buytenhek 	int bus = dev->bus->number;
59392b913b0SLennert Buytenhek 
59492b913b0SLennert Buytenhek 	/*
59592b913b0SLennert Buytenhek 	 * PCIe endpoint?
59692b913b0SLennert Buytenhek 	 */
5977a6bb262SPer Andersson 	if (orion5x_pci_disabled || bus < orion5x_pci_local_bus_nr())
59892b913b0SLennert Buytenhek 		return IRQ_ORION5X_PCIE0_INT;
59992b913b0SLennert Buytenhek 
60092b913b0SLennert Buytenhek 	return -1;
60192b913b0SLennert Buytenhek }
602