18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PCIe driver for Marvell Armada 370 and Armada XP SoCs
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/pci.h>
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/delay.h>
128c2ecf20Sopenharmony_ci#include <linux/gpio.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/mbus.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/of_address.h>
188c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
198c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
208c2ecf20Sopenharmony_ci#include <linux/of_pci.h>
218c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "../pci.h"
248c2ecf20Sopenharmony_ci#include "../pci-bridge-emul.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * PCIe unit register offsets.
288c2ecf20Sopenharmony_ci */
298c2ecf20Sopenharmony_ci#define PCIE_DEV_ID_OFF		0x0000
308c2ecf20Sopenharmony_ci#define PCIE_CMD_OFF		0x0004
318c2ecf20Sopenharmony_ci#define PCIE_DEV_REV_OFF	0x0008
328c2ecf20Sopenharmony_ci#define PCIE_BAR_LO_OFF(n)	(0x0010 + ((n) << 3))
338c2ecf20Sopenharmony_ci#define PCIE_BAR_HI_OFF(n)	(0x0014 + ((n) << 3))
348c2ecf20Sopenharmony_ci#define PCIE_CAP_PCIEXP		0x0060
358c2ecf20Sopenharmony_ci#define PCIE_HEADER_LOG_4_OFF	0x0128
368c2ecf20Sopenharmony_ci#define PCIE_BAR_CTRL_OFF(n)	(0x1804 + (((n) - 1) * 4))
378c2ecf20Sopenharmony_ci#define PCIE_WIN04_CTRL_OFF(n)	(0x1820 + ((n) << 4))
388c2ecf20Sopenharmony_ci#define PCIE_WIN04_BASE_OFF(n)	(0x1824 + ((n) << 4))
398c2ecf20Sopenharmony_ci#define PCIE_WIN04_REMAP_OFF(n)	(0x182c + ((n) << 4))
408c2ecf20Sopenharmony_ci#define PCIE_WIN5_CTRL_OFF	0x1880
418c2ecf20Sopenharmony_ci#define PCIE_WIN5_BASE_OFF	0x1884
428c2ecf20Sopenharmony_ci#define PCIE_WIN5_REMAP_OFF	0x188c
438c2ecf20Sopenharmony_ci#define PCIE_CONF_ADDR_OFF	0x18f8
448c2ecf20Sopenharmony_ci#define  PCIE_CONF_ADDR_EN		0x80000000
458c2ecf20Sopenharmony_ci#define  PCIE_CONF_REG(r)		((((r) & 0xf00) << 16) | ((r) & 0xfc))
468c2ecf20Sopenharmony_ci#define  PCIE_CONF_BUS(b)		(((b) & 0xff) << 16)
478c2ecf20Sopenharmony_ci#define  PCIE_CONF_DEV(d)		(((d) & 0x1f) << 11)
488c2ecf20Sopenharmony_ci#define  PCIE_CONF_FUNC(f)		(((f) & 0x7) << 8)
498c2ecf20Sopenharmony_ci#define  PCIE_CONF_ADDR(bus, devfn, where) \
508c2ecf20Sopenharmony_ci	(PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn))    | \
518c2ecf20Sopenharmony_ci	 PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where) | \
528c2ecf20Sopenharmony_ci	 PCIE_CONF_ADDR_EN)
538c2ecf20Sopenharmony_ci#define PCIE_CONF_DATA_OFF	0x18fc
548c2ecf20Sopenharmony_ci#define PCIE_MASK_OFF		0x1910
558c2ecf20Sopenharmony_ci#define  PCIE_MASK_ENABLE_INTS          0x0f000000
568c2ecf20Sopenharmony_ci#define PCIE_CTRL_OFF		0x1a00
578c2ecf20Sopenharmony_ci#define  PCIE_CTRL_X1_MODE		0x0001
588c2ecf20Sopenharmony_ci#define PCIE_STAT_OFF		0x1a04
598c2ecf20Sopenharmony_ci#define  PCIE_STAT_BUS                  0xff00
608c2ecf20Sopenharmony_ci#define  PCIE_STAT_DEV                  0x1f0000
618c2ecf20Sopenharmony_ci#define  PCIE_STAT_LINK_DOWN		BIT(0)
628c2ecf20Sopenharmony_ci#define PCIE_RC_RTSTA		0x1a14
638c2ecf20Sopenharmony_ci#define PCIE_DEBUG_CTRL         0x1a60
648c2ecf20Sopenharmony_ci#define  PCIE_DEBUG_SOFT_RESET		BIT(20)
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistruct mvebu_pcie_port;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Structure representing all PCIe interfaces */
698c2ecf20Sopenharmony_cistruct mvebu_pcie {
708c2ecf20Sopenharmony_ci	struct platform_device *pdev;
718c2ecf20Sopenharmony_ci	struct mvebu_pcie_port *ports;
728c2ecf20Sopenharmony_ci	struct resource io;
738c2ecf20Sopenharmony_ci	struct resource realio;
748c2ecf20Sopenharmony_ci	struct resource mem;
758c2ecf20Sopenharmony_ci	struct resource busn;
768c2ecf20Sopenharmony_ci	int nports;
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistruct mvebu_pcie_window {
808c2ecf20Sopenharmony_ci	phys_addr_t base;
818c2ecf20Sopenharmony_ci	phys_addr_t remap;
828c2ecf20Sopenharmony_ci	size_t size;
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* Structure representing one PCIe interface */
868c2ecf20Sopenharmony_cistruct mvebu_pcie_port {
878c2ecf20Sopenharmony_ci	char *name;
888c2ecf20Sopenharmony_ci	void __iomem *base;
898c2ecf20Sopenharmony_ci	u32 port;
908c2ecf20Sopenharmony_ci	u32 lane;
918c2ecf20Sopenharmony_ci	int devfn;
928c2ecf20Sopenharmony_ci	unsigned int mem_target;
938c2ecf20Sopenharmony_ci	unsigned int mem_attr;
948c2ecf20Sopenharmony_ci	unsigned int io_target;
958c2ecf20Sopenharmony_ci	unsigned int io_attr;
968c2ecf20Sopenharmony_ci	struct clk *clk;
978c2ecf20Sopenharmony_ci	struct gpio_desc *reset_gpio;
988c2ecf20Sopenharmony_ci	char *reset_name;
998c2ecf20Sopenharmony_ci	struct pci_bridge_emul bridge;
1008c2ecf20Sopenharmony_ci	struct device_node *dn;
1018c2ecf20Sopenharmony_ci	struct mvebu_pcie *pcie;
1028c2ecf20Sopenharmony_ci	struct mvebu_pcie_window memwin;
1038c2ecf20Sopenharmony_ci	struct mvebu_pcie_window iowin;
1048c2ecf20Sopenharmony_ci	u32 saved_pcie_stat;
1058c2ecf20Sopenharmony_ci	struct resource regs;
1068c2ecf20Sopenharmony_ci};
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic inline void mvebu_writel(struct mvebu_pcie_port *port, u32 val, u32 reg)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	writel(val, port->base + reg);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic inline u32 mvebu_readl(struct mvebu_pcie_port *port, u32 reg)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	return readl(port->base + reg);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic inline bool mvebu_has_ioport(struct mvebu_pcie_port *port)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	return port->io_target != -1 && port->io_attr != -1;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic bool mvebu_pcie_link_up(struct mvebu_pcie_port *port)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	return !(mvebu_readl(port, PCIE_STAT_OFF) & PCIE_STAT_LINK_DOWN);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie_port *port, int nr)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	u32 stat;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	stat = mvebu_readl(port, PCIE_STAT_OFF);
1338c2ecf20Sopenharmony_ci	stat &= ~PCIE_STAT_BUS;
1348c2ecf20Sopenharmony_ci	stat |= nr << 8;
1358c2ecf20Sopenharmony_ci	mvebu_writel(port, stat, PCIE_STAT_OFF);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie_port *port, int nr)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	u32 stat;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	stat = mvebu_readl(port, PCIE_STAT_OFF);
1438c2ecf20Sopenharmony_ci	stat &= ~PCIE_STAT_DEV;
1448c2ecf20Sopenharmony_ci	stat |= nr << 16;
1458c2ecf20Sopenharmony_ci	mvebu_writel(port, stat, PCIE_STAT_OFF);
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/*
1498c2ecf20Sopenharmony_ci * Setup PCIE BARs and Address Decode Wins:
1508c2ecf20Sopenharmony_ci * BAR[0] -> internal registers (needed for MSI)
1518c2ecf20Sopenharmony_ci * BAR[1] -> covers all DRAM banks
1528c2ecf20Sopenharmony_ci * BAR[2] -> Disabled
1538c2ecf20Sopenharmony_ci * WIN[0-3] -> DRAM bank[0-3]
1548c2ecf20Sopenharmony_ci */
1558c2ecf20Sopenharmony_cistatic void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	const struct mbus_dram_target_info *dram;
1588c2ecf20Sopenharmony_ci	u32 size;
1598c2ecf20Sopenharmony_ci	int i;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	dram = mv_mbus_dram_info();
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	/* First, disable and clear BARs and windows. */
1648c2ecf20Sopenharmony_ci	for (i = 1; i < 3; i++) {
1658c2ecf20Sopenharmony_ci		mvebu_writel(port, 0, PCIE_BAR_CTRL_OFF(i));
1668c2ecf20Sopenharmony_ci		mvebu_writel(port, 0, PCIE_BAR_LO_OFF(i));
1678c2ecf20Sopenharmony_ci		mvebu_writel(port, 0, PCIE_BAR_HI_OFF(i));
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++) {
1718c2ecf20Sopenharmony_ci		mvebu_writel(port, 0, PCIE_WIN04_CTRL_OFF(i));
1728c2ecf20Sopenharmony_ci		mvebu_writel(port, 0, PCIE_WIN04_BASE_OFF(i));
1738c2ecf20Sopenharmony_ci		mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i));
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	mvebu_writel(port, 0, PCIE_WIN5_CTRL_OFF);
1778c2ecf20Sopenharmony_ci	mvebu_writel(port, 0, PCIE_WIN5_BASE_OFF);
1788c2ecf20Sopenharmony_ci	mvebu_writel(port, 0, PCIE_WIN5_REMAP_OFF);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* Setup windows for DDR banks.  Count total DDR size on the fly. */
1818c2ecf20Sopenharmony_ci	size = 0;
1828c2ecf20Sopenharmony_ci	for (i = 0; i < dram->num_cs; i++) {
1838c2ecf20Sopenharmony_ci		const struct mbus_dram_window *cs = dram->cs + i;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		mvebu_writel(port, cs->base & 0xffff0000,
1868c2ecf20Sopenharmony_ci			     PCIE_WIN04_BASE_OFF(i));
1878c2ecf20Sopenharmony_ci		mvebu_writel(port, 0, PCIE_WIN04_REMAP_OFF(i));
1888c2ecf20Sopenharmony_ci		mvebu_writel(port,
1898c2ecf20Sopenharmony_ci			     ((cs->size - 1) & 0xffff0000) |
1908c2ecf20Sopenharmony_ci			     (cs->mbus_attr << 8) |
1918c2ecf20Sopenharmony_ci			     (dram->mbus_dram_target_id << 4) | 1,
1928c2ecf20Sopenharmony_ci			     PCIE_WIN04_CTRL_OFF(i));
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci		size += cs->size;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* Round up 'size' to the nearest power of two. */
1988c2ecf20Sopenharmony_ci	if ((size & (size - 1)) != 0)
1998c2ecf20Sopenharmony_ci		size = 1 << fls(size);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* Setup BAR[1] to all DRAM banks. */
2028c2ecf20Sopenharmony_ci	mvebu_writel(port, dram->cs[0].base, PCIE_BAR_LO_OFF(1));
2038c2ecf20Sopenharmony_ci	mvebu_writel(port, 0, PCIE_BAR_HI_OFF(1));
2048c2ecf20Sopenharmony_ci	mvebu_writel(port, ((size - 1) & 0xffff0000) | 1,
2058c2ecf20Sopenharmony_ci		     PCIE_BAR_CTRL_OFF(1));
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/*
2088c2ecf20Sopenharmony_ci	 * Point BAR[0] to the device's internal registers.
2098c2ecf20Sopenharmony_ci	 */
2108c2ecf20Sopenharmony_ci	mvebu_writel(port, round_down(port->regs.start, SZ_1M), PCIE_BAR_LO_OFF(0));
2118c2ecf20Sopenharmony_ci	mvebu_writel(port, 0, PCIE_BAR_HI_OFF(0));
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	u32 cmd, mask;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* Point PCIe unit MBUS decode windows to DRAM space. */
2198c2ecf20Sopenharmony_ci	mvebu_pcie_setup_wins(port);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* Master + slave enable. */
2228c2ecf20Sopenharmony_ci	cmd = mvebu_readl(port, PCIE_CMD_OFF);
2238c2ecf20Sopenharmony_ci	cmd |= PCI_COMMAND_IO;
2248c2ecf20Sopenharmony_ci	cmd |= PCI_COMMAND_MEMORY;
2258c2ecf20Sopenharmony_ci	cmd |= PCI_COMMAND_MASTER;
2268c2ecf20Sopenharmony_ci	mvebu_writel(port, cmd, PCIE_CMD_OFF);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* Enable interrupt lines A-D. */
2298c2ecf20Sopenharmony_ci	mask = mvebu_readl(port, PCIE_MASK_OFF);
2308c2ecf20Sopenharmony_ci	mask |= PCIE_MASK_ENABLE_INTS;
2318c2ecf20Sopenharmony_ci	mvebu_writel(port, mask, PCIE_MASK_OFF);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int mvebu_pcie_hw_rd_conf(struct mvebu_pcie_port *port,
2358c2ecf20Sopenharmony_ci				 struct pci_bus *bus,
2368c2ecf20Sopenharmony_ci				 u32 devfn, int where, int size, u32 *val)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
2418c2ecf20Sopenharmony_ci		     PCIE_CONF_ADDR_OFF);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	switch (size) {
2448c2ecf20Sopenharmony_ci	case 1:
2458c2ecf20Sopenharmony_ci		*val = readb_relaxed(conf_data + (where & 3));
2468c2ecf20Sopenharmony_ci		break;
2478c2ecf20Sopenharmony_ci	case 2:
2488c2ecf20Sopenharmony_ci		*val = readw_relaxed(conf_data + (where & 2));
2498c2ecf20Sopenharmony_ci		break;
2508c2ecf20Sopenharmony_ci	case 4:
2518c2ecf20Sopenharmony_ci		*val = readl_relaxed(conf_data);
2528c2ecf20Sopenharmony_ci		break;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int mvebu_pcie_hw_wr_conf(struct mvebu_pcie_port *port,
2598c2ecf20Sopenharmony_ci				 struct pci_bus *bus,
2608c2ecf20Sopenharmony_ci				 u32 devfn, int where, int size, u32 val)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	void __iomem *conf_data = port->base + PCIE_CONF_DATA_OFF;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	mvebu_writel(port, PCIE_CONF_ADDR(bus->number, devfn, where),
2658c2ecf20Sopenharmony_ci		     PCIE_CONF_ADDR_OFF);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	switch (size) {
2688c2ecf20Sopenharmony_ci	case 1:
2698c2ecf20Sopenharmony_ci		writeb(val, conf_data + (where & 3));
2708c2ecf20Sopenharmony_ci		break;
2718c2ecf20Sopenharmony_ci	case 2:
2728c2ecf20Sopenharmony_ci		writew(val, conf_data + (where & 2));
2738c2ecf20Sopenharmony_ci		break;
2748c2ecf20Sopenharmony_ci	case 4:
2758c2ecf20Sopenharmony_ci		writel(val, conf_data);
2768c2ecf20Sopenharmony_ci		break;
2778c2ecf20Sopenharmony_ci	default:
2788c2ecf20Sopenharmony_ci		return PCIBIOS_BAD_REGISTER_NUMBER;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/*
2858c2ecf20Sopenharmony_ci * Remove windows, starting from the largest ones to the smallest
2868c2ecf20Sopenharmony_ci * ones.
2878c2ecf20Sopenharmony_ci */
2888c2ecf20Sopenharmony_cistatic void mvebu_pcie_del_windows(struct mvebu_pcie_port *port,
2898c2ecf20Sopenharmony_ci				   phys_addr_t base, size_t size)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	while (size) {
2928c2ecf20Sopenharmony_ci		size_t sz = 1 << (fls(size) - 1);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci		mvebu_mbus_del_window(base, sz);
2958c2ecf20Sopenharmony_ci		base += sz;
2968c2ecf20Sopenharmony_ci		size -= sz;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci/*
3018c2ecf20Sopenharmony_ci * MBus windows can only have a power of two size, but PCI BARs do not
3028c2ecf20Sopenharmony_ci * have this constraint. Therefore, we have to split the PCI BAR into
3038c2ecf20Sopenharmony_ci * areas each having a power of two size. We start from the largest
3048c2ecf20Sopenharmony_ci * one (i.e highest order bit set in the size).
3058c2ecf20Sopenharmony_ci */
3068c2ecf20Sopenharmony_cistatic void mvebu_pcie_add_windows(struct mvebu_pcie_port *port,
3078c2ecf20Sopenharmony_ci				   unsigned int target, unsigned int attribute,
3088c2ecf20Sopenharmony_ci				   phys_addr_t base, size_t size,
3098c2ecf20Sopenharmony_ci				   phys_addr_t remap)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	size_t size_mapped = 0;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	while (size) {
3148c2ecf20Sopenharmony_ci		size_t sz = 1 << (fls(size) - 1);
3158c2ecf20Sopenharmony_ci		int ret;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		ret = mvebu_mbus_add_window_remap_by_id(target, attribute, base,
3188c2ecf20Sopenharmony_ci							sz, remap);
3198c2ecf20Sopenharmony_ci		if (ret) {
3208c2ecf20Sopenharmony_ci			phys_addr_t end = base + sz - 1;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci			dev_err(&port->pcie->pdev->dev,
3238c2ecf20Sopenharmony_ci				"Could not create MBus window at [mem %pa-%pa]: %d\n",
3248c2ecf20Sopenharmony_ci				&base, &end, ret);
3258c2ecf20Sopenharmony_ci			mvebu_pcie_del_windows(port, base - size_mapped,
3268c2ecf20Sopenharmony_ci					       size_mapped);
3278c2ecf20Sopenharmony_ci			return;
3288c2ecf20Sopenharmony_ci		}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		size -= sz;
3318c2ecf20Sopenharmony_ci		size_mapped += sz;
3328c2ecf20Sopenharmony_ci		base += sz;
3338c2ecf20Sopenharmony_ci		if (remap != MVEBU_MBUS_NO_REMAP)
3348c2ecf20Sopenharmony_ci			remap += sz;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
3398c2ecf20Sopenharmony_ci				  unsigned int target, unsigned int attribute,
3408c2ecf20Sopenharmony_ci				  const struct mvebu_pcie_window *desired,
3418c2ecf20Sopenharmony_ci				  struct mvebu_pcie_window *cur)
3428c2ecf20Sopenharmony_ci{
3438c2ecf20Sopenharmony_ci	if (desired->base == cur->base && desired->remap == cur->remap &&
3448c2ecf20Sopenharmony_ci	    desired->size == cur->size)
3458c2ecf20Sopenharmony_ci		return;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (cur->size != 0) {
3488c2ecf20Sopenharmony_ci		mvebu_pcie_del_windows(port, cur->base, cur->size);
3498c2ecf20Sopenharmony_ci		cur->size = 0;
3508c2ecf20Sopenharmony_ci		cur->base = 0;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		/*
3538c2ecf20Sopenharmony_ci		 * If something tries to change the window while it is enabled
3548c2ecf20Sopenharmony_ci		 * the change will not be done atomically. That would be
3558c2ecf20Sopenharmony_ci		 * difficult to do in the general case.
3568c2ecf20Sopenharmony_ci		 */
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (desired->size == 0)
3608c2ecf20Sopenharmony_ci		return;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	mvebu_pcie_add_windows(port, target, attribute, desired->base,
3638c2ecf20Sopenharmony_ci			       desired->size, desired->remap);
3648c2ecf20Sopenharmony_ci	*cur = *desired;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	struct mvebu_pcie_window desired = {};
3708c2ecf20Sopenharmony_ci	struct pci_bridge_emul_conf *conf = &port->bridge.conf;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* Are the new iobase/iolimit values invalid? */
3738c2ecf20Sopenharmony_ci	if (conf->iolimit < conf->iobase ||
3748c2ecf20Sopenharmony_ci	    conf->iolimitupper < conf->iobaseupper ||
3758c2ecf20Sopenharmony_ci	    !(conf->command & PCI_COMMAND_IO)) {
3768c2ecf20Sopenharmony_ci		mvebu_pcie_set_window(port, port->io_target, port->io_attr,
3778c2ecf20Sopenharmony_ci				      &desired, &port->iowin);
3788c2ecf20Sopenharmony_ci		return;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (!mvebu_has_ioport(port)) {
3828c2ecf20Sopenharmony_ci		dev_WARN(&port->pcie->pdev->dev,
3838c2ecf20Sopenharmony_ci			 "Attempt to set IO when IO is disabled\n");
3848c2ecf20Sopenharmony_ci		return;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	/*
3888c2ecf20Sopenharmony_ci	 * We read the PCI-to-PCI bridge emulated registers, and
3898c2ecf20Sopenharmony_ci	 * calculate the base address and size of the address decoding
3908c2ecf20Sopenharmony_ci	 * window to setup, according to the PCI-to-PCI bridge
3918c2ecf20Sopenharmony_ci	 * specifications. iobase is the bus address, port->iowin_base
3928c2ecf20Sopenharmony_ci	 * is the CPU address.
3938c2ecf20Sopenharmony_ci	 */
3948c2ecf20Sopenharmony_ci	desired.remap = ((conf->iobase & 0xF0) << 8) |
3958c2ecf20Sopenharmony_ci			(conf->iobaseupper << 16);
3968c2ecf20Sopenharmony_ci	desired.base = port->pcie->io.start + desired.remap;
3978c2ecf20Sopenharmony_ci	desired.size = ((0xFFF | ((conf->iolimit & 0xF0) << 8) |
3988c2ecf20Sopenharmony_ci			 (conf->iolimitupper << 16)) -
3998c2ecf20Sopenharmony_ci			desired.remap) +
4008c2ecf20Sopenharmony_ci		       1;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired,
4038c2ecf20Sopenharmony_ci			      &port->iowin);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
4098c2ecf20Sopenharmony_ci	struct pci_bridge_emul_conf *conf = &port->bridge.conf;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* Are the new membase/memlimit values invalid? */
4128c2ecf20Sopenharmony_ci	if (conf->memlimit < conf->membase ||
4138c2ecf20Sopenharmony_ci	    !(conf->command & PCI_COMMAND_MEMORY)) {
4148c2ecf20Sopenharmony_ci		mvebu_pcie_set_window(port, port->mem_target, port->mem_attr,
4158c2ecf20Sopenharmony_ci				      &desired, &port->memwin);
4168c2ecf20Sopenharmony_ci		return;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/*
4208c2ecf20Sopenharmony_ci	 * We read the PCI-to-PCI bridge emulated registers, and
4218c2ecf20Sopenharmony_ci	 * calculate the base address and size of the address decoding
4228c2ecf20Sopenharmony_ci	 * window to setup, according to the PCI-to-PCI bridge
4238c2ecf20Sopenharmony_ci	 * specifications.
4248c2ecf20Sopenharmony_ci	 */
4258c2ecf20Sopenharmony_ci	desired.base = ((conf->membase & 0xFFF0) << 16);
4268c2ecf20Sopenharmony_ci	desired.size = (((conf->memlimit & 0xFFF0) << 16) | 0xFFFFF) -
4278c2ecf20Sopenharmony_ci		       desired.base + 1;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired,
4308c2ecf20Sopenharmony_ci			      &port->memwin);
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic pci_bridge_emul_read_status_t
4348c2ecf20Sopenharmony_cimvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
4358c2ecf20Sopenharmony_ci				     int reg, u32 *value)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct mvebu_pcie_port *port = bridge->data;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	switch (reg) {
4408c2ecf20Sopenharmony_ci	case PCI_EXP_DEVCAP:
4418c2ecf20Sopenharmony_ci		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCAP);
4428c2ecf20Sopenharmony_ci		break;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	case PCI_EXP_DEVCTL:
4458c2ecf20Sopenharmony_ci		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL) &
4468c2ecf20Sopenharmony_ci				 ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE |
4478c2ecf20Sopenharmony_ci				   PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE);
4488c2ecf20Sopenharmony_ci		break;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	case PCI_EXP_LNKCAP:
4518c2ecf20Sopenharmony_ci		/*
4528c2ecf20Sopenharmony_ci		 * PCIe requires the clock power management capability to be
4538c2ecf20Sopenharmony_ci		 * hard-wired to zero for downstream ports
4548c2ecf20Sopenharmony_ci		 */
4558c2ecf20Sopenharmony_ci		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCAP) &
4568c2ecf20Sopenharmony_ci			 ~PCI_EXP_LNKCAP_CLKPM;
4578c2ecf20Sopenharmony_ci		break;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	case PCI_EXP_LNKCTL:
4608c2ecf20Sopenharmony_ci		*value = mvebu_readl(port, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
4618c2ecf20Sopenharmony_ci		break;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	case PCI_EXP_SLTCTL:
4648c2ecf20Sopenharmony_ci		*value = PCI_EXP_SLTSTA_PDS << 16;
4658c2ecf20Sopenharmony_ci		break;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	case PCI_EXP_RTSTA:
4688c2ecf20Sopenharmony_ci		*value = mvebu_readl(port, PCIE_RC_RTSTA);
4698c2ecf20Sopenharmony_ci		break;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	default:
4728c2ecf20Sopenharmony_ci		return PCI_BRIDGE_EMUL_NOT_HANDLED;
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	return PCI_BRIDGE_EMUL_HANDLED;
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic void
4798c2ecf20Sopenharmony_cimvebu_pci_bridge_emul_base_conf_write(struct pci_bridge_emul *bridge,
4808c2ecf20Sopenharmony_ci				      int reg, u32 old, u32 new, u32 mask)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct mvebu_pcie_port *port = bridge->data;
4838c2ecf20Sopenharmony_ci	struct pci_bridge_emul_conf *conf = &bridge->conf;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	switch (reg) {
4868c2ecf20Sopenharmony_ci	case PCI_COMMAND:
4878c2ecf20Sopenharmony_ci	{
4888c2ecf20Sopenharmony_ci		if (!mvebu_has_ioport(port))
4898c2ecf20Sopenharmony_ci			conf->command &= ~PCI_COMMAND_IO;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		if ((old ^ new) & PCI_COMMAND_IO)
4928c2ecf20Sopenharmony_ci			mvebu_pcie_handle_iobase_change(port);
4938c2ecf20Sopenharmony_ci		if ((old ^ new) & PCI_COMMAND_MEMORY)
4948c2ecf20Sopenharmony_ci			mvebu_pcie_handle_membase_change(port);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci		break;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	case PCI_IO_BASE:
5008c2ecf20Sopenharmony_ci		/*
5018c2ecf20Sopenharmony_ci		 * We keep bit 1 set, it is a read-only bit that
5028c2ecf20Sopenharmony_ci		 * indicates we support 32 bits addressing for the
5038c2ecf20Sopenharmony_ci		 * I/O
5048c2ecf20Sopenharmony_ci		 */
5058c2ecf20Sopenharmony_ci		conf->iobase |= PCI_IO_RANGE_TYPE_32;
5068c2ecf20Sopenharmony_ci		conf->iolimit |= PCI_IO_RANGE_TYPE_32;
5078c2ecf20Sopenharmony_ci		mvebu_pcie_handle_iobase_change(port);
5088c2ecf20Sopenharmony_ci		break;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	case PCI_MEMORY_BASE:
5118c2ecf20Sopenharmony_ci		mvebu_pcie_handle_membase_change(port);
5128c2ecf20Sopenharmony_ci		break;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	case PCI_IO_BASE_UPPER16:
5158c2ecf20Sopenharmony_ci		mvebu_pcie_handle_iobase_change(port);
5168c2ecf20Sopenharmony_ci		break;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	case PCI_PRIMARY_BUS:
5198c2ecf20Sopenharmony_ci		mvebu_pcie_set_local_bus_nr(port, conf->secondary_bus);
5208c2ecf20Sopenharmony_ci		break;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	default:
5238c2ecf20Sopenharmony_ci		break;
5248c2ecf20Sopenharmony_ci	}
5258c2ecf20Sopenharmony_ci}
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_cistatic void
5288c2ecf20Sopenharmony_cimvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
5298c2ecf20Sopenharmony_ci				      int reg, u32 old, u32 new, u32 mask)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	struct mvebu_pcie_port *port = bridge->data;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	switch (reg) {
5348c2ecf20Sopenharmony_ci	case PCI_EXP_DEVCTL:
5358c2ecf20Sopenharmony_ci		/*
5368c2ecf20Sopenharmony_ci		 * Armada370 data says these bits must always
5378c2ecf20Sopenharmony_ci		 * be zero when in root complex mode.
5388c2ecf20Sopenharmony_ci		 */
5398c2ecf20Sopenharmony_ci		new &= ~(PCI_EXP_DEVCTL_URRE | PCI_EXP_DEVCTL_FERE |
5408c2ecf20Sopenharmony_ci			 PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_CERE);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci		mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_DEVCTL);
5438c2ecf20Sopenharmony_ci		break;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	case PCI_EXP_LNKCTL:
5468c2ecf20Sopenharmony_ci		/*
5478c2ecf20Sopenharmony_ci		 * If we don't support CLKREQ, we must ensure that the
5488c2ecf20Sopenharmony_ci		 * CLKREQ enable bit always reads zero.  Since we haven't
5498c2ecf20Sopenharmony_ci		 * had this capability, and it's dependent on board wiring,
5508c2ecf20Sopenharmony_ci		 * disable it for the time being.
5518c2ecf20Sopenharmony_ci		 */
5528c2ecf20Sopenharmony_ci		new &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci		mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
5558c2ecf20Sopenharmony_ci		break;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	case PCI_EXP_RTSTA:
5588c2ecf20Sopenharmony_ci		mvebu_writel(port, new, PCIE_RC_RTSTA);
5598c2ecf20Sopenharmony_ci		break;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic struct pci_bridge_emul_ops mvebu_pci_bridge_emul_ops = {
5648c2ecf20Sopenharmony_ci	.write_base = mvebu_pci_bridge_emul_base_conf_write,
5658c2ecf20Sopenharmony_ci	.read_pcie = mvebu_pci_bridge_emul_pcie_conf_read,
5668c2ecf20Sopenharmony_ci	.write_pcie = mvebu_pci_bridge_emul_pcie_conf_write,
5678c2ecf20Sopenharmony_ci};
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci/*
5708c2ecf20Sopenharmony_ci * Initialize the configuration space of the PCI-to-PCI bridge
5718c2ecf20Sopenharmony_ci * associated with the given PCIe interface.
5728c2ecf20Sopenharmony_ci */
5738c2ecf20Sopenharmony_cistatic void mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	struct pci_bridge_emul *bridge = &port->bridge;
5768c2ecf20Sopenharmony_ci	u32 pcie_cap = mvebu_readl(port, PCIE_CAP_PCIEXP);
5778c2ecf20Sopenharmony_ci	u8 pcie_cap_ver = ((pcie_cap >> 16) & PCI_EXP_FLAGS_VERS);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	bridge->conf.vendor = PCI_VENDOR_ID_MARVELL;
5808c2ecf20Sopenharmony_ci	bridge->conf.device = mvebu_readl(port, PCIE_DEV_ID_OFF) >> 16;
5818c2ecf20Sopenharmony_ci	bridge->conf.class_revision =
5828c2ecf20Sopenharmony_ci		mvebu_readl(port, PCIE_DEV_REV_OFF) & 0xff;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (mvebu_has_ioport(port)) {
5858c2ecf20Sopenharmony_ci		/* We support 32 bits I/O addressing */
5868c2ecf20Sopenharmony_ci		bridge->conf.iobase = PCI_IO_RANGE_TYPE_32;
5878c2ecf20Sopenharmony_ci		bridge->conf.iolimit = PCI_IO_RANGE_TYPE_32;
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	/*
5918c2ecf20Sopenharmony_ci	 * Older mvebu hardware provides PCIe Capability structure only in
5928c2ecf20Sopenharmony_ci	 * version 1. New hardware provides it in version 2.
5938c2ecf20Sopenharmony_ci	 */
5948c2ecf20Sopenharmony_ci	bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver);
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	bridge->has_pcie = true;
5978c2ecf20Sopenharmony_ci	bridge->data = port;
5988c2ecf20Sopenharmony_ci	bridge->ops = &mvebu_pci_bridge_emul_ops;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	pci_bridge_emul_init(bridge, PCI_BRIDGE_EMUL_NO_PREFETCHABLE_BAR);
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_cistatic inline struct mvebu_pcie *sys_to_pcie(struct pci_sys_data *sys)
6048c2ecf20Sopenharmony_ci{
6058c2ecf20Sopenharmony_ci	return sys->private_data;
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic struct mvebu_pcie_port *mvebu_pcie_find_port(struct mvebu_pcie *pcie,
6098c2ecf20Sopenharmony_ci						    struct pci_bus *bus,
6108c2ecf20Sopenharmony_ci						    int devfn)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	int i;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	for (i = 0; i < pcie->nports; i++) {
6158c2ecf20Sopenharmony_ci		struct mvebu_pcie_port *port = &pcie->ports[i];
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		if (bus->number == 0 && port->devfn == devfn)
6188c2ecf20Sopenharmony_ci			return port;
6198c2ecf20Sopenharmony_ci		if (bus->number != 0 &&
6208c2ecf20Sopenharmony_ci		    bus->number >= port->bridge.conf.secondary_bus &&
6218c2ecf20Sopenharmony_ci		    bus->number <= port->bridge.conf.subordinate_bus)
6228c2ecf20Sopenharmony_ci			return port;
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	return NULL;
6268c2ecf20Sopenharmony_ci}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci/* PCI configuration space write function */
6298c2ecf20Sopenharmony_cistatic int mvebu_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
6308c2ecf20Sopenharmony_ci			      int where, int size, u32 val)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	struct mvebu_pcie *pcie = bus->sysdata;
6338c2ecf20Sopenharmony_ci	struct mvebu_pcie_port *port;
6348c2ecf20Sopenharmony_ci	int ret;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	port = mvebu_pcie_find_port(pcie, bus, devfn);
6378c2ecf20Sopenharmony_ci	if (!port)
6388c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	/* Access the emulated PCI-to-PCI bridge */
6418c2ecf20Sopenharmony_ci	if (bus->number == 0)
6428c2ecf20Sopenharmony_ci		return pci_bridge_emul_conf_write(&port->bridge, where,
6438c2ecf20Sopenharmony_ci						  size, val);
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (!mvebu_pcie_link_up(port))
6468c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	/* Access the real PCIe interface */
6498c2ecf20Sopenharmony_ci	ret = mvebu_pcie_hw_wr_conf(port, bus, devfn,
6508c2ecf20Sopenharmony_ci				    where, size, val);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	return ret;
6538c2ecf20Sopenharmony_ci}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci/* PCI configuration space read function */
6568c2ecf20Sopenharmony_cistatic int mvebu_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
6578c2ecf20Sopenharmony_ci			      int size, u32 *val)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	struct mvebu_pcie *pcie = bus->sysdata;
6608c2ecf20Sopenharmony_ci	struct mvebu_pcie_port *port;
6618c2ecf20Sopenharmony_ci	int ret;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	port = mvebu_pcie_find_port(pcie, bus, devfn);
6648c2ecf20Sopenharmony_ci	if (!port) {
6658c2ecf20Sopenharmony_ci		*val = 0xffffffff;
6668c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	/* Access the emulated PCI-to-PCI bridge */
6708c2ecf20Sopenharmony_ci	if (bus->number == 0)
6718c2ecf20Sopenharmony_ci		return pci_bridge_emul_conf_read(&port->bridge, where,
6728c2ecf20Sopenharmony_ci						 size, val);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	if (!mvebu_pcie_link_up(port)) {
6758c2ecf20Sopenharmony_ci		*val = 0xffffffff;
6768c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
6778c2ecf20Sopenharmony_ci	}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	/* Access the real PCIe interface */
6808c2ecf20Sopenharmony_ci	ret = mvebu_pcie_hw_rd_conf(port, bus, devfn,
6818c2ecf20Sopenharmony_ci				    where, size, val);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	return ret;
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cistatic struct pci_ops mvebu_pcie_ops = {
6878c2ecf20Sopenharmony_ci	.read = mvebu_pcie_rd_conf,
6888c2ecf20Sopenharmony_ci	.write = mvebu_pcie_wr_conf,
6898c2ecf20Sopenharmony_ci};
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic resource_size_t mvebu_pcie_align_resource(struct pci_dev *dev,
6928c2ecf20Sopenharmony_ci						 const struct resource *res,
6938c2ecf20Sopenharmony_ci						 resource_size_t start,
6948c2ecf20Sopenharmony_ci						 resource_size_t size,
6958c2ecf20Sopenharmony_ci						 resource_size_t align)
6968c2ecf20Sopenharmony_ci{
6978c2ecf20Sopenharmony_ci	if (dev->bus->number != 0)
6988c2ecf20Sopenharmony_ci		return start;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	/*
7018c2ecf20Sopenharmony_ci	 * On the PCI-to-PCI bridge side, the I/O windows must have at
7028c2ecf20Sopenharmony_ci	 * least a 64 KB size and the memory windows must have at
7038c2ecf20Sopenharmony_ci	 * least a 1 MB size. Moreover, MBus windows need to have a
7048c2ecf20Sopenharmony_ci	 * base address aligned on their size, and their size must be
7058c2ecf20Sopenharmony_ci	 * a power of two. This means that if the BAR doesn't have a
7068c2ecf20Sopenharmony_ci	 * power of two size, several MBus windows will actually be
7078c2ecf20Sopenharmony_ci	 * created. We need to ensure that the biggest MBus window
7088c2ecf20Sopenharmony_ci	 * (which will be the first one) is aligned on its size, which
7098c2ecf20Sopenharmony_ci	 * explains the rounddown_pow_of_two() being done here.
7108c2ecf20Sopenharmony_ci	 */
7118c2ecf20Sopenharmony_ci	if (res->flags & IORESOURCE_IO)
7128c2ecf20Sopenharmony_ci		return round_up(start, max_t(resource_size_t, SZ_64K,
7138c2ecf20Sopenharmony_ci					     rounddown_pow_of_two(size)));
7148c2ecf20Sopenharmony_ci	else if (res->flags & IORESOURCE_MEM)
7158c2ecf20Sopenharmony_ci		return round_up(start, max_t(resource_size_t, SZ_1M,
7168c2ecf20Sopenharmony_ci					     rounddown_pow_of_two(size)));
7178c2ecf20Sopenharmony_ci	else
7188c2ecf20Sopenharmony_ci		return start;
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_cistatic void __iomem *mvebu_pcie_map_registers(struct platform_device *pdev,
7228c2ecf20Sopenharmony_ci					      struct device_node *np,
7238c2ecf20Sopenharmony_ci					      struct mvebu_pcie_port *port)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	int ret = 0;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	ret = of_address_to_resource(np, 0, &port->regs);
7288c2ecf20Sopenharmony_ci	if (ret)
7298c2ecf20Sopenharmony_ci		return (void __iomem *)ERR_PTR(ret);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	return devm_ioremap_resource(&pdev->dev, &port->regs);
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci#define DT_FLAGS_TO_TYPE(flags)       (((flags) >> 24) & 0x03)
7358c2ecf20Sopenharmony_ci#define    DT_TYPE_IO                 0x1
7368c2ecf20Sopenharmony_ci#define    DT_TYPE_MEM32              0x2
7378c2ecf20Sopenharmony_ci#define DT_CPUADDR_TO_TARGET(cpuaddr) (((cpuaddr) >> 56) & 0xFF)
7388c2ecf20Sopenharmony_ci#define DT_CPUADDR_TO_ATTR(cpuaddr)   (((cpuaddr) >> 48) & 0xFF)
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_cistatic int mvebu_get_tgt_attr(struct device_node *np, int devfn,
7418c2ecf20Sopenharmony_ci			      unsigned long type,
7428c2ecf20Sopenharmony_ci			      unsigned int *tgt,
7438c2ecf20Sopenharmony_ci			      unsigned int *attr)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	const int na = 3, ns = 2;
7468c2ecf20Sopenharmony_ci	const __be32 *range;
7478c2ecf20Sopenharmony_ci	int rlen, nranges, rangesz, pna, i;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	*tgt = -1;
7508c2ecf20Sopenharmony_ci	*attr = -1;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	range = of_get_property(np, "ranges", &rlen);
7538c2ecf20Sopenharmony_ci	if (!range)
7548c2ecf20Sopenharmony_ci		return -EINVAL;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	pna = of_n_addr_cells(np);
7578c2ecf20Sopenharmony_ci	rangesz = pna + na + ns;
7588c2ecf20Sopenharmony_ci	nranges = rlen / sizeof(__be32) / rangesz;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	for (i = 0; i < nranges; i++, range += rangesz) {
7618c2ecf20Sopenharmony_ci		u32 flags = of_read_number(range, 1);
7628c2ecf20Sopenharmony_ci		u32 slot = of_read_number(range + 1, 1);
7638c2ecf20Sopenharmony_ci		u64 cpuaddr = of_read_number(range + na, pna);
7648c2ecf20Sopenharmony_ci		unsigned long rtype;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci		if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_IO)
7678c2ecf20Sopenharmony_ci			rtype = IORESOURCE_IO;
7688c2ecf20Sopenharmony_ci		else if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_MEM32)
7698c2ecf20Sopenharmony_ci			rtype = IORESOURCE_MEM;
7708c2ecf20Sopenharmony_ci		else
7718c2ecf20Sopenharmony_ci			continue;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci		if (slot == PCI_SLOT(devfn) && type == rtype) {
7748c2ecf20Sopenharmony_ci			*tgt = DT_CPUADDR_TO_TARGET(cpuaddr);
7758c2ecf20Sopenharmony_ci			*attr = DT_CPUADDR_TO_ATTR(cpuaddr);
7768c2ecf20Sopenharmony_ci			return 0;
7778c2ecf20Sopenharmony_ci		}
7788c2ecf20Sopenharmony_ci	}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	return -ENOENT;
7818c2ecf20Sopenharmony_ci}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
7848c2ecf20Sopenharmony_cistatic int mvebu_pcie_suspend(struct device *dev)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	struct mvebu_pcie *pcie;
7878c2ecf20Sopenharmony_ci	int i;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	pcie = dev_get_drvdata(dev);
7908c2ecf20Sopenharmony_ci	for (i = 0; i < pcie->nports; i++) {
7918c2ecf20Sopenharmony_ci		struct mvebu_pcie_port *port = pcie->ports + i;
7928c2ecf20Sopenharmony_ci		port->saved_pcie_stat = mvebu_readl(port, PCIE_STAT_OFF);
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	return 0;
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_cistatic int mvebu_pcie_resume(struct device *dev)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	struct mvebu_pcie *pcie;
8018c2ecf20Sopenharmony_ci	int i;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	pcie = dev_get_drvdata(dev);
8048c2ecf20Sopenharmony_ci	for (i = 0; i < pcie->nports; i++) {
8058c2ecf20Sopenharmony_ci		struct mvebu_pcie_port *port = pcie->ports + i;
8068c2ecf20Sopenharmony_ci		mvebu_writel(port, port->saved_pcie_stat, PCIE_STAT_OFF);
8078c2ecf20Sopenharmony_ci		mvebu_pcie_setup_hw(port);
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	return 0;
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ci#endif
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_cistatic void mvebu_pcie_port_clk_put(void *data)
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	struct mvebu_pcie_port *port = data;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	clk_put(port->clk);
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistatic int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
8228c2ecf20Sopenharmony_ci	struct mvebu_pcie_port *port, struct device_node *child)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	struct device *dev = &pcie->pdev->dev;
8258c2ecf20Sopenharmony_ci	enum of_gpio_flags flags;
8268c2ecf20Sopenharmony_ci	int reset_gpio, ret;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	port->pcie = pcie;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (of_property_read_u32(child, "marvell,pcie-port", &port->port)) {
8318c2ecf20Sopenharmony_ci		dev_warn(dev, "ignoring %pOF, missing pcie-port property\n",
8328c2ecf20Sopenharmony_ci			 child);
8338c2ecf20Sopenharmony_ci		goto skip;
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (of_property_read_u32(child, "marvell,pcie-lane", &port->lane))
8378c2ecf20Sopenharmony_ci		port->lane = 0;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	port->name = devm_kasprintf(dev, GFP_KERNEL, "pcie%d.%d", port->port,
8408c2ecf20Sopenharmony_ci				    port->lane);
8418c2ecf20Sopenharmony_ci	if (!port->name) {
8428c2ecf20Sopenharmony_ci		ret = -ENOMEM;
8438c2ecf20Sopenharmony_ci		goto err;
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	port->devfn = of_pci_get_devfn(child);
8478c2ecf20Sopenharmony_ci	if (port->devfn < 0)
8488c2ecf20Sopenharmony_ci		goto skip;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	ret = mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_MEM,
8518c2ecf20Sopenharmony_ci				 &port->mem_target, &port->mem_attr);
8528c2ecf20Sopenharmony_ci	if (ret < 0) {
8538c2ecf20Sopenharmony_ci		dev_err(dev, "%s: cannot get tgt/attr for mem window\n",
8548c2ecf20Sopenharmony_ci			port->name);
8558c2ecf20Sopenharmony_ci		goto skip;
8568c2ecf20Sopenharmony_ci	}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (resource_size(&pcie->io) != 0) {
8598c2ecf20Sopenharmony_ci		mvebu_get_tgt_attr(dev->of_node, port->devfn, IORESOURCE_IO,
8608c2ecf20Sopenharmony_ci				   &port->io_target, &port->io_attr);
8618c2ecf20Sopenharmony_ci	} else {
8628c2ecf20Sopenharmony_ci		port->io_target = -1;
8638c2ecf20Sopenharmony_ci		port->io_attr = -1;
8648c2ecf20Sopenharmony_ci	}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	reset_gpio = of_get_named_gpio_flags(child, "reset-gpios", 0, &flags);
8678c2ecf20Sopenharmony_ci	if (reset_gpio == -EPROBE_DEFER) {
8688c2ecf20Sopenharmony_ci		ret = reset_gpio;
8698c2ecf20Sopenharmony_ci		goto err;
8708c2ecf20Sopenharmony_ci	}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	if (gpio_is_valid(reset_gpio)) {
8738c2ecf20Sopenharmony_ci		unsigned long gpio_flags;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci		port->reset_name = devm_kasprintf(dev, GFP_KERNEL, "%s-reset",
8768c2ecf20Sopenharmony_ci						  port->name);
8778c2ecf20Sopenharmony_ci		if (!port->reset_name) {
8788c2ecf20Sopenharmony_ci			ret = -ENOMEM;
8798c2ecf20Sopenharmony_ci			goto err;
8808c2ecf20Sopenharmony_ci		}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci		if (flags & OF_GPIO_ACTIVE_LOW) {
8838c2ecf20Sopenharmony_ci			dev_info(dev, "%pOF: reset gpio is active low\n",
8848c2ecf20Sopenharmony_ci				 child);
8858c2ecf20Sopenharmony_ci			gpio_flags = GPIOF_ACTIVE_LOW |
8868c2ecf20Sopenharmony_ci				     GPIOF_OUT_INIT_LOW;
8878c2ecf20Sopenharmony_ci		} else {
8888c2ecf20Sopenharmony_ci			gpio_flags = GPIOF_OUT_INIT_HIGH;
8898c2ecf20Sopenharmony_ci		}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci		ret = devm_gpio_request_one(dev, reset_gpio, gpio_flags,
8928c2ecf20Sopenharmony_ci					    port->reset_name);
8938c2ecf20Sopenharmony_ci		if (ret) {
8948c2ecf20Sopenharmony_ci			if (ret == -EPROBE_DEFER)
8958c2ecf20Sopenharmony_ci				goto err;
8968c2ecf20Sopenharmony_ci			goto skip;
8978c2ecf20Sopenharmony_ci		}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci		port->reset_gpio = gpio_to_desc(reset_gpio);
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	port->clk = of_clk_get_by_name(child, NULL);
9038c2ecf20Sopenharmony_ci	if (IS_ERR(port->clk)) {
9048c2ecf20Sopenharmony_ci		dev_err(dev, "%s: cannot get clock\n", port->name);
9058c2ecf20Sopenharmony_ci		goto skip;
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	ret = devm_add_action(dev, mvebu_pcie_port_clk_put, port);
9098c2ecf20Sopenharmony_ci	if (ret < 0) {
9108c2ecf20Sopenharmony_ci		clk_put(port->clk);
9118c2ecf20Sopenharmony_ci		goto err;
9128c2ecf20Sopenharmony_ci	}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	return 1;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ciskip:
9178c2ecf20Sopenharmony_ci	ret = 0;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* In the case of skipping, we need to free these */
9208c2ecf20Sopenharmony_ci	devm_kfree(dev, port->reset_name);
9218c2ecf20Sopenharmony_ci	port->reset_name = NULL;
9228c2ecf20Sopenharmony_ci	devm_kfree(dev, port->name);
9238c2ecf20Sopenharmony_ci	port->name = NULL;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_cierr:
9268c2ecf20Sopenharmony_ci	return ret;
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci/*
9308c2ecf20Sopenharmony_ci * Power up a PCIe port.  PCIe requires the refclk to be stable for 100µs
9318c2ecf20Sopenharmony_ci * prior to releasing PERST.  See table 2-4 in section 2.6.2 AC Specifications
9328c2ecf20Sopenharmony_ci * of the PCI Express Card Electromechanical Specification, 1.1.
9338c2ecf20Sopenharmony_ci */
9348c2ecf20Sopenharmony_cistatic int mvebu_pcie_powerup(struct mvebu_pcie_port *port)
9358c2ecf20Sopenharmony_ci{
9368c2ecf20Sopenharmony_ci	int ret;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(port->clk);
9398c2ecf20Sopenharmony_ci	if (ret < 0)
9408c2ecf20Sopenharmony_ci		return ret;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	if (port->reset_gpio) {
9438c2ecf20Sopenharmony_ci		u32 reset_udelay = PCI_PM_D3COLD_WAIT * 1000;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci		of_property_read_u32(port->dn, "reset-delay-us",
9468c2ecf20Sopenharmony_ci				     &reset_udelay);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci		udelay(100);
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(port->reset_gpio, 0);
9518c2ecf20Sopenharmony_ci		msleep(reset_udelay / 1000);
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	return 0;
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci/*
9588c2ecf20Sopenharmony_ci * Power down a PCIe port.  Strictly, PCIe requires us to place the card
9598c2ecf20Sopenharmony_ci * in D3hot state before asserting PERST#.
9608c2ecf20Sopenharmony_ci */
9618c2ecf20Sopenharmony_cistatic void mvebu_pcie_powerdown(struct mvebu_pcie_port *port)
9628c2ecf20Sopenharmony_ci{
9638c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(port->reset_gpio, 1);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	clk_disable_unprepare(port->clk);
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci/*
9698c2ecf20Sopenharmony_ci * devm_of_pci_get_host_bridge_resources() only sets up translateable resources,
9708c2ecf20Sopenharmony_ci * so we need extra resource setup parsing our special DT properties encoding
9718c2ecf20Sopenharmony_ci * the MEM and IO apertures.
9728c2ecf20Sopenharmony_ci */
9738c2ecf20Sopenharmony_cistatic int mvebu_pcie_parse_request_resources(struct mvebu_pcie *pcie)
9748c2ecf20Sopenharmony_ci{
9758c2ecf20Sopenharmony_ci	struct device *dev = &pcie->pdev->dev;
9768c2ecf20Sopenharmony_ci	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
9778c2ecf20Sopenharmony_ci	int ret;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	/* Get the PCIe memory aperture */
9808c2ecf20Sopenharmony_ci	mvebu_mbus_get_pcie_mem_aperture(&pcie->mem);
9818c2ecf20Sopenharmony_ci	if (resource_size(&pcie->mem) == 0) {
9828c2ecf20Sopenharmony_ci		dev_err(dev, "invalid memory aperture size\n");
9838c2ecf20Sopenharmony_ci		return -EINVAL;
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	pcie->mem.name = "PCI MEM";
9878c2ecf20Sopenharmony_ci	pci_add_resource(&bridge->windows, &pcie->mem);
9888c2ecf20Sopenharmony_ci	ret = devm_request_resource(dev, &iomem_resource, &pcie->mem);
9898c2ecf20Sopenharmony_ci	if (ret)
9908c2ecf20Sopenharmony_ci		return ret;
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci	/* Get the PCIe IO aperture */
9938c2ecf20Sopenharmony_ci	mvebu_mbus_get_pcie_io_aperture(&pcie->io);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	if (resource_size(&pcie->io) != 0) {
9968c2ecf20Sopenharmony_ci		pcie->realio.flags = pcie->io.flags;
9978c2ecf20Sopenharmony_ci		pcie->realio.start = PCIBIOS_MIN_IO;
9988c2ecf20Sopenharmony_ci		pcie->realio.end = min_t(resource_size_t,
9998c2ecf20Sopenharmony_ci					 IO_SPACE_LIMIT - SZ_64K,
10008c2ecf20Sopenharmony_ci					 resource_size(&pcie->io) - 1);
10018c2ecf20Sopenharmony_ci		pcie->realio.name = "PCI I/O";
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci		pci_add_resource(&bridge->windows, &pcie->realio);
10048c2ecf20Sopenharmony_ci		ret = devm_request_resource(dev, &ioport_resource, &pcie->realio);
10058c2ecf20Sopenharmony_ci		if (ret)
10068c2ecf20Sopenharmony_ci			return ret;
10078c2ecf20Sopenharmony_ci	}
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	return 0;
10108c2ecf20Sopenharmony_ci}
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci/*
10138c2ecf20Sopenharmony_ci * This is a copy of pci_host_probe(), except that it does the I/O
10148c2ecf20Sopenharmony_ci * remap as the last step, once we are sure we won't fail.
10158c2ecf20Sopenharmony_ci *
10168c2ecf20Sopenharmony_ci * It should be removed once the I/O remap error handling issue has
10178c2ecf20Sopenharmony_ci * been sorted out.
10188c2ecf20Sopenharmony_ci */
10198c2ecf20Sopenharmony_cistatic int mvebu_pci_host_probe(struct pci_host_bridge *bridge)
10208c2ecf20Sopenharmony_ci{
10218c2ecf20Sopenharmony_ci	struct mvebu_pcie *pcie;
10228c2ecf20Sopenharmony_ci	struct pci_bus *bus, *child;
10238c2ecf20Sopenharmony_ci	int ret;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	ret = pci_scan_root_bus_bridge(bridge);
10268c2ecf20Sopenharmony_ci	if (ret < 0) {
10278c2ecf20Sopenharmony_ci		dev_err(bridge->dev.parent, "Scanning root bridge failed");
10288c2ecf20Sopenharmony_ci		return ret;
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	pcie = pci_host_bridge_priv(bridge);
10328c2ecf20Sopenharmony_ci	if (resource_size(&pcie->io) != 0) {
10338c2ecf20Sopenharmony_ci		unsigned int i;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci		for (i = 0; i < resource_size(&pcie->realio); i += SZ_64K)
10368c2ecf20Sopenharmony_ci			pci_ioremap_io(i, pcie->io.start + i);
10378c2ecf20Sopenharmony_ci	}
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	bus = bridge->bus;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	/*
10428c2ecf20Sopenharmony_ci	 * We insert PCI resources into the iomem_resource and
10438c2ecf20Sopenharmony_ci	 * ioport_resource trees in either pci_bus_claim_resources()
10448c2ecf20Sopenharmony_ci	 * or pci_bus_assign_resources().
10458c2ecf20Sopenharmony_ci	 */
10468c2ecf20Sopenharmony_ci	if (pci_has_flag(PCI_PROBE_ONLY)) {
10478c2ecf20Sopenharmony_ci		pci_bus_claim_resources(bus);
10488c2ecf20Sopenharmony_ci	} else {
10498c2ecf20Sopenharmony_ci		pci_bus_size_bridges(bus);
10508c2ecf20Sopenharmony_ci		pci_bus_assign_resources(bus);
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_ci		list_for_each_entry(child, &bus->children, node)
10538c2ecf20Sopenharmony_ci			pcie_bus_configure_settings(child);
10548c2ecf20Sopenharmony_ci	}
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	pci_bus_add_devices(bus);
10578c2ecf20Sopenharmony_ci	return 0;
10588c2ecf20Sopenharmony_ci}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_cistatic int mvebu_pcie_probe(struct platform_device *pdev)
10618c2ecf20Sopenharmony_ci{
10628c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
10638c2ecf20Sopenharmony_ci	struct mvebu_pcie *pcie;
10648c2ecf20Sopenharmony_ci	struct pci_host_bridge *bridge;
10658c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
10668c2ecf20Sopenharmony_ci	struct device_node *child;
10678c2ecf20Sopenharmony_ci	int num, i, ret;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct mvebu_pcie));
10708c2ecf20Sopenharmony_ci	if (!bridge)
10718c2ecf20Sopenharmony_ci		return -ENOMEM;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	pcie = pci_host_bridge_priv(bridge);
10748c2ecf20Sopenharmony_ci	pcie->pdev = pdev;
10758c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, pcie);
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	ret = mvebu_pcie_parse_request_resources(pcie);
10788c2ecf20Sopenharmony_ci	if (ret)
10798c2ecf20Sopenharmony_ci		return ret;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	num = of_get_available_child_count(np);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	pcie->ports = devm_kcalloc(dev, num, sizeof(*pcie->ports), GFP_KERNEL);
10848c2ecf20Sopenharmony_ci	if (!pcie->ports)
10858c2ecf20Sopenharmony_ci		return -ENOMEM;
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_ci	i = 0;
10888c2ecf20Sopenharmony_ci	for_each_available_child_of_node(np, child) {
10898c2ecf20Sopenharmony_ci		struct mvebu_pcie_port *port = &pcie->ports[i];
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci		ret = mvebu_pcie_parse_port(pcie, port, child);
10928c2ecf20Sopenharmony_ci		if (ret < 0) {
10938c2ecf20Sopenharmony_ci			of_node_put(child);
10948c2ecf20Sopenharmony_ci			return ret;
10958c2ecf20Sopenharmony_ci		} else if (ret == 0) {
10968c2ecf20Sopenharmony_ci			continue;
10978c2ecf20Sopenharmony_ci		}
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci		port->dn = child;
11008c2ecf20Sopenharmony_ci		i++;
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci	pcie->nports = i;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	for (i = 0; i < pcie->nports; i++) {
11058c2ecf20Sopenharmony_ci		struct mvebu_pcie_port *port = &pcie->ports[i];
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci		child = port->dn;
11088c2ecf20Sopenharmony_ci		if (!child)
11098c2ecf20Sopenharmony_ci			continue;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci		ret = mvebu_pcie_powerup(port);
11128c2ecf20Sopenharmony_ci		if (ret < 0)
11138c2ecf20Sopenharmony_ci			continue;
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci		port->base = mvebu_pcie_map_registers(pdev, child, port);
11168c2ecf20Sopenharmony_ci		if (IS_ERR(port->base)) {
11178c2ecf20Sopenharmony_ci			dev_err(dev, "%s: cannot map registers\n", port->name);
11188c2ecf20Sopenharmony_ci			port->base = NULL;
11198c2ecf20Sopenharmony_ci			mvebu_pcie_powerdown(port);
11208c2ecf20Sopenharmony_ci			continue;
11218c2ecf20Sopenharmony_ci		}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci		mvebu_pcie_setup_hw(port);
11248c2ecf20Sopenharmony_ci		mvebu_pcie_set_local_dev_nr(port, 1);
11258c2ecf20Sopenharmony_ci		mvebu_pci_bridge_emul_init(port);
11268c2ecf20Sopenharmony_ci	}
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	pcie->nports = i;
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	bridge->sysdata = pcie;
11318c2ecf20Sopenharmony_ci	bridge->ops = &mvebu_pcie_ops;
11328c2ecf20Sopenharmony_ci	bridge->align_resource = mvebu_pcie_align_resource;
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	return mvebu_pci_host_probe(bridge);
11358c2ecf20Sopenharmony_ci}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_cistatic const struct of_device_id mvebu_pcie_of_match_table[] = {
11388c2ecf20Sopenharmony_ci	{ .compatible = "marvell,armada-xp-pcie", },
11398c2ecf20Sopenharmony_ci	{ .compatible = "marvell,armada-370-pcie", },
11408c2ecf20Sopenharmony_ci	{ .compatible = "marvell,dove-pcie", },
11418c2ecf20Sopenharmony_ci	{ .compatible = "marvell,kirkwood-pcie", },
11428c2ecf20Sopenharmony_ci	{},
11438c2ecf20Sopenharmony_ci};
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mvebu_pcie_pm_ops = {
11468c2ecf20Sopenharmony_ci	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mvebu_pcie_suspend, mvebu_pcie_resume)
11478c2ecf20Sopenharmony_ci};
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_cistatic struct platform_driver mvebu_pcie_driver = {
11508c2ecf20Sopenharmony_ci	.driver = {
11518c2ecf20Sopenharmony_ci		.name = "mvebu-pcie",
11528c2ecf20Sopenharmony_ci		.of_match_table = mvebu_pcie_of_match_table,
11538c2ecf20Sopenharmony_ci		/* driver unloading/unbinding currently not supported */
11548c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
11558c2ecf20Sopenharmony_ci		.pm = &mvebu_pcie_pm_ops,
11568c2ecf20Sopenharmony_ci	},
11578c2ecf20Sopenharmony_ci	.probe = mvebu_pcie_probe,
11588c2ecf20Sopenharmony_ci};
11598c2ecf20Sopenharmony_cibuiltin_platform_driver(mvebu_pcie_driver);
1160