18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * arch/arm/mach-orion5x/pci.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * PCI and PCIe functions for Marvell Orion System On Chip
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Maintainer: Tzachi Perelstein <tzachi@marvell.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
98c2ecf20Sopenharmony_ci * License version 2.  This program is licensed "as is" without any
108c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/pci.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/mbus.h>
178c2ecf20Sopenharmony_ci#include <video/vga.h>
188c2ecf20Sopenharmony_ci#include <asm/irq.h>
198c2ecf20Sopenharmony_ci#include <asm/mach/pci.h>
208c2ecf20Sopenharmony_ci#include <plat/pcie.h>
218c2ecf20Sopenharmony_ci#include <plat/addr-map.h>
228c2ecf20Sopenharmony_ci#include "common.h"
238c2ecf20Sopenharmony_ci#include "orion5x.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*****************************************************************************
268c2ecf20Sopenharmony_ci * Orion has one PCIe controller and one PCI controller.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * Note1: The local PCIe bus number is '0'. The local PCI bus number
298c2ecf20Sopenharmony_ci * follows the scanned PCIe bridged busses, if any.
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Note2: It is possible for PCI/PCIe agents to access many subsystem's
328c2ecf20Sopenharmony_ci * space, by configuring BARs and Address Decode Windows, e.g. flashes on
338c2ecf20Sopenharmony_ci * device bus, Orion registers, etc. However this code only enable the
348c2ecf20Sopenharmony_ci * access to DDR banks.
358c2ecf20Sopenharmony_ci ****************************************************************************/
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*****************************************************************************
398c2ecf20Sopenharmony_ci * PCIe controller
408c2ecf20Sopenharmony_ci ****************************************************************************/
418c2ecf20Sopenharmony_ci#define PCIE_BASE	(ORION5X_PCIE_VIRT_BASE)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_civoid __init orion5x_pcie_id(u32 *dev, u32 *rev)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	*dev = orion_pcie_dev_id(PCIE_BASE);
468c2ecf20Sopenharmony_ci	*rev = orion_pcie_rev(PCIE_BASE);
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic int pcie_valid_config(int bus, int dev)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	/*
528c2ecf20Sopenharmony_ci	 * Don't go out when trying to access --
538c2ecf20Sopenharmony_ci	 * 1. nonexisting device on local bus
548c2ecf20Sopenharmony_ci	 * 2. where there's no device connected (no link)
558c2ecf20Sopenharmony_ci	 */
568c2ecf20Sopenharmony_ci	if (bus == 0 && dev == 0)
578c2ecf20Sopenharmony_ci		return 1;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (!orion_pcie_link_up(PCIE_BASE))
608c2ecf20Sopenharmony_ci		return 0;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (bus == 0 && dev != 1)
638c2ecf20Sopenharmony_ci		return 0;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return 1;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * PCIe config cycles are done by programming the PCIE_CONF_ADDR register
718c2ecf20Sopenharmony_ci * and then reading the PCIE_CONF_DATA register. Need to make sure these
728c2ecf20Sopenharmony_ci * transactions are atomic.
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(orion5x_pcie_lock);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
778c2ecf20Sopenharmony_ci			int size, u32 *val)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	unsigned long flags;
808c2ecf20Sopenharmony_ci	int ret;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) {
838c2ecf20Sopenharmony_ci		*val = 0xffffffff;
848c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	spin_lock_irqsave(&orion5x_pcie_lock, flags);
888c2ecf20Sopenharmony_ci	ret = orion_pcie_rd_conf(PCIE_BASE, bus, devfn, where, size, val);
898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&orion5x_pcie_lock, flags);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return ret;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic int pcie_rd_conf_wa(struct pci_bus *bus, u32 devfn,
958c2ecf20Sopenharmony_ci			   int where, int size, u32 *val)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	int ret;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) {
1008c2ecf20Sopenharmony_ci		*val = 0xffffffff;
1018c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	/*
1058c2ecf20Sopenharmony_ci	 * We only support access to the non-extended configuration
1068c2ecf20Sopenharmony_ci	 * space when using the WA access method (or we would have to
1078c2ecf20Sopenharmony_ci	 * sacrifice 256M of CPU virtual address space.)
1088c2ecf20Sopenharmony_ci	 */
1098c2ecf20Sopenharmony_ci	if (where >= 0x100) {
1108c2ecf20Sopenharmony_ci		*val = 0xffffffff;
1118c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	ret = orion_pcie_rd_conf_wa(ORION5X_PCIE_WA_VIRT_BASE,
1158c2ecf20Sopenharmony_ci				    bus, devfn, where, size, val);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return ret;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int pcie_wr_conf(struct pci_bus *bus, u32 devfn,
1218c2ecf20Sopenharmony_ci			int where, int size, u32 val)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	unsigned long flags;
1248c2ecf20Sopenharmony_ci	int ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0)
1278c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	spin_lock_irqsave(&orion5x_pcie_lock, flags);
1308c2ecf20Sopenharmony_ci	ret = orion_pcie_wr_conf(PCIE_BASE, bus, devfn, where, size, val);
1318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&orion5x_pcie_lock, flags);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return ret;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic struct pci_ops pcie_ops = {
1378c2ecf20Sopenharmony_ci	.read = pcie_rd_conf,
1388c2ecf20Sopenharmony_ci	.write = pcie_wr_conf,
1398c2ecf20Sopenharmony_ci};
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int __init pcie_setup(struct pci_sys_data *sys)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct resource *res;
1458c2ecf20Sopenharmony_ci	int dev;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	/*
1488c2ecf20Sopenharmony_ci	 * Generic PCIe unit setup.
1498c2ecf20Sopenharmony_ci	 */
1508c2ecf20Sopenharmony_ci	orion_pcie_setup(PCIE_BASE);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/*
1538c2ecf20Sopenharmony_ci	 * Check whether to apply Orion-1/Orion-NAS PCIe config
1548c2ecf20Sopenharmony_ci	 * read transaction workaround.
1558c2ecf20Sopenharmony_ci	 */
1568c2ecf20Sopenharmony_ci	dev = orion_pcie_dev_id(PCIE_BASE);
1578c2ecf20Sopenharmony_ci	if (dev == MV88F5181_DEV_ID || dev == MV88F5182_DEV_ID) {
1588c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "Applying Orion-1/Orion-NAS PCIe config "
1598c2ecf20Sopenharmony_ci				   "read transaction workaround\n");
1608c2ecf20Sopenharmony_ci		mvebu_mbus_add_window_by_id(ORION_MBUS_PCIE_WA_TARGET,
1618c2ecf20Sopenharmony_ci					    ORION_MBUS_PCIE_WA_ATTR,
1628c2ecf20Sopenharmony_ci					    ORION5X_PCIE_WA_PHYS_BASE,
1638c2ecf20Sopenharmony_ci					    ORION5X_PCIE_WA_SIZE);
1648c2ecf20Sopenharmony_ci		pcie_ops.read = pcie_rd_conf_wa;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	pci_ioremap_io(sys->busnr * SZ_64K, ORION5X_PCIE_IO_PHYS_BASE);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	/*
1708c2ecf20Sopenharmony_ci	 * Request resources.
1718c2ecf20Sopenharmony_ci	 */
1728c2ecf20Sopenharmony_ci	res = kzalloc(sizeof(struct resource), GFP_KERNEL);
1738c2ecf20Sopenharmony_ci	if (!res)
1748c2ecf20Sopenharmony_ci		panic("pcie_setup unable to alloc resources");
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/*
1778c2ecf20Sopenharmony_ci	 * IORESOURCE_MEM
1788c2ecf20Sopenharmony_ci	 */
1798c2ecf20Sopenharmony_ci	res->name = "PCIe Memory Space";
1808c2ecf20Sopenharmony_ci	res->flags = IORESOURCE_MEM;
1818c2ecf20Sopenharmony_ci	res->start = ORION5X_PCIE_MEM_PHYS_BASE;
1828c2ecf20Sopenharmony_ci	res->end = res->start + ORION5X_PCIE_MEM_SIZE - 1;
1838c2ecf20Sopenharmony_ci	if (request_resource(&iomem_resource, res))
1848c2ecf20Sopenharmony_ci		panic("Request PCIe Memory resource failed\n");
1858c2ecf20Sopenharmony_ci	pci_add_resource_offset(&sys->resources, res, sys->mem_offset);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return 1;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/*****************************************************************************
1918c2ecf20Sopenharmony_ci * PCI controller
1928c2ecf20Sopenharmony_ci ****************************************************************************/
1938c2ecf20Sopenharmony_ci#define ORION5X_PCI_REG(x)	(ORION5X_PCI_VIRT_BASE + (x))
1948c2ecf20Sopenharmony_ci#define PCI_MODE		ORION5X_PCI_REG(0xd00)
1958c2ecf20Sopenharmony_ci#define PCI_CMD			ORION5X_PCI_REG(0xc00)
1968c2ecf20Sopenharmony_ci#define PCI_P2P_CONF		ORION5X_PCI_REG(0x1d14)
1978c2ecf20Sopenharmony_ci#define PCI_CONF_ADDR		ORION5X_PCI_REG(0xc78)
1988c2ecf20Sopenharmony_ci#define PCI_CONF_DATA		ORION5X_PCI_REG(0xc7c)
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/*
2018c2ecf20Sopenharmony_ci * PCI_MODE bits
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_ci#define PCI_MODE_64BIT			(1 << 2)
2048c2ecf20Sopenharmony_ci#define PCI_MODE_PCIX			((1 << 4) | (1 << 5))
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/*
2078c2ecf20Sopenharmony_ci * PCI_CMD bits
2088c2ecf20Sopenharmony_ci */
2098c2ecf20Sopenharmony_ci#define PCI_CMD_HOST_REORDER		(1 << 29)
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci/*
2128c2ecf20Sopenharmony_ci * PCI_P2P_CONF bits
2138c2ecf20Sopenharmony_ci */
2148c2ecf20Sopenharmony_ci#define PCI_P2P_BUS_OFFS		16
2158c2ecf20Sopenharmony_ci#define PCI_P2P_BUS_MASK		(0xff << PCI_P2P_BUS_OFFS)
2168c2ecf20Sopenharmony_ci#define PCI_P2P_DEV_OFFS		24
2178c2ecf20Sopenharmony_ci#define PCI_P2P_DEV_MASK		(0x1f << PCI_P2P_DEV_OFFS)
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/*
2208c2ecf20Sopenharmony_ci * PCI_CONF_ADDR bits
2218c2ecf20Sopenharmony_ci */
2228c2ecf20Sopenharmony_ci#define PCI_CONF_REG(reg)		((reg) & 0xfc)
2238c2ecf20Sopenharmony_ci#define PCI_CONF_FUNC(func)		(((func) & 0x3) << 8)
2248c2ecf20Sopenharmony_ci#define PCI_CONF_DEV(dev)		(((dev) & 0x1f) << 11)
2258c2ecf20Sopenharmony_ci#define PCI_CONF_BUS(bus)		(((bus) & 0xff) << 16)
2268c2ecf20Sopenharmony_ci#define PCI_CONF_ADDR_EN		(1 << 31)
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/*
2298c2ecf20Sopenharmony_ci * Internal configuration space
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_ci#define PCI_CONF_FUNC_STAT_CMD		0
2328c2ecf20Sopenharmony_ci#define PCI_CONF_REG_STAT_CMD		4
2338c2ecf20Sopenharmony_ci#define PCIX_STAT			0x64
2348c2ecf20Sopenharmony_ci#define PCIX_STAT_BUS_OFFS		8
2358c2ecf20Sopenharmony_ci#define PCIX_STAT_BUS_MASK		(0xff << PCIX_STAT_BUS_OFFS)
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/*
2388c2ecf20Sopenharmony_ci * PCI Address Decode Windows registers
2398c2ecf20Sopenharmony_ci */
2408c2ecf20Sopenharmony_ci#define PCI_BAR_SIZE_DDR_CS(n)	(((n) == 0) ? ORION5X_PCI_REG(0xc08) : \
2418c2ecf20Sopenharmony_ci				 ((n) == 1) ? ORION5X_PCI_REG(0xd08) : \
2428c2ecf20Sopenharmony_ci				 ((n) == 2) ? ORION5X_PCI_REG(0xc0c) : \
2438c2ecf20Sopenharmony_ci				 ((n) == 3) ? ORION5X_PCI_REG(0xd0c) : NULL)
2448c2ecf20Sopenharmony_ci#define PCI_BAR_REMAP_DDR_CS(n)	(((n) == 0) ? ORION5X_PCI_REG(0xc48) : \
2458c2ecf20Sopenharmony_ci				 ((n) == 1) ? ORION5X_PCI_REG(0xd48) : \
2468c2ecf20Sopenharmony_ci				 ((n) == 2) ? ORION5X_PCI_REG(0xc4c) : \
2478c2ecf20Sopenharmony_ci				 ((n) == 3) ? ORION5X_PCI_REG(0xd4c) : NULL)
2488c2ecf20Sopenharmony_ci#define PCI_BAR_ENABLE		ORION5X_PCI_REG(0xc3c)
2498c2ecf20Sopenharmony_ci#define PCI_ADDR_DECODE_CTRL	ORION5X_PCI_REG(0xd3c)
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/*
2528c2ecf20Sopenharmony_ci * PCI configuration helpers for BAR settings
2538c2ecf20Sopenharmony_ci */
2548c2ecf20Sopenharmony_ci#define PCI_CONF_FUNC_BAR_CS(n)		((n) >> 1)
2558c2ecf20Sopenharmony_ci#define PCI_CONF_REG_BAR_LO_CS(n)	(((n) & 1) ? 0x18 : 0x10)
2568c2ecf20Sopenharmony_ci#define PCI_CONF_REG_BAR_HI_CS(n)	(((n) & 1) ? 0x1c : 0x14)
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci/*
2598c2ecf20Sopenharmony_ci * PCI config cycles are done by programming the PCI_CONF_ADDR register
2608c2ecf20Sopenharmony_ci * and then reading the PCI_CONF_DATA register. Need to make sure these
2618c2ecf20Sopenharmony_ci * transactions are atomic.
2628c2ecf20Sopenharmony_ci */
2638c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(orion5x_pci_lock);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic int orion5x_pci_cardbus_mode;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic int orion5x_pci_local_bus_nr(void)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	u32 conf = readl(PCI_P2P_CONF);
2708c2ecf20Sopenharmony_ci	return((conf & PCI_P2P_BUS_MASK) >> PCI_P2P_BUS_OFFS);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic int orion5x_pci_hw_rd_conf(int bus, int dev, u32 func,
2748c2ecf20Sopenharmony_ci					u32 where, u32 size, u32 *val)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	unsigned long flags;
2778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&orion5x_pci_lock, flags);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	writel(PCI_CONF_BUS(bus) |
2808c2ecf20Sopenharmony_ci		PCI_CONF_DEV(dev) | PCI_CONF_REG(where) |
2818c2ecf20Sopenharmony_ci		PCI_CONF_FUNC(func) | PCI_CONF_ADDR_EN, PCI_CONF_ADDR);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	*val = readl(PCI_CONF_DATA);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (size == 1)
2868c2ecf20Sopenharmony_ci		*val = (*val >> (8*(where & 0x3))) & 0xff;
2878c2ecf20Sopenharmony_ci	else if (size == 2)
2888c2ecf20Sopenharmony_ci		*val = (*val >> (8*(where & 0x3))) & 0xffff;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&orion5x_pci_lock, flags);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int orion5x_pci_hw_wr_conf(int bus, int dev, u32 func,
2968c2ecf20Sopenharmony_ci					u32 where, u32 size, u32 val)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	unsigned long flags;
2998c2ecf20Sopenharmony_ci	int ret = PCIBIOS_SUCCESSFUL;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	spin_lock_irqsave(&orion5x_pci_lock, flags);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	writel(PCI_CONF_BUS(bus) |
3048c2ecf20Sopenharmony_ci		PCI_CONF_DEV(dev) | PCI_CONF_REG(where) |
3058c2ecf20Sopenharmony_ci		PCI_CONF_FUNC(func) | PCI_CONF_ADDR_EN, PCI_CONF_ADDR);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (size == 4) {
3088c2ecf20Sopenharmony_ci		__raw_writel(val, PCI_CONF_DATA);
3098c2ecf20Sopenharmony_ci	} else if (size == 2) {
3108c2ecf20Sopenharmony_ci		__raw_writew(val, PCI_CONF_DATA + (where & 0x3));
3118c2ecf20Sopenharmony_ci	} else if (size == 1) {
3128c2ecf20Sopenharmony_ci		__raw_writeb(val, PCI_CONF_DATA + (where & 0x3));
3138c2ecf20Sopenharmony_ci	} else {
3148c2ecf20Sopenharmony_ci		ret = PCIBIOS_BAD_REGISTER_NUMBER;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&orion5x_pci_lock, flags);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	return ret;
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int orion5x_pci_valid_config(int bus, u32 devfn)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	if (bus == orion5x_pci_local_bus_nr()) {
3258c2ecf20Sopenharmony_ci		/*
3268c2ecf20Sopenharmony_ci		 * Don't go out for local device
3278c2ecf20Sopenharmony_ci		 */
3288c2ecf20Sopenharmony_ci		if (PCI_SLOT(devfn) == 0 && PCI_FUNC(devfn) != 0)
3298c2ecf20Sopenharmony_ci			return 0;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci		/*
3328c2ecf20Sopenharmony_ci		 * When the PCI signals are directly connected to a
3338c2ecf20Sopenharmony_ci		 * Cardbus slot, ignore all but device IDs 0 and 1.
3348c2ecf20Sopenharmony_ci		 */
3358c2ecf20Sopenharmony_ci		if (orion5x_pci_cardbus_mode && PCI_SLOT(devfn) > 1)
3368c2ecf20Sopenharmony_ci			return 0;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return 1;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic int orion5x_pci_rd_conf(struct pci_bus *bus, u32 devfn,
3438c2ecf20Sopenharmony_ci				int where, int size, u32 *val)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	if (!orion5x_pci_valid_config(bus->number, devfn)) {
3468c2ecf20Sopenharmony_ci		*val = 0xffffffff;
3478c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return orion5x_pci_hw_rd_conf(bus->number, PCI_SLOT(devfn),
3518c2ecf20Sopenharmony_ci					PCI_FUNC(devfn), where, size, val);
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int orion5x_pci_wr_conf(struct pci_bus *bus, u32 devfn,
3558c2ecf20Sopenharmony_ci				int where, int size, u32 val)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	if (!orion5x_pci_valid_config(bus->number, devfn))
3588c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return orion5x_pci_hw_wr_conf(bus->number, PCI_SLOT(devfn),
3618c2ecf20Sopenharmony_ci					PCI_FUNC(devfn), where, size, val);
3628c2ecf20Sopenharmony_ci}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_cistatic struct pci_ops pci_ops = {
3658c2ecf20Sopenharmony_ci	.read = orion5x_pci_rd_conf,
3668c2ecf20Sopenharmony_ci	.write = orion5x_pci_wr_conf,
3678c2ecf20Sopenharmony_ci};
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic void __init orion5x_pci_set_bus_nr(int nr)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	u32 p2p = readl(PCI_P2P_CONF);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (readl(PCI_MODE) & PCI_MODE_PCIX) {
3748c2ecf20Sopenharmony_ci		/*
3758c2ecf20Sopenharmony_ci		 * PCI-X mode
3768c2ecf20Sopenharmony_ci		 */
3778c2ecf20Sopenharmony_ci		u32 pcix_status, bus, dev;
3788c2ecf20Sopenharmony_ci		bus = (p2p & PCI_P2P_BUS_MASK) >> PCI_P2P_BUS_OFFS;
3798c2ecf20Sopenharmony_ci		dev = (p2p & PCI_P2P_DEV_MASK) >> PCI_P2P_DEV_OFFS;
3808c2ecf20Sopenharmony_ci		orion5x_pci_hw_rd_conf(bus, dev, 0, PCIX_STAT, 4, &pcix_status);
3818c2ecf20Sopenharmony_ci		pcix_status &= ~PCIX_STAT_BUS_MASK;
3828c2ecf20Sopenharmony_ci		pcix_status |= (nr << PCIX_STAT_BUS_OFFS);
3838c2ecf20Sopenharmony_ci		orion5x_pci_hw_wr_conf(bus, dev, 0, PCIX_STAT, 4, pcix_status);
3848c2ecf20Sopenharmony_ci	} else {
3858c2ecf20Sopenharmony_ci		/*
3868c2ecf20Sopenharmony_ci		 * PCI Conventional mode
3878c2ecf20Sopenharmony_ci		 */
3888c2ecf20Sopenharmony_ci		p2p &= ~PCI_P2P_BUS_MASK;
3898c2ecf20Sopenharmony_ci		p2p |= (nr << PCI_P2P_BUS_OFFS);
3908c2ecf20Sopenharmony_ci		writel(p2p, PCI_P2P_CONF);
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic void __init orion5x_pci_master_slave_enable(void)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	int bus_nr, func, reg;
3978c2ecf20Sopenharmony_ci	u32 val;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	bus_nr = orion5x_pci_local_bus_nr();
4008c2ecf20Sopenharmony_ci	func = PCI_CONF_FUNC_STAT_CMD;
4018c2ecf20Sopenharmony_ci	reg = PCI_CONF_REG_STAT_CMD;
4028c2ecf20Sopenharmony_ci	orion5x_pci_hw_rd_conf(bus_nr, 0, func, reg, 4, &val);
4038c2ecf20Sopenharmony_ci	val |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
4048c2ecf20Sopenharmony_ci	orion5x_pci_hw_wr_conf(bus_nr, 0, func, reg, 4, val | 0x7);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void __init orion5x_setup_pci_wins(void)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	const struct mbus_dram_target_info *dram = mv_mbus_dram_info();
4108c2ecf20Sopenharmony_ci	u32 win_enable;
4118c2ecf20Sopenharmony_ci	int bus;
4128c2ecf20Sopenharmony_ci	int i;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/*
4158c2ecf20Sopenharmony_ci	 * First, disable windows.
4168c2ecf20Sopenharmony_ci	 */
4178c2ecf20Sopenharmony_ci	win_enable = 0xffffffff;
4188c2ecf20Sopenharmony_ci	writel(win_enable, PCI_BAR_ENABLE);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/*
4218c2ecf20Sopenharmony_ci	 * Setup windows for DDR banks.
4228c2ecf20Sopenharmony_ci	 */
4238c2ecf20Sopenharmony_ci	bus = orion5x_pci_local_bus_nr();
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	for (i = 0; i < dram->num_cs; i++) {
4268c2ecf20Sopenharmony_ci		const struct mbus_dram_window *cs = dram->cs + i;
4278c2ecf20Sopenharmony_ci		u32 func = PCI_CONF_FUNC_BAR_CS(cs->cs_index);
4288c2ecf20Sopenharmony_ci		u32 reg;
4298c2ecf20Sopenharmony_ci		u32 val;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		/*
4328c2ecf20Sopenharmony_ci		 * Write DRAM bank base address register.
4338c2ecf20Sopenharmony_ci		 */
4348c2ecf20Sopenharmony_ci		reg = PCI_CONF_REG_BAR_LO_CS(cs->cs_index);
4358c2ecf20Sopenharmony_ci		orion5x_pci_hw_rd_conf(bus, 0, func, reg, 4, &val);
4368c2ecf20Sopenharmony_ci		val = (cs->base & 0xfffff000) | (val & 0xfff);
4378c2ecf20Sopenharmony_ci		orion5x_pci_hw_wr_conf(bus, 0, func, reg, 4, val);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		/*
4408c2ecf20Sopenharmony_ci		 * Write DRAM bank size register.
4418c2ecf20Sopenharmony_ci		 */
4428c2ecf20Sopenharmony_ci		reg = PCI_CONF_REG_BAR_HI_CS(cs->cs_index);
4438c2ecf20Sopenharmony_ci		orion5x_pci_hw_wr_conf(bus, 0, func, reg, 4, 0);
4448c2ecf20Sopenharmony_ci		writel((cs->size - 1) & 0xfffff000,
4458c2ecf20Sopenharmony_ci			PCI_BAR_SIZE_DDR_CS(cs->cs_index));
4468c2ecf20Sopenharmony_ci		writel(cs->base & 0xfffff000,
4478c2ecf20Sopenharmony_ci			PCI_BAR_REMAP_DDR_CS(cs->cs_index));
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci		/*
4508c2ecf20Sopenharmony_ci		 * Enable decode window for this chip select.
4518c2ecf20Sopenharmony_ci		 */
4528c2ecf20Sopenharmony_ci		win_enable &= ~(1 << cs->cs_index);
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	/*
4568c2ecf20Sopenharmony_ci	 * Re-enable decode windows.
4578c2ecf20Sopenharmony_ci	 */
4588c2ecf20Sopenharmony_ci	writel(win_enable, PCI_BAR_ENABLE);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/*
4618c2ecf20Sopenharmony_ci	 * Disable automatic update of address remapping when writing to BARs.
4628c2ecf20Sopenharmony_ci	 */
4638c2ecf20Sopenharmony_ci	orion5x_setbits(PCI_ADDR_DECODE_CTRL, 1);
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic int __init pci_setup(struct pci_sys_data *sys)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	struct resource *res;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	/*
4718c2ecf20Sopenharmony_ci	 * Point PCI unit MBUS decode windows to DRAM space.
4728c2ecf20Sopenharmony_ci	 */
4738c2ecf20Sopenharmony_ci	orion5x_setup_pci_wins();
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	/*
4768c2ecf20Sopenharmony_ci	 * Master + Slave enable
4778c2ecf20Sopenharmony_ci	 */
4788c2ecf20Sopenharmony_ci	orion5x_pci_master_slave_enable();
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	/*
4818c2ecf20Sopenharmony_ci	 * Force ordering
4828c2ecf20Sopenharmony_ci	 */
4838c2ecf20Sopenharmony_ci	orion5x_setbits(PCI_CMD, PCI_CMD_HOST_REORDER);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	pci_ioremap_io(sys->busnr * SZ_64K, ORION5X_PCI_IO_PHYS_BASE);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/*
4888c2ecf20Sopenharmony_ci	 * Request resources
4898c2ecf20Sopenharmony_ci	 */
4908c2ecf20Sopenharmony_ci	res = kzalloc(sizeof(struct resource), GFP_KERNEL);
4918c2ecf20Sopenharmony_ci	if (!res)
4928c2ecf20Sopenharmony_ci		panic("pci_setup unable to alloc resources");
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/*
4958c2ecf20Sopenharmony_ci	 * IORESOURCE_MEM
4968c2ecf20Sopenharmony_ci	 */
4978c2ecf20Sopenharmony_ci	res->name = "PCI Memory Space";
4988c2ecf20Sopenharmony_ci	res->flags = IORESOURCE_MEM;
4998c2ecf20Sopenharmony_ci	res->start = ORION5X_PCI_MEM_PHYS_BASE;
5008c2ecf20Sopenharmony_ci	res->end = res->start + ORION5X_PCI_MEM_SIZE - 1;
5018c2ecf20Sopenharmony_ci	if (request_resource(&iomem_resource, res))
5028c2ecf20Sopenharmony_ci		panic("Request PCI Memory resource failed\n");
5038c2ecf20Sopenharmony_ci	pci_add_resource_offset(&sys->resources, res, sys->mem_offset);
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	return 1;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci/*****************************************************************************
5108c2ecf20Sopenharmony_ci * General PCIe + PCI
5118c2ecf20Sopenharmony_ci ****************************************************************************/
5128c2ecf20Sopenharmony_cistatic void rc_pci_fixup(struct pci_dev *dev)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	/*
5158c2ecf20Sopenharmony_ci	 * Prevent enumeration of root complex.
5168c2ecf20Sopenharmony_ci	 */
5178c2ecf20Sopenharmony_ci	if (dev->bus->parent == NULL && dev->devfn == 0) {
5188c2ecf20Sopenharmony_ci		int i;
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
5218c2ecf20Sopenharmony_ci			dev->resource[i].start = 0;
5228c2ecf20Sopenharmony_ci			dev->resource[i].end   = 0;
5238c2ecf20Sopenharmony_ci			dev->resource[i].flags = 0;
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci	}
5268c2ecf20Sopenharmony_ci}
5278c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic int orion5x_pci_disabled __initdata;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_civoid __init orion5x_pci_disable(void)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	orion5x_pci_disabled = 1;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_civoid __init orion5x_pci_set_cardbus_mode(void)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	orion5x_pci_cardbus_mode = 1;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ciint __init orion5x_pci_sys_setup(int nr, struct pci_sys_data *sys)
5428c2ecf20Sopenharmony_ci{
5438c2ecf20Sopenharmony_ci	vga_base = ORION5X_PCIE_MEM_PHYS_BASE;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	if (nr == 0) {
5468c2ecf20Sopenharmony_ci		orion_pcie_set_local_bus_nr(PCIE_BASE, sys->busnr);
5478c2ecf20Sopenharmony_ci		return pcie_setup(sys);
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if (nr == 1 && !orion5x_pci_disabled) {
5518c2ecf20Sopenharmony_ci		orion5x_pci_set_bus_nr(sys->busnr);
5528c2ecf20Sopenharmony_ci		return pci_setup(sys);
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	return 0;
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ciint __init orion5x_pci_sys_scan_bus(int nr, struct pci_host_bridge *bridge)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	struct pci_sys_data *sys = pci_host_bridge_priv(bridge);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	list_splice_init(&sys->resources, &bridge->windows);
5638c2ecf20Sopenharmony_ci	bridge->dev.parent = NULL;
5648c2ecf20Sopenharmony_ci	bridge->sysdata = sys;
5658c2ecf20Sopenharmony_ci	bridge->busnr = sys->busnr;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if (nr == 0) {
5688c2ecf20Sopenharmony_ci		bridge->ops = &pcie_ops;
5698c2ecf20Sopenharmony_ci		return pci_scan_root_bus_bridge(bridge);
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	if (nr == 1 && !orion5x_pci_disabled) {
5738c2ecf20Sopenharmony_ci		bridge->ops = &pci_ops;
5748c2ecf20Sopenharmony_ci		return pci_scan_root_bus_bridge(bridge);
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	BUG();
5788c2ecf20Sopenharmony_ci	return -ENODEV;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ciint __init orion5x_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	int bus = dev->bus->number;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	/*
5868c2ecf20Sopenharmony_ci	 * PCIe endpoint?
5878c2ecf20Sopenharmony_ci	 */
5888c2ecf20Sopenharmony_ci	if (orion5x_pci_disabled || bus < orion5x_pci_local_bus_nr())
5898c2ecf20Sopenharmony_ci		return IRQ_ORION5X_PCIE0_INT;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	return -1;
5928c2ecf20Sopenharmony_ci}
593