162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/* Copyright (C) 2009 - 2019 Broadcom */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/bitfield.h>
562306a36Sopenharmony_ci#include <linux/bitops.h>
662306a36Sopenharmony_ci#include <linux/clk.h>
762306a36Sopenharmony_ci#include <linux/compiler.h>
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/iopoll.h>
1362306a36Sopenharmony_ci#include <linux/ioport.h>
1462306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
1562306a36Sopenharmony_ci#include <linux/irqdomain.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/list.h>
1862306a36Sopenharmony_ci#include <linux/log2.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/msi.h>
2162306a36Sopenharmony_ci#include <linux/of_address.h>
2262306a36Sopenharmony_ci#include <linux/of_irq.h>
2362306a36Sopenharmony_ci#include <linux/of_pci.h>
2462306a36Sopenharmony_ci#include <linux/of_platform.h>
2562306a36Sopenharmony_ci#include <linux/pci.h>
2662306a36Sopenharmony_ci#include <linux/pci-ecam.h>
2762306a36Sopenharmony_ci#include <linux/printk.h>
2862306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
2962306a36Sopenharmony_ci#include <linux/reset.h>
3062306a36Sopenharmony_ci#include <linux/sizes.h>
3162306a36Sopenharmony_ci#include <linux/slab.h>
3262306a36Sopenharmony_ci#include <linux/string.h>
3362306a36Sopenharmony_ci#include <linux/types.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include "../pci.h"
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */
3862306a36Sopenharmony_ci#define BRCM_PCIE_CAP_REGS				0x00ac
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* Broadcom STB PCIe Register Offsets */
4162306a36Sopenharmony_ci#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1				0x0188
4262306a36Sopenharmony_ci#define  PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK	0xc
4362306a36Sopenharmony_ci#define  PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN			0x0
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define PCIE_RC_CFG_PRIV1_ID_VAL3			0x043c
4662306a36Sopenharmony_ci#define  PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK	0xffffff
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY			0x04dc
4962306a36Sopenharmony_ci#define  PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK	0xc00
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define PCIE_RC_DL_MDIO_ADDR				0x1100
5262306a36Sopenharmony_ci#define PCIE_RC_DL_MDIO_WR_DATA				0x1104
5362306a36Sopenharmony_ci#define PCIE_RC_DL_MDIO_RD_DATA				0x1108
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define PCIE_MISC_MISC_CTRL				0x4008
5662306a36Sopenharmony_ci#define  PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK	0x80
5762306a36Sopenharmony_ci#define  PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK	0x400
5862306a36Sopenharmony_ci#define  PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK		0x1000
5962306a36Sopenharmony_ci#define  PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK	0x2000
6062306a36Sopenharmony_ci#define  PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK	0x300000
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define  PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK		0xf8000000
6362306a36Sopenharmony_ci#define  PCIE_MISC_MISC_CTRL_SCB1_SIZE_MASK		0x07c00000
6462306a36Sopenharmony_ci#define  PCIE_MISC_MISC_CTRL_SCB2_SIZE_MASK		0x0000001f
6562306a36Sopenharmony_ci#define  SCB_SIZE_MASK(x) PCIE_MISC_MISC_CTRL_SCB ## x ## _SIZE_MASK
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO		0x400c
6862306a36Sopenharmony_ci#define PCIE_MEM_WIN0_LO(win)	\
6962306a36Sopenharmony_ci		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 8)
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI		0x4010
7262306a36Sopenharmony_ci#define PCIE_MEM_WIN0_HI(win)	\
7362306a36Sopenharmony_ci		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 8)
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define PCIE_MISC_RC_BAR1_CONFIG_LO			0x402c
7662306a36Sopenharmony_ci#define  PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK		0x1f
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#define PCIE_MISC_RC_BAR2_CONFIG_LO			0x4034
7962306a36Sopenharmony_ci#define  PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK		0x1f
8062306a36Sopenharmony_ci#define PCIE_MISC_RC_BAR2_CONFIG_HI			0x4038
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define PCIE_MISC_RC_BAR3_CONFIG_LO			0x403c
8362306a36Sopenharmony_ci#define  PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK		0x1f
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#define PCIE_MISC_MSI_BAR_CONFIG_LO			0x4044
8662306a36Sopenharmony_ci#define PCIE_MISC_MSI_BAR_CONFIG_HI			0x4048
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define PCIE_MISC_MSI_DATA_CONFIG			0x404c
8962306a36Sopenharmony_ci#define  PCIE_MISC_MSI_DATA_CONFIG_VAL_32		0xffe06540
9062306a36Sopenharmony_ci#define  PCIE_MISC_MSI_DATA_CONFIG_VAL_8		0xfff86540
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define PCIE_MISC_PCIE_CTRL				0x4064
9362306a36Sopenharmony_ci#define  PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK	0x1
9462306a36Sopenharmony_ci#define PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK		0x4
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define PCIE_MISC_PCIE_STATUS				0x4068
9762306a36Sopenharmony_ci#define  PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK		0x80
9862306a36Sopenharmony_ci#define  PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK	0x20
9962306a36Sopenharmony_ci#define  PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK	0x10
10062306a36Sopenharmony_ci#define  PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK	0x40
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci#define PCIE_MISC_REVISION				0x406c
10362306a36Sopenharmony_ci#define  BRCM_PCIE_HW_REV_33				0x0303
10462306a36Sopenharmony_ci#define  BRCM_PCIE_HW_REV_3_20				0x0320
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT		0x4070
10762306a36Sopenharmony_ci#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK	0xfff00000
10862306a36Sopenharmony_ci#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK	0xfff0
10962306a36Sopenharmony_ci#define PCIE_MEM_WIN0_BASE_LIMIT(win)	\
11062306a36Sopenharmony_ci		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + ((win) * 4)
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI			0x4080
11362306a36Sopenharmony_ci#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK	0xff
11462306a36Sopenharmony_ci#define PCIE_MEM_WIN0_BASE_HI(win)	\
11562306a36Sopenharmony_ci		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + ((win) * 8)
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI			0x4084
11862306a36Sopenharmony_ci#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK	0xff
11962306a36Sopenharmony_ci#define PCIE_MEM_WIN0_LIMIT_HI(win)	\
12062306a36Sopenharmony_ci		PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8)
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci#define PCIE_MISC_HARD_PCIE_HARD_DEBUG					0x4204
12362306a36Sopenharmony_ci#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK	0x2
12462306a36Sopenharmony_ci#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK		0x08000000
12562306a36Sopenharmony_ci#define  PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK		0x00800000
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci#define PCIE_INTR2_CPU_BASE		0x4300
12962306a36Sopenharmony_ci#define PCIE_MSI_INTR2_BASE		0x4500
13062306a36Sopenharmony_ci/* Offsets from PCIE_INTR2_CPU_BASE and PCIE_MSI_INTR2_BASE */
13162306a36Sopenharmony_ci#define  MSI_INT_STATUS			0x0
13262306a36Sopenharmony_ci#define  MSI_INT_CLR			0x8
13362306a36Sopenharmony_ci#define  MSI_INT_MASK_SET		0x10
13462306a36Sopenharmony_ci#define  MSI_INT_MASK_CLR		0x14
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#define PCIE_EXT_CFG_DATA				0x8000
13762306a36Sopenharmony_ci#define PCIE_EXT_CFG_INDEX				0x9000
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define  PCIE_RGR1_SW_INIT_1_PERST_MASK			0x1
14062306a36Sopenharmony_ci#define  PCIE_RGR1_SW_INIT_1_PERST_SHIFT		0x0
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#define RGR1_SW_INIT_1_INIT_GENERIC_MASK		0x2
14362306a36Sopenharmony_ci#define RGR1_SW_INIT_1_INIT_GENERIC_SHIFT		0x1
14462306a36Sopenharmony_ci#define RGR1_SW_INIT_1_INIT_7278_MASK			0x1
14562306a36Sopenharmony_ci#define RGR1_SW_INIT_1_INIT_7278_SHIFT			0x0
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/* PCIe parameters */
14862306a36Sopenharmony_ci#define BRCM_NUM_PCIE_OUT_WINS		0x4
14962306a36Sopenharmony_ci#define BRCM_INT_PCI_MSI_NR		32
15062306a36Sopenharmony_ci#define BRCM_INT_PCI_MSI_LEGACY_NR	8
15162306a36Sopenharmony_ci#define BRCM_INT_PCI_MSI_SHIFT		0
15262306a36Sopenharmony_ci#define BRCM_INT_PCI_MSI_MASK		GENMASK(BRCM_INT_PCI_MSI_NR - 1, 0)
15362306a36Sopenharmony_ci#define BRCM_INT_PCI_MSI_LEGACY_MASK	GENMASK(31, \
15462306a36Sopenharmony_ci						32 - BRCM_INT_PCI_MSI_LEGACY_NR)
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/* MSI target addresses */
15762306a36Sopenharmony_ci#define BRCM_MSI_TARGET_ADDR_LT_4GB	0x0fffffffcULL
15862306a36Sopenharmony_ci#define BRCM_MSI_TARGET_ADDR_GT_4GB	0xffffffffcULL
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/* MDIO registers */
16162306a36Sopenharmony_ci#define MDIO_PORT0			0x0
16262306a36Sopenharmony_ci#define MDIO_DATA_MASK			0x7fffffff
16362306a36Sopenharmony_ci#define MDIO_PORT_MASK			0xf0000
16462306a36Sopenharmony_ci#define MDIO_REGAD_MASK			0xffff
16562306a36Sopenharmony_ci#define MDIO_CMD_MASK			0xfff00000
16662306a36Sopenharmony_ci#define MDIO_CMD_READ			0x1
16762306a36Sopenharmony_ci#define MDIO_CMD_WRITE			0x0
16862306a36Sopenharmony_ci#define MDIO_DATA_DONE_MASK		0x80000000
16962306a36Sopenharmony_ci#define MDIO_RD_DONE(x)			(((x) & MDIO_DATA_DONE_MASK) ? 1 : 0)
17062306a36Sopenharmony_ci#define MDIO_WT_DONE(x)			(((x) & MDIO_DATA_DONE_MASK) ? 0 : 1)
17162306a36Sopenharmony_ci#define SSC_REGS_ADDR			0x1100
17262306a36Sopenharmony_ci#define SET_ADDR_OFFSET			0x1f
17362306a36Sopenharmony_ci#define SSC_CNTL_OFFSET			0x2
17462306a36Sopenharmony_ci#define SSC_CNTL_OVRD_EN_MASK		0x8000
17562306a36Sopenharmony_ci#define SSC_CNTL_OVRD_VAL_MASK		0x4000
17662306a36Sopenharmony_ci#define SSC_STATUS_OFFSET		0x1
17762306a36Sopenharmony_ci#define SSC_STATUS_SSC_MASK		0x400
17862306a36Sopenharmony_ci#define SSC_STATUS_PLL_LOCK_MASK	0x800
17962306a36Sopenharmony_ci#define PCIE_BRCM_MAX_MEMC		3
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci#define IDX_ADDR(pcie)			(pcie->reg_offsets[EXT_CFG_INDEX])
18262306a36Sopenharmony_ci#define DATA_ADDR(pcie)			(pcie->reg_offsets[EXT_CFG_DATA])
18362306a36Sopenharmony_ci#define PCIE_RGR1_SW_INIT_1(pcie)	(pcie->reg_offsets[RGR1_SW_INIT_1])
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/* Rescal registers */
18662306a36Sopenharmony_ci#define PCIE_DVT_PMU_PCIE_PHY_CTRL				0xc700
18762306a36Sopenharmony_ci#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS			0x3
18862306a36Sopenharmony_ci#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_MASK		0x4
18962306a36Sopenharmony_ci#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_SHIFT	0x2
19062306a36Sopenharmony_ci#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_MASK		0x2
19162306a36Sopenharmony_ci#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT		0x1
19262306a36Sopenharmony_ci#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_MASK		0x1
19362306a36Sopenharmony_ci#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT		0x0
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/* Forward declarations */
19662306a36Sopenharmony_cistruct brcm_pcie;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cienum {
19962306a36Sopenharmony_ci	RGR1_SW_INIT_1,
20062306a36Sopenharmony_ci	EXT_CFG_INDEX,
20162306a36Sopenharmony_ci	EXT_CFG_DATA,
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cienum {
20562306a36Sopenharmony_ci	RGR1_SW_INIT_1_INIT_MASK,
20662306a36Sopenharmony_ci	RGR1_SW_INIT_1_INIT_SHIFT,
20762306a36Sopenharmony_ci};
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cienum pcie_type {
21062306a36Sopenharmony_ci	GENERIC,
21162306a36Sopenharmony_ci	BCM7425,
21262306a36Sopenharmony_ci	BCM7435,
21362306a36Sopenharmony_ci	BCM4908,
21462306a36Sopenharmony_ci	BCM7278,
21562306a36Sopenharmony_ci	BCM2711,
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistruct pcie_cfg_data {
21962306a36Sopenharmony_ci	const int *offsets;
22062306a36Sopenharmony_ci	const enum pcie_type type;
22162306a36Sopenharmony_ci	void (*perst_set)(struct brcm_pcie *pcie, u32 val);
22262306a36Sopenharmony_ci	void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
22362306a36Sopenharmony_ci};
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_cistruct subdev_regulators {
22662306a36Sopenharmony_ci	unsigned int num_supplies;
22762306a36Sopenharmony_ci	struct regulator_bulk_data supplies[];
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistruct brcm_msi {
23162306a36Sopenharmony_ci	struct device		*dev;
23262306a36Sopenharmony_ci	void __iomem		*base;
23362306a36Sopenharmony_ci	struct device_node	*np;
23462306a36Sopenharmony_ci	struct irq_domain	*msi_domain;
23562306a36Sopenharmony_ci	struct irq_domain	*inner_domain;
23662306a36Sopenharmony_ci	struct mutex		lock; /* guards the alloc/free operations */
23762306a36Sopenharmony_ci	u64			target_addr;
23862306a36Sopenharmony_ci	int			irq;
23962306a36Sopenharmony_ci	DECLARE_BITMAP(used, BRCM_INT_PCI_MSI_NR);
24062306a36Sopenharmony_ci	bool			legacy;
24162306a36Sopenharmony_ci	/* Some chips have MSIs in bits [31..24] of a shared register. */
24262306a36Sopenharmony_ci	int			legacy_shift;
24362306a36Sopenharmony_ci	int			nr; /* No. of MSI available, depends on chip */
24462306a36Sopenharmony_ci	/* This is the base pointer for interrupt status/set/clr regs */
24562306a36Sopenharmony_ci	void __iomem		*intr_base;
24662306a36Sopenharmony_ci};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/* Internal PCIe Host Controller Information.*/
24962306a36Sopenharmony_cistruct brcm_pcie {
25062306a36Sopenharmony_ci	struct device		*dev;
25162306a36Sopenharmony_ci	void __iomem		*base;
25262306a36Sopenharmony_ci	struct clk		*clk;
25362306a36Sopenharmony_ci	struct device_node	*np;
25462306a36Sopenharmony_ci	bool			ssc;
25562306a36Sopenharmony_ci	int			gen;
25662306a36Sopenharmony_ci	u64			msi_target_addr;
25762306a36Sopenharmony_ci	struct brcm_msi		*msi;
25862306a36Sopenharmony_ci	const int		*reg_offsets;
25962306a36Sopenharmony_ci	enum pcie_type		type;
26062306a36Sopenharmony_ci	struct reset_control	*rescal;
26162306a36Sopenharmony_ci	struct reset_control	*perst_reset;
26262306a36Sopenharmony_ci	int			num_memc;
26362306a36Sopenharmony_ci	u64			memc_size[PCIE_BRCM_MAX_MEMC];
26462306a36Sopenharmony_ci	u32			hw_rev;
26562306a36Sopenharmony_ci	void			(*perst_set)(struct brcm_pcie *pcie, u32 val);
26662306a36Sopenharmony_ci	void			(*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
26762306a36Sopenharmony_ci	struct subdev_regulators *sr;
26862306a36Sopenharmony_ci	bool			ep_wakeup_capable;
26962306a36Sopenharmony_ci};
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic inline bool is_bmips(const struct brcm_pcie *pcie)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	return pcie->type == BCM7435 || pcie->type == BCM7425;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * This is to convert the size of the inbound "BAR" region to the
27862306a36Sopenharmony_ci * non-linear values of PCIE_X_MISC_RC_BAR[123]_CONFIG_LO.SIZE
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_cistatic int brcm_pcie_encode_ibar_size(u64 size)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	int log2_in = ilog2(size);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (log2_in >= 12 && log2_in <= 15)
28562306a36Sopenharmony_ci		/* Covers 4KB to 32KB (inclusive) */
28662306a36Sopenharmony_ci		return (log2_in - 12) + 0x1c;
28762306a36Sopenharmony_ci	else if (log2_in >= 16 && log2_in <= 35)
28862306a36Sopenharmony_ci		/* Covers 64KB to 32GB, (inclusive) */
28962306a36Sopenharmony_ci		return log2_in - 15;
29062306a36Sopenharmony_ci	/* Something is awry so disable */
29162306a36Sopenharmony_ci	return 0;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	u32 pkt = 0;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	pkt |= FIELD_PREP(MDIO_PORT_MASK, port);
29962306a36Sopenharmony_ci	pkt |= FIELD_PREP(MDIO_REGAD_MASK, regad);
30062306a36Sopenharmony_ci	pkt |= FIELD_PREP(MDIO_CMD_MASK, cmd);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return pkt;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/* negative return value indicates error */
30662306a36Sopenharmony_cistatic int brcm_pcie_mdio_read(void __iomem *base, u8 port, u8 regad, u32 *val)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	u32 data;
30962306a36Sopenharmony_ci	int err;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_READ),
31262306a36Sopenharmony_ci		   base + PCIE_RC_DL_MDIO_ADDR);
31362306a36Sopenharmony_ci	readl(base + PCIE_RC_DL_MDIO_ADDR);
31462306a36Sopenharmony_ci	err = readl_poll_timeout_atomic(base + PCIE_RC_DL_MDIO_RD_DATA, data,
31562306a36Sopenharmony_ci					MDIO_RD_DONE(data), 10, 100);
31662306a36Sopenharmony_ci	*val = FIELD_GET(MDIO_DATA_MASK, data);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return err;
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci/* negative return value indicates error */
32262306a36Sopenharmony_cistatic int brcm_pcie_mdio_write(void __iomem *base, u8 port,
32362306a36Sopenharmony_ci				u8 regad, u16 wrdata)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	u32 data;
32662306a36Sopenharmony_ci	int err;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_WRITE),
32962306a36Sopenharmony_ci		   base + PCIE_RC_DL_MDIO_ADDR);
33062306a36Sopenharmony_ci	readl(base + PCIE_RC_DL_MDIO_ADDR);
33162306a36Sopenharmony_ci	writel(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	err = readl_poll_timeout_atomic(base + PCIE_RC_DL_MDIO_WR_DATA, data,
33462306a36Sopenharmony_ci					MDIO_WT_DONE(data), 10, 100);
33562306a36Sopenharmony_ci	return err;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci/*
33962306a36Sopenharmony_ci * Configures device for Spread Spectrum Clocking (SSC) mode; a negative
34062306a36Sopenharmony_ci * return value indicates error.
34162306a36Sopenharmony_ci */
34262306a36Sopenharmony_cistatic int brcm_pcie_set_ssc(struct brcm_pcie *pcie)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	int pll, ssc;
34562306a36Sopenharmony_ci	int ret;
34662306a36Sopenharmony_ci	u32 tmp;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET,
34962306a36Sopenharmony_ci				   SSC_REGS_ADDR);
35062306a36Sopenharmony_ci	if (ret < 0)
35162306a36Sopenharmony_ci		return ret;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0,
35462306a36Sopenharmony_ci				  SSC_CNTL_OFFSET, &tmp);
35562306a36Sopenharmony_ci	if (ret < 0)
35662306a36Sopenharmony_ci		return ret;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_EN_MASK);
35962306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_VAL_MASK);
36062306a36Sopenharmony_ci	ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0,
36162306a36Sopenharmony_ci				   SSC_CNTL_OFFSET, tmp);
36262306a36Sopenharmony_ci	if (ret < 0)
36362306a36Sopenharmony_ci		return ret;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	usleep_range(1000, 2000);
36662306a36Sopenharmony_ci	ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0,
36762306a36Sopenharmony_ci				  SSC_STATUS_OFFSET, &tmp);
36862306a36Sopenharmony_ci	if (ret < 0)
36962306a36Sopenharmony_ci		return ret;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	ssc = FIELD_GET(SSC_STATUS_SSC_MASK, tmp);
37262306a36Sopenharmony_ci	pll = FIELD_GET(SSC_STATUS_PLL_LOCK_MASK, tmp);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return ssc && pll ? 0 : -EIO;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci/* Limits operation to a specific generation (1, 2, or 3) */
37862306a36Sopenharmony_cistatic void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
38162306a36Sopenharmony_ci	u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen;
38462306a36Sopenharmony_ci	writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	lnkctl2 = (lnkctl2 & ~0xf) | gen;
38762306a36Sopenharmony_ci	writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2);
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
39162306a36Sopenharmony_ci				       unsigned int win, u64 cpu_addr,
39262306a36Sopenharmony_ci				       u64 pcie_addr, u64 size)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	u32 cpu_addr_mb_high, limit_addr_mb_high;
39562306a36Sopenharmony_ci	phys_addr_t cpu_addr_mb, limit_addr_mb;
39662306a36Sopenharmony_ci	int high_addr_shift;
39762306a36Sopenharmony_ci	u32 tmp;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	/* Set the base of the pcie_addr window */
40062306a36Sopenharmony_ci	writel(lower_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_LO(win));
40162306a36Sopenharmony_ci	writel(upper_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_HI(win));
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* Write the addr base & limit lower bits (in MBs) */
40462306a36Sopenharmony_ci	cpu_addr_mb = cpu_addr / SZ_1M;
40562306a36Sopenharmony_ci	limit_addr_mb = (cpu_addr + size - 1) / SZ_1M;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
40862306a36Sopenharmony_ci	u32p_replace_bits(&tmp, cpu_addr_mb,
40962306a36Sopenharmony_ci			  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK);
41062306a36Sopenharmony_ci	u32p_replace_bits(&tmp, limit_addr_mb,
41162306a36Sopenharmony_ci			  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK);
41262306a36Sopenharmony_ci	writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (is_bmips(pcie))
41562306a36Sopenharmony_ci		return;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* Write the cpu & limit addr upper bits */
41862306a36Sopenharmony_ci	high_addr_shift =
41962306a36Sopenharmony_ci		HWEIGHT32(PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	cpu_addr_mb_high = cpu_addr_mb >> high_addr_shift;
42262306a36Sopenharmony_ci	tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_HI(win));
42362306a36Sopenharmony_ci	u32p_replace_bits(&tmp, cpu_addr_mb_high,
42462306a36Sopenharmony_ci			  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK);
42562306a36Sopenharmony_ci	writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_HI(win));
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	limit_addr_mb_high = limit_addr_mb >> high_addr_shift;
42862306a36Sopenharmony_ci	tmp = readl(pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
42962306a36Sopenharmony_ci	u32p_replace_bits(&tmp, limit_addr_mb_high,
43062306a36Sopenharmony_ci			  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK);
43162306a36Sopenharmony_ci	writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic struct irq_chip brcm_msi_irq_chip = {
43562306a36Sopenharmony_ci	.name            = "BRCM STB PCIe MSI",
43662306a36Sopenharmony_ci	.irq_ack         = irq_chip_ack_parent,
43762306a36Sopenharmony_ci	.irq_mask        = pci_msi_mask_irq,
43862306a36Sopenharmony_ci	.irq_unmask      = pci_msi_unmask_irq,
43962306a36Sopenharmony_ci};
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic struct msi_domain_info brcm_msi_domain_info = {
44262306a36Sopenharmony_ci	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
44362306a36Sopenharmony_ci		   MSI_FLAG_MULTI_PCI_MSI),
44462306a36Sopenharmony_ci	.chip	= &brcm_msi_irq_chip,
44562306a36Sopenharmony_ci};
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic void brcm_pcie_msi_isr(struct irq_desc *desc)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
45062306a36Sopenharmony_ci	unsigned long status;
45162306a36Sopenharmony_ci	struct brcm_msi *msi;
45262306a36Sopenharmony_ci	struct device *dev;
45362306a36Sopenharmony_ci	u32 bit;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
45662306a36Sopenharmony_ci	msi = irq_desc_get_handler_data(desc);
45762306a36Sopenharmony_ci	dev = msi->dev;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	status = readl(msi->intr_base + MSI_INT_STATUS);
46062306a36Sopenharmony_ci	status >>= msi->legacy_shift;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	for_each_set_bit(bit, &status, msi->nr) {
46362306a36Sopenharmony_ci		int ret;
46462306a36Sopenharmony_ci		ret = generic_handle_domain_irq(msi->inner_domain, bit);
46562306a36Sopenharmony_ci		if (ret)
46662306a36Sopenharmony_ci			dev_dbg(dev, "unexpected MSI\n");
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	msg->address_lo = lower_32_bits(msi->target_addr);
47762306a36Sopenharmony_ci	msg->address_hi = upper_32_bits(msi->target_addr);
47862306a36Sopenharmony_ci	msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | data->hwirq;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic int brcm_msi_set_affinity(struct irq_data *irq_data,
48262306a36Sopenharmony_ci				 const struct cpumask *mask, bool force)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	return -EINVAL;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic void brcm_msi_ack_irq(struct irq_data *data)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
49062306a36Sopenharmony_ci	const int shift_amt = data->hwirq + msi->legacy_shift;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	writel(1 << shift_amt, msi->intr_base + MSI_INT_CLR);
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic struct irq_chip brcm_msi_bottom_irq_chip = {
49762306a36Sopenharmony_ci	.name			= "BRCM STB MSI",
49862306a36Sopenharmony_ci	.irq_compose_msi_msg	= brcm_msi_compose_msi_msg,
49962306a36Sopenharmony_ci	.irq_set_affinity	= brcm_msi_set_affinity,
50062306a36Sopenharmony_ci	.irq_ack                = brcm_msi_ack_irq,
50162306a36Sopenharmony_ci};
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_cistatic int brcm_msi_alloc(struct brcm_msi *msi, unsigned int nr_irqs)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	int hwirq;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	mutex_lock(&msi->lock);
50862306a36Sopenharmony_ci	hwirq = bitmap_find_free_region(msi->used, msi->nr,
50962306a36Sopenharmony_ci					order_base_2(nr_irqs));
51062306a36Sopenharmony_ci	mutex_unlock(&msi->lock);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	return hwirq;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq,
51662306a36Sopenharmony_ci			  unsigned int nr_irqs)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	mutex_lock(&msi->lock);
51962306a36Sopenharmony_ci	bitmap_release_region(msi->used, hwirq, order_base_2(nr_irqs));
52062306a36Sopenharmony_ci	mutex_unlock(&msi->lock);
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_cistatic int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
52462306a36Sopenharmony_ci				 unsigned int nr_irqs, void *args)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct brcm_msi *msi = domain->host_data;
52762306a36Sopenharmony_ci	int hwirq, i;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	hwirq = brcm_msi_alloc(msi, nr_irqs);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (hwirq < 0)
53262306a36Sopenharmony_ci		return hwirq;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	for (i = 0; i < nr_irqs; i++)
53562306a36Sopenharmony_ci		irq_domain_set_info(domain, virq + i, hwirq + i,
53662306a36Sopenharmony_ci				    &brcm_msi_bottom_irq_chip, domain->host_data,
53762306a36Sopenharmony_ci				    handle_edge_irq, NULL, NULL);
53862306a36Sopenharmony_ci	return 0;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic void brcm_irq_domain_free(struct irq_domain *domain,
54262306a36Sopenharmony_ci				 unsigned int virq, unsigned int nr_irqs)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
54562306a36Sopenharmony_ci	struct brcm_msi *msi = irq_data_get_irq_chip_data(d);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	brcm_msi_free(msi, d->hwirq, nr_irqs);
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic const struct irq_domain_ops msi_domain_ops = {
55162306a36Sopenharmony_ci	.alloc	= brcm_irq_domain_alloc,
55262306a36Sopenharmony_ci	.free	= brcm_irq_domain_free,
55362306a36Sopenharmony_ci};
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic int brcm_allocate_domains(struct brcm_msi *msi)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np);
55862306a36Sopenharmony_ci	struct device *dev = msi->dev;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	msi->inner_domain = irq_domain_add_linear(NULL, msi->nr, &msi_domain_ops, msi);
56162306a36Sopenharmony_ci	if (!msi->inner_domain) {
56262306a36Sopenharmony_ci		dev_err(dev, "failed to create IRQ domain\n");
56362306a36Sopenharmony_ci		return -ENOMEM;
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	msi->msi_domain = pci_msi_create_irq_domain(fwnode,
56762306a36Sopenharmony_ci						    &brcm_msi_domain_info,
56862306a36Sopenharmony_ci						    msi->inner_domain);
56962306a36Sopenharmony_ci	if (!msi->msi_domain) {
57062306a36Sopenharmony_ci		dev_err(dev, "failed to create MSI domain\n");
57162306a36Sopenharmony_ci		irq_domain_remove(msi->inner_domain);
57262306a36Sopenharmony_ci		return -ENOMEM;
57362306a36Sopenharmony_ci	}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	return 0;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic void brcm_free_domains(struct brcm_msi *msi)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	irq_domain_remove(msi->msi_domain);
58162306a36Sopenharmony_ci	irq_domain_remove(msi->inner_domain);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic void brcm_msi_remove(struct brcm_pcie *pcie)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	struct brcm_msi *msi = pcie->msi;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (!msi)
58962306a36Sopenharmony_ci		return;
59062306a36Sopenharmony_ci	irq_set_chained_handler_and_data(msi->irq, NULL, NULL);
59162306a36Sopenharmony_ci	brcm_free_domains(msi);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic void brcm_msi_set_regs(struct brcm_msi *msi)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	u32 val = msi->legacy ? BRCM_INT_PCI_MSI_LEGACY_MASK :
59762306a36Sopenharmony_ci				BRCM_INT_PCI_MSI_MASK;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	writel(val, msi->intr_base + MSI_INT_MASK_CLR);
60062306a36Sopenharmony_ci	writel(val, msi->intr_base + MSI_INT_CLR);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/*
60362306a36Sopenharmony_ci	 * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI
60462306a36Sopenharmony_ci	 * enable, which we set to 1.
60562306a36Sopenharmony_ci	 */
60662306a36Sopenharmony_ci	writel(lower_32_bits(msi->target_addr) | 0x1,
60762306a36Sopenharmony_ci	       msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO);
60862306a36Sopenharmony_ci	writel(upper_32_bits(msi->target_addr),
60962306a36Sopenharmony_ci	       msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	val = msi->legacy ? PCIE_MISC_MSI_DATA_CONFIG_VAL_8 : PCIE_MISC_MSI_DATA_CONFIG_VAL_32;
61262306a36Sopenharmony_ci	writel(val, msi->base + PCIE_MISC_MSI_DATA_CONFIG);
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct brcm_msi *msi;
61862306a36Sopenharmony_ci	int irq, ret;
61962306a36Sopenharmony_ci	struct device *dev = pcie->dev;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	irq = irq_of_parse_and_map(dev->of_node, 1);
62262306a36Sopenharmony_ci	if (irq <= 0) {
62362306a36Sopenharmony_ci		dev_err(dev, "cannot map MSI interrupt\n");
62462306a36Sopenharmony_ci		return -ENODEV;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL);
62862306a36Sopenharmony_ci	if (!msi)
62962306a36Sopenharmony_ci		return -ENOMEM;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	mutex_init(&msi->lock);
63262306a36Sopenharmony_ci	msi->dev = dev;
63362306a36Sopenharmony_ci	msi->base = pcie->base;
63462306a36Sopenharmony_ci	msi->np = pcie->np;
63562306a36Sopenharmony_ci	msi->target_addr = pcie->msi_target_addr;
63662306a36Sopenharmony_ci	msi->irq = irq;
63762306a36Sopenharmony_ci	msi->legacy = pcie->hw_rev < BRCM_PCIE_HW_REV_33;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	/*
64062306a36Sopenharmony_ci	 * Sanity check to make sure that the 'used' bitmap in struct brcm_msi
64162306a36Sopenharmony_ci	 * is large enough.
64262306a36Sopenharmony_ci	 */
64362306a36Sopenharmony_ci	BUILD_BUG_ON(BRCM_INT_PCI_MSI_LEGACY_NR > BRCM_INT_PCI_MSI_NR);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	if (msi->legacy) {
64662306a36Sopenharmony_ci		msi->intr_base = msi->base + PCIE_INTR2_CPU_BASE;
64762306a36Sopenharmony_ci		msi->nr = BRCM_INT_PCI_MSI_LEGACY_NR;
64862306a36Sopenharmony_ci		msi->legacy_shift = 24;
64962306a36Sopenharmony_ci	} else {
65062306a36Sopenharmony_ci		msi->intr_base = msi->base + PCIE_MSI_INTR2_BASE;
65162306a36Sopenharmony_ci		msi->nr = BRCM_INT_PCI_MSI_NR;
65262306a36Sopenharmony_ci		msi->legacy_shift = 0;
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	ret = brcm_allocate_domains(msi);
65662306a36Sopenharmony_ci	if (ret)
65762306a36Sopenharmony_ci		return ret;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	irq_set_chained_handler_and_data(msi->irq, brcm_pcie_msi_isr, msi);
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	brcm_msi_set_regs(msi);
66262306a36Sopenharmony_ci	pcie->msi = msi;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	return 0;
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci/* The controller is capable of serving in both RC and EP roles */
66862306a36Sopenharmony_cistatic bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	void __iomem *base = pcie->base;
67162306a36Sopenharmony_ci	u32 val = readl(base + PCIE_MISC_PCIE_STATUS);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val);
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic bool brcm_pcie_link_up(struct brcm_pcie *pcie)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS);
67962306a36Sopenharmony_ci	u32 dla = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK, val);
68062306a36Sopenharmony_ci	u32 plu = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK, val);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	return dla && plu;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic void __iomem *brcm_pcie_map_bus(struct pci_bus *bus,
68662306a36Sopenharmony_ci				       unsigned int devfn, int where)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	struct brcm_pcie *pcie = bus->sysdata;
68962306a36Sopenharmony_ci	void __iomem *base = pcie->base;
69062306a36Sopenharmony_ci	int idx;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	/* Accesses to the RC go right to the RC registers if !devfn */
69362306a36Sopenharmony_ci	if (pci_is_root_bus(bus))
69462306a36Sopenharmony_ci		return devfn ? NULL : base + PCIE_ECAM_REG(where);
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	/* An access to our HW w/o link-up will cause a CPU Abort */
69762306a36Sopenharmony_ci	if (!brcm_pcie_link_up(pcie))
69862306a36Sopenharmony_ci		return NULL;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	/* For devices, write to the config space index register */
70162306a36Sopenharmony_ci	idx = PCIE_ECAM_OFFSET(bus->number, devfn, 0);
70262306a36Sopenharmony_ci	writel(idx, pcie->base + PCIE_EXT_CFG_INDEX);
70362306a36Sopenharmony_ci	return base + PCIE_EXT_CFG_DATA + PCIE_ECAM_REG(where);
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic void __iomem *brcm7425_pcie_map_bus(struct pci_bus *bus,
70762306a36Sopenharmony_ci					   unsigned int devfn, int where)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct brcm_pcie *pcie = bus->sysdata;
71062306a36Sopenharmony_ci	void __iomem *base = pcie->base;
71162306a36Sopenharmony_ci	int idx;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	/* Accesses to the RC go right to the RC registers if !devfn */
71462306a36Sopenharmony_ci	if (pci_is_root_bus(bus))
71562306a36Sopenharmony_ci		return devfn ? NULL : base + PCIE_ECAM_REG(where);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	/* An access to our HW w/o link-up will cause a CPU Abort */
71862306a36Sopenharmony_ci	if (!brcm_pcie_link_up(pcie))
71962306a36Sopenharmony_ci		return NULL;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	/* For devices, write to the config space index register */
72262306a36Sopenharmony_ci	idx = PCIE_ECAM_OFFSET(bus->number, devfn, where);
72362306a36Sopenharmony_ci	writel(idx, base + IDX_ADDR(pcie));
72462306a36Sopenharmony_ci	return base + DATA_ADDR(pcie);
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	u32 tmp, mask =  RGR1_SW_INIT_1_INIT_GENERIC_MASK;
73062306a36Sopenharmony_ci	u32 shift = RGR1_SW_INIT_1_INIT_GENERIC_SHIFT;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
73362306a36Sopenharmony_ci	tmp = (tmp & ~mask) | ((val << shift) & mask);
73462306a36Sopenharmony_ci	writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	u32 tmp, mask =  RGR1_SW_INIT_1_INIT_7278_MASK;
74062306a36Sopenharmony_ci	u32 shift = RGR1_SW_INIT_1_INIT_7278_SHIFT;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
74362306a36Sopenharmony_ci	tmp = (tmp & ~mask) | ((val << shift) & mask);
74462306a36Sopenharmony_ci	writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_cistatic void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	if (WARN_ONCE(!pcie->perst_reset, "missing PERST# reset controller\n"))
75062306a36Sopenharmony_ci		return;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (val)
75362306a36Sopenharmony_ci		reset_control_assert(pcie->perst_reset);
75462306a36Sopenharmony_ci	else
75562306a36Sopenharmony_ci		reset_control_deassert(pcie->perst_reset);
75662306a36Sopenharmony_ci}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cistatic void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	u32 tmp;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/* Perst bit has moved and assert value is 0 */
76362306a36Sopenharmony_ci	tmp = readl(pcie->base + PCIE_MISC_PCIE_CTRL);
76462306a36Sopenharmony_ci	u32p_replace_bits(&tmp, !val, PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK);
76562306a36Sopenharmony_ci	writel(tmp, pcie->base +  PCIE_MISC_PCIE_CTRL);
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_cistatic void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
76962306a36Sopenharmony_ci{
77062306a36Sopenharmony_ci	u32 tmp;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
77362306a36Sopenharmony_ci	u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_PERST_MASK);
77462306a36Sopenharmony_ci	writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
77562306a36Sopenharmony_ci}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cistatic int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
77862306a36Sopenharmony_ci							u64 *rc_bar2_size,
77962306a36Sopenharmony_ci							u64 *rc_bar2_offset)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
78262306a36Sopenharmony_ci	struct resource_entry *entry;
78362306a36Sopenharmony_ci	struct device *dev = pcie->dev;
78462306a36Sopenharmony_ci	u64 lowest_pcie_addr = ~(u64)0;
78562306a36Sopenharmony_ci	int ret, i = 0;
78662306a36Sopenharmony_ci	u64 size = 0;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	resource_list_for_each_entry(entry, &bridge->dma_ranges) {
78962306a36Sopenharmony_ci		u64 pcie_beg = entry->res->start - entry->offset;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci		size += entry->res->end - entry->res->start + 1;
79262306a36Sopenharmony_ci		if (pcie_beg < lowest_pcie_addr)
79362306a36Sopenharmony_ci			lowest_pcie_addr = pcie_beg;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (lowest_pcie_addr == ~(u64)0) {
79762306a36Sopenharmony_ci		dev_err(dev, "DT node has no dma-ranges\n");
79862306a36Sopenharmony_ci		return -EINVAL;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	ret = of_property_read_variable_u64_array(pcie->np, "brcm,scb-sizes", pcie->memc_size, 1,
80262306a36Sopenharmony_ci						  PCIE_BRCM_MAX_MEMC);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (ret <= 0) {
80562306a36Sopenharmony_ci		/* Make an educated guess */
80662306a36Sopenharmony_ci		pcie->num_memc = 1;
80762306a36Sopenharmony_ci		pcie->memc_size[0] = 1ULL << fls64(size - 1);
80862306a36Sopenharmony_ci	} else {
80962306a36Sopenharmony_ci		pcie->num_memc = ret;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	/* Each memc is viewed through a "port" that is a power of 2 */
81362306a36Sopenharmony_ci	for (i = 0, size = 0; i < pcie->num_memc; i++)
81462306a36Sopenharmony_ci		size += pcie->memc_size[i];
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/* System memory starts at this address in PCIe-space */
81762306a36Sopenharmony_ci	*rc_bar2_offset = lowest_pcie_addr;
81862306a36Sopenharmony_ci	/* The sum of all memc views must also be a power of 2 */
81962306a36Sopenharmony_ci	*rc_bar2_size = 1ULL << fls64(size - 1);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	/*
82262306a36Sopenharmony_ci	 * We validate the inbound memory view even though we should trust
82362306a36Sopenharmony_ci	 * whatever the device-tree provides. This is because of an HW issue on
82462306a36Sopenharmony_ci	 * early Raspberry Pi 4's revisions (bcm2711). It turns out its
82562306a36Sopenharmony_ci	 * firmware has to dynamically edit dma-ranges due to a bug on the
82662306a36Sopenharmony_ci	 * PCIe controller integration, which prohibits any access above the
82762306a36Sopenharmony_ci	 * lower 3GB of memory. Given this, we decided to keep the dma-ranges
82862306a36Sopenharmony_ci	 * in check, avoiding hard to debug device-tree related issues in the
82962306a36Sopenharmony_ci	 * future:
83062306a36Sopenharmony_ci	 *
83162306a36Sopenharmony_ci	 * The PCIe host controller by design must set the inbound viewport to
83262306a36Sopenharmony_ci	 * be a contiguous arrangement of all of the system's memory.  In
83362306a36Sopenharmony_ci	 * addition, its size mut be a power of two.  To further complicate
83462306a36Sopenharmony_ci	 * matters, the viewport must start on a pcie-address that is aligned
83562306a36Sopenharmony_ci	 * on a multiple of its size.  If a portion of the viewport does not
83662306a36Sopenharmony_ci	 * represent system memory -- e.g. 3GB of memory requires a 4GB
83762306a36Sopenharmony_ci	 * viewport -- we can map the outbound memory in or after 3GB and even
83862306a36Sopenharmony_ci	 * though the viewport will overlap the outbound memory the controller
83962306a36Sopenharmony_ci	 * will know to send outbound memory downstream and everything else
84062306a36Sopenharmony_ci	 * upstream.
84162306a36Sopenharmony_ci	 *
84262306a36Sopenharmony_ci	 * For example:
84362306a36Sopenharmony_ci	 *
84462306a36Sopenharmony_ci	 * - The best-case scenario, memory up to 3GB, is to place the inbound
84562306a36Sopenharmony_ci	 *   region in the first 4GB of pcie-space, as some legacy devices can
84662306a36Sopenharmony_ci	 *   only address 32bits. We would also like to put the MSI under 4GB
84762306a36Sopenharmony_ci	 *   as well, since some devices require a 32bit MSI target address.
84862306a36Sopenharmony_ci	 *
84962306a36Sopenharmony_ci	 * - If the system memory is 4GB or larger we cannot start the inbound
85062306a36Sopenharmony_ci	 *   region at location 0 (since we have to allow some space for
85162306a36Sopenharmony_ci	 *   outbound memory @ 3GB). So instead it will  start at the 1x
85262306a36Sopenharmony_ci	 *   multiple of its size
85362306a36Sopenharmony_ci	 */
85462306a36Sopenharmony_ci	if (!*rc_bar2_size || (*rc_bar2_offset & (*rc_bar2_size - 1)) ||
85562306a36Sopenharmony_ci	    (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) {
85662306a36Sopenharmony_ci		dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n",
85762306a36Sopenharmony_ci			*rc_bar2_size, *rc_bar2_offset);
85862306a36Sopenharmony_ci		return -EINVAL;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	return 0;
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic int brcm_pcie_setup(struct brcm_pcie *pcie)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	u64 rc_bar2_offset, rc_bar2_size;
86762306a36Sopenharmony_ci	void __iomem *base = pcie->base;
86862306a36Sopenharmony_ci	struct pci_host_bridge *bridge;
86962306a36Sopenharmony_ci	struct resource_entry *entry;
87062306a36Sopenharmony_ci	u32 tmp, burst, aspm_support;
87162306a36Sopenharmony_ci	int num_out_wins = 0;
87262306a36Sopenharmony_ci	int ret, memc;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	/* Reset the bridge */
87562306a36Sopenharmony_ci	pcie->bridge_sw_init_set(pcie, 1);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* Ensure that PERST# is asserted; some bootloaders may deassert it. */
87862306a36Sopenharmony_ci	if (pcie->type == BCM2711)
87962306a36Sopenharmony_ci		pcie->perst_set(pcie, 1);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	usleep_range(100, 200);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* Take the bridge out of reset */
88462306a36Sopenharmony_ci	pcie->bridge_sw_init_set(pcie, 0);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
88762306a36Sopenharmony_ci	if (is_bmips(pcie))
88862306a36Sopenharmony_ci		tmp &= ~PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
88962306a36Sopenharmony_ci	else
89062306a36Sopenharmony_ci		tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
89162306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
89262306a36Sopenharmony_ci	/* Wait for SerDes to be stable */
89362306a36Sopenharmony_ci	usleep_range(100, 200);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	/*
89662306a36Sopenharmony_ci	 * SCB_MAX_BURST_SIZE is a two bit field.  For GENERIC chips it
89762306a36Sopenharmony_ci	 * is encoded as 0=128, 1=256, 2=512, 3=Rsvd, for BCM7278 it
89862306a36Sopenharmony_ci	 * is encoded as 0=Rsvd, 1=128, 2=256, 3=512.
89962306a36Sopenharmony_ci	 */
90062306a36Sopenharmony_ci	if (is_bmips(pcie))
90162306a36Sopenharmony_ci		burst = 0x1; /* 256 bytes */
90262306a36Sopenharmony_ci	else if (pcie->type == BCM2711)
90362306a36Sopenharmony_ci		burst = 0x0; /* 128 bytes */
90462306a36Sopenharmony_ci	else if (pcie->type == BCM7278)
90562306a36Sopenharmony_ci		burst = 0x3; /* 512 bytes */
90662306a36Sopenharmony_ci	else
90762306a36Sopenharmony_ci		burst = 0x2; /* 512 bytes */
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/*
91062306a36Sopenharmony_ci	 * Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN,
91162306a36Sopenharmony_ci	 * RCB_MPS_MODE, RCB_64B_MODE
91262306a36Sopenharmony_ci	 */
91362306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_MISC_CTRL);
91462306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK);
91562306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK);
91662306a36Sopenharmony_ci	u32p_replace_bits(&tmp, burst, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
91762306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK);
91862306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK);
91962306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_MISC_CTRL);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size,
92262306a36Sopenharmony_ci						    &rc_bar2_offset);
92362306a36Sopenharmony_ci	if (ret)
92462306a36Sopenharmony_ci		return ret;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	tmp = lower_32_bits(rc_bar2_offset);
92762306a36Sopenharmony_ci	u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size),
92862306a36Sopenharmony_ci			  PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK);
92962306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO);
93062306a36Sopenharmony_ci	writel(upper_32_bits(rc_bar2_offset),
93162306a36Sopenharmony_ci	       base + PCIE_MISC_RC_BAR2_CONFIG_HI);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_MISC_CTRL);
93462306a36Sopenharmony_ci	for (memc = 0; memc < pcie->num_memc; memc++) {
93562306a36Sopenharmony_ci		u32 scb_size_val = ilog2(pcie->memc_size[memc]) - 15;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci		if (memc == 0)
93862306a36Sopenharmony_ci			u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(0));
93962306a36Sopenharmony_ci		else if (memc == 1)
94062306a36Sopenharmony_ci			u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(1));
94162306a36Sopenharmony_ci		else if (memc == 2)
94262306a36Sopenharmony_ci			u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(2));
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_MISC_CTRL);
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	/*
94762306a36Sopenharmony_ci	 * We ideally want the MSI target address to be located in the 32bit
94862306a36Sopenharmony_ci	 * addressable memory area. Some devices might depend on it. This is
94962306a36Sopenharmony_ci	 * possible either when the inbound window is located above the lower
95062306a36Sopenharmony_ci	 * 4GB or when the inbound area is smaller than 4GB (taking into
95162306a36Sopenharmony_ci	 * account the rounding-up we're forced to perform).
95262306a36Sopenharmony_ci	 */
95362306a36Sopenharmony_ci	if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G)
95462306a36Sopenharmony_ci		pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
95562306a36Sopenharmony_ci	else
95662306a36Sopenharmony_ci		pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (!brcm_pcie_rc_mode(pcie)) {
95962306a36Sopenharmony_ci		dev_err(pcie->dev, "PCIe RC controller misconfigured as Endpoint\n");
96062306a36Sopenharmony_ci		return -EINVAL;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/* disable the PCIe->GISB memory window (RC_BAR1) */
96462306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
96562306a36Sopenharmony_ci	tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
96662306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_RC_BAR1_CONFIG_LO);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	/* disable the PCIe->SCB memory window (RC_BAR3) */
96962306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_RC_BAR3_CONFIG_LO);
97062306a36Sopenharmony_ci	tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK;
97162306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	/* Don't advertise L0s capability if 'aspm-no-l0s' */
97462306a36Sopenharmony_ci	aspm_support = PCIE_LINK_STATE_L1;
97562306a36Sopenharmony_ci	if (!of_property_read_bool(pcie->np, "aspm-no-l0s"))
97662306a36Sopenharmony_ci		aspm_support |= PCIE_LINK_STATE_L0S;
97762306a36Sopenharmony_ci	tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
97862306a36Sopenharmony_ci	u32p_replace_bits(&tmp, aspm_support,
97962306a36Sopenharmony_ci		PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
98062306a36Sopenharmony_ci	writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	/*
98362306a36Sopenharmony_ci	 * For config space accesses on the RC, show the right class for
98462306a36Sopenharmony_ci	 * a PCIe-PCIe bridge (the default setting is to be EP mode).
98562306a36Sopenharmony_ci	 */
98662306a36Sopenharmony_ci	tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3);
98762306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 0x060400,
98862306a36Sopenharmony_ci			  PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK);
98962306a36Sopenharmony_ci	writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	bridge = pci_host_bridge_from_priv(pcie);
99262306a36Sopenharmony_ci	resource_list_for_each_entry(entry, &bridge->windows) {
99362306a36Sopenharmony_ci		struct resource *res = entry->res;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci		if (resource_type(res) != IORESOURCE_MEM)
99662306a36Sopenharmony_ci			continue;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci		if (num_out_wins >= BRCM_NUM_PCIE_OUT_WINS) {
99962306a36Sopenharmony_ci			dev_err(pcie->dev, "too many outbound wins\n");
100062306a36Sopenharmony_ci			return -EINVAL;
100162306a36Sopenharmony_ci		}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci		if (is_bmips(pcie)) {
100462306a36Sopenharmony_ci			u64 start = res->start;
100562306a36Sopenharmony_ci			unsigned int j, nwins = resource_size(res) / SZ_128M;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci			/* bmips PCIe outbound windows have a 128MB max size */
100862306a36Sopenharmony_ci			if (nwins > BRCM_NUM_PCIE_OUT_WINS)
100962306a36Sopenharmony_ci				nwins = BRCM_NUM_PCIE_OUT_WINS;
101062306a36Sopenharmony_ci			for (j = 0; j < nwins; j++, start += SZ_128M)
101162306a36Sopenharmony_ci				brcm_pcie_set_outbound_win(pcie, j, start,
101262306a36Sopenharmony_ci							   start - entry->offset,
101362306a36Sopenharmony_ci							   SZ_128M);
101462306a36Sopenharmony_ci			break;
101562306a36Sopenharmony_ci		}
101662306a36Sopenharmony_ci		brcm_pcie_set_outbound_win(pcie, num_out_wins, res->start,
101762306a36Sopenharmony_ci					   res->start - entry->offset,
101862306a36Sopenharmony_ci					   resource_size(res));
101962306a36Sopenharmony_ci		num_out_wins++;
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	/* PCIe->SCB endian mode for BAR */
102362306a36Sopenharmony_ci	tmp = readl(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
102462306a36Sopenharmony_ci	u32p_replace_bits(&tmp, PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN,
102562306a36Sopenharmony_ci		PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK);
102662306a36Sopenharmony_ci	writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1);
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	return 0;
102962306a36Sopenharmony_ci}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic int brcm_pcie_start_link(struct brcm_pcie *pcie)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct device *dev = pcie->dev;
103462306a36Sopenharmony_ci	void __iomem *base = pcie->base;
103562306a36Sopenharmony_ci	u16 nlw, cls, lnksta;
103662306a36Sopenharmony_ci	bool ssc_good = false;
103762306a36Sopenharmony_ci	u32 tmp;
103862306a36Sopenharmony_ci	int ret, i;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/* Unassert the fundamental reset */
104162306a36Sopenharmony_ci	pcie->perst_set(pcie, 0);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	/*
104462306a36Sopenharmony_ci	 * Wait for 100ms after PERST# deassertion; see PCIe CEM specification
104562306a36Sopenharmony_ci	 * sections 2.2, PCIe r5.0, 6.6.1.
104662306a36Sopenharmony_ci	 */
104762306a36Sopenharmony_ci	msleep(100);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	/*
105062306a36Sopenharmony_ci	 * Give the RC/EP even more time to wake up, before trying to
105162306a36Sopenharmony_ci	 * configure RC.  Intermittently check status for link-up, up to a
105262306a36Sopenharmony_ci	 * total of 100ms.
105362306a36Sopenharmony_ci	 */
105462306a36Sopenharmony_ci	for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5)
105562306a36Sopenharmony_ci		msleep(5);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	if (!brcm_pcie_link_up(pcie)) {
105862306a36Sopenharmony_ci		dev_err(dev, "link down\n");
105962306a36Sopenharmony_ci		return -ENODEV;
106062306a36Sopenharmony_ci	}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	if (pcie->gen)
106362306a36Sopenharmony_ci		brcm_pcie_set_gen(pcie, pcie->gen);
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	if (pcie->ssc) {
106662306a36Sopenharmony_ci		ret = brcm_pcie_set_ssc(pcie);
106762306a36Sopenharmony_ci		if (ret == 0)
106862306a36Sopenharmony_ci			ssc_good = true;
106962306a36Sopenharmony_ci		else
107062306a36Sopenharmony_ci			dev_err(dev, "failed attempt to enter ssc mode\n");
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA);
107462306a36Sopenharmony_ci	cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta);
107562306a36Sopenharmony_ci	nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
107662306a36Sopenharmony_ci	dev_info(dev, "link up, %s x%u %s\n",
107762306a36Sopenharmony_ci		 pci_speed_string(pcie_link_speed[cls]), nlw,
107862306a36Sopenharmony_ci		 ssc_good ? "(SSC)" : "(!SSC)");
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	/*
108162306a36Sopenharmony_ci	 * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1
108262306a36Sopenharmony_ci	 * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1.
108362306a36Sopenharmony_ci	 */
108462306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
108562306a36Sopenharmony_ci	tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
108662306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	return 0;
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic const char * const supplies[] = {
109262306a36Sopenharmony_ci	"vpcie3v3",
109362306a36Sopenharmony_ci	"vpcie3v3aux",
109462306a36Sopenharmony_ci	"vpcie12v",
109562306a36Sopenharmony_ci};
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_cistatic void *alloc_subdev_regulators(struct device *dev)
109862306a36Sopenharmony_ci{
109962306a36Sopenharmony_ci	const size_t size = sizeof(struct subdev_regulators) +
110062306a36Sopenharmony_ci		sizeof(struct regulator_bulk_data) * ARRAY_SIZE(supplies);
110162306a36Sopenharmony_ci	struct subdev_regulators *sr;
110262306a36Sopenharmony_ci	int i;
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci	sr = devm_kzalloc(dev, size, GFP_KERNEL);
110562306a36Sopenharmony_ci	if (sr) {
110662306a36Sopenharmony_ci		sr->num_supplies = ARRAY_SIZE(supplies);
110762306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(supplies); i++)
110862306a36Sopenharmony_ci			sr->supplies[i].supply = supplies[i];
110962306a36Sopenharmony_ci	}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	return sr;
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cistatic int brcm_pcie_add_bus(struct pci_bus *bus)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	struct brcm_pcie *pcie = bus->sysdata;
111762306a36Sopenharmony_ci	struct device *dev = &bus->dev;
111862306a36Sopenharmony_ci	struct subdev_regulators *sr;
111962306a36Sopenharmony_ci	int ret;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (!bus->parent || !pci_is_root_bus(bus->parent))
112262306a36Sopenharmony_ci		return 0;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	if (dev->of_node) {
112562306a36Sopenharmony_ci		sr = alloc_subdev_regulators(dev);
112662306a36Sopenharmony_ci		if (!sr) {
112762306a36Sopenharmony_ci			dev_info(dev, "Can't allocate regulators for downstream device\n");
112862306a36Sopenharmony_ci			goto no_regulators;
112962306a36Sopenharmony_ci		}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci		pcie->sr = sr;
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		ret = regulator_bulk_get(dev, sr->num_supplies, sr->supplies);
113462306a36Sopenharmony_ci		if (ret) {
113562306a36Sopenharmony_ci			dev_info(dev, "No regulators for downstream device\n");
113662306a36Sopenharmony_ci			goto no_regulators;
113762306a36Sopenharmony_ci		}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci		ret = regulator_bulk_enable(sr->num_supplies, sr->supplies);
114062306a36Sopenharmony_ci		if (ret) {
114162306a36Sopenharmony_ci			dev_err(dev, "Can't enable regulators for downstream device\n");
114262306a36Sopenharmony_ci			regulator_bulk_free(sr->num_supplies, sr->supplies);
114362306a36Sopenharmony_ci			pcie->sr = NULL;
114462306a36Sopenharmony_ci		}
114562306a36Sopenharmony_ci	}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cino_regulators:
114862306a36Sopenharmony_ci	brcm_pcie_start_link(pcie);
114962306a36Sopenharmony_ci	return 0;
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_cistatic void brcm_pcie_remove_bus(struct pci_bus *bus)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	struct brcm_pcie *pcie = bus->sysdata;
115562306a36Sopenharmony_ci	struct subdev_regulators *sr = pcie->sr;
115662306a36Sopenharmony_ci	struct device *dev = &bus->dev;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (!sr)
115962306a36Sopenharmony_ci		return;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	if (regulator_bulk_disable(sr->num_supplies, sr->supplies))
116262306a36Sopenharmony_ci		dev_err(dev, "Failed to disable regulators for downstream device\n");
116362306a36Sopenharmony_ci	regulator_bulk_free(sr->num_supplies, sr->supplies);
116462306a36Sopenharmony_ci	pcie->sr = NULL;
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci/* L23 is a low-power PCIe link state */
116862306a36Sopenharmony_cistatic void brcm_pcie_enter_l23(struct brcm_pcie *pcie)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	void __iomem *base = pcie->base;
117162306a36Sopenharmony_ci	int l23, i;
117262306a36Sopenharmony_ci	u32 tmp;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	/* Assert request for L23 */
117562306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_PCIE_CTRL);
117662306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 1, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK);
117762306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_PCIE_CTRL);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	/* Wait up to 36 msec for L23 */
118062306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_PCIE_STATUS);
118162306a36Sopenharmony_ci	l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, tmp);
118262306a36Sopenharmony_ci	for (i = 0; i < 15 && !l23; i++) {
118362306a36Sopenharmony_ci		usleep_range(2000, 2400);
118462306a36Sopenharmony_ci		tmp = readl(base + PCIE_MISC_PCIE_STATUS);
118562306a36Sopenharmony_ci		l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK,
118662306a36Sopenharmony_ci				tmp);
118762306a36Sopenharmony_ci	}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	if (!l23)
119062306a36Sopenharmony_ci		dev_err(pcie->dev, "failed to enter low-power link state\n");
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_cistatic int brcm_phy_cntl(struct brcm_pcie *pcie, const int start)
119462306a36Sopenharmony_ci{
119562306a36Sopenharmony_ci	static const u32 shifts[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = {
119662306a36Sopenharmony_ci		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT,
119762306a36Sopenharmony_ci		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT,
119862306a36Sopenharmony_ci		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_SHIFT,};
119962306a36Sopenharmony_ci	static const u32 masks[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = {
120062306a36Sopenharmony_ci		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_MASK,
120162306a36Sopenharmony_ci		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_MASK,
120262306a36Sopenharmony_ci		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_MASK,};
120362306a36Sopenharmony_ci	const int beg = start ? 0 : PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS - 1;
120462306a36Sopenharmony_ci	const int end = start ? PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS : -1;
120562306a36Sopenharmony_ci	u32 tmp, combined_mask = 0;
120662306a36Sopenharmony_ci	u32 val;
120762306a36Sopenharmony_ci	void __iomem *base = pcie->base;
120862306a36Sopenharmony_ci	int i, ret;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	for (i = beg; i != end; start ? i++ : i--) {
121162306a36Sopenharmony_ci		val = start ? BIT_MASK(shifts[i]) : 0;
121262306a36Sopenharmony_ci		tmp = readl(base + PCIE_DVT_PMU_PCIE_PHY_CTRL);
121362306a36Sopenharmony_ci		tmp = (tmp & ~masks[i]) | (val & masks[i]);
121462306a36Sopenharmony_ci		writel(tmp, base + PCIE_DVT_PMU_PCIE_PHY_CTRL);
121562306a36Sopenharmony_ci		usleep_range(50, 200);
121662306a36Sopenharmony_ci		combined_mask |= masks[i];
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	tmp = readl(base + PCIE_DVT_PMU_PCIE_PHY_CTRL);
122062306a36Sopenharmony_ci	val = start ? combined_mask : 0;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	ret = (tmp & combined_mask) == val ? 0 : -EIO;
122362306a36Sopenharmony_ci	if (ret)
122462306a36Sopenharmony_ci		dev_err(pcie->dev, "failed to %s phy\n", (start ? "start" : "stop"));
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	return ret;
122762306a36Sopenharmony_ci}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_cistatic inline int brcm_phy_start(struct brcm_pcie *pcie)
123062306a36Sopenharmony_ci{
123162306a36Sopenharmony_ci	return pcie->rescal ? brcm_phy_cntl(pcie, 1) : 0;
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cistatic inline int brcm_phy_stop(struct brcm_pcie *pcie)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	return pcie->rescal ? brcm_phy_cntl(pcie, 0) : 0;
123762306a36Sopenharmony_ci}
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic void brcm_pcie_turn_off(struct brcm_pcie *pcie)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	void __iomem *base = pcie->base;
124262306a36Sopenharmony_ci	int tmp;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	if (brcm_pcie_link_up(pcie))
124562306a36Sopenharmony_ci		brcm_pcie_enter_l23(pcie);
124662306a36Sopenharmony_ci	/* Assert fundamental reset */
124762306a36Sopenharmony_ci	pcie->perst_set(pcie, 1);
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci	/* Deassert request for L23 in case it was asserted */
125062306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_PCIE_CTRL);
125162306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 0, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK);
125262306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_PCIE_CTRL);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	/* Turn off SerDes */
125562306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
125662306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
125762306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	/* Shutdown PCIe bridge */
126062306a36Sopenharmony_ci	pcie->bridge_sw_init_set(pcie, 1);
126162306a36Sopenharmony_ci}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistatic int pci_dev_may_wakeup(struct pci_dev *dev, void *data)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	bool *ret = data;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	if (device_may_wakeup(&dev->dev)) {
126862306a36Sopenharmony_ci		*ret = true;
126962306a36Sopenharmony_ci		dev_info(&dev->dev, "Possible wake-up device; regulators will not be disabled\n");
127062306a36Sopenharmony_ci	}
127162306a36Sopenharmony_ci	return (int) *ret;
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic int brcm_pcie_suspend_noirq(struct device *dev)
127562306a36Sopenharmony_ci{
127662306a36Sopenharmony_ci	struct brcm_pcie *pcie = dev_get_drvdata(dev);
127762306a36Sopenharmony_ci	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
127862306a36Sopenharmony_ci	int ret;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	brcm_pcie_turn_off(pcie);
128162306a36Sopenharmony_ci	/*
128262306a36Sopenharmony_ci	 * If brcm_phy_stop() returns an error, just dev_err(). If we
128362306a36Sopenharmony_ci	 * return the error it will cause the suspend to fail and this is a
128462306a36Sopenharmony_ci	 * forgivable offense that will probably be erased on resume.
128562306a36Sopenharmony_ci	 */
128662306a36Sopenharmony_ci	if (brcm_phy_stop(pcie))
128762306a36Sopenharmony_ci		dev_err(dev, "Could not stop phy for suspend\n");
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	ret = reset_control_rearm(pcie->rescal);
129062306a36Sopenharmony_ci	if (ret) {
129162306a36Sopenharmony_ci		dev_err(dev, "Could not rearm rescal reset\n");
129262306a36Sopenharmony_ci		return ret;
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	if (pcie->sr) {
129662306a36Sopenharmony_ci		/*
129762306a36Sopenharmony_ci		 * Now turn off the regulators, but if at least one
129862306a36Sopenharmony_ci		 * downstream device is enabled as a wake-up source, do not
129962306a36Sopenharmony_ci		 * turn off regulators.
130062306a36Sopenharmony_ci		 */
130162306a36Sopenharmony_ci		pcie->ep_wakeup_capable = false;
130262306a36Sopenharmony_ci		pci_walk_bus(bridge->bus, pci_dev_may_wakeup,
130362306a36Sopenharmony_ci			     &pcie->ep_wakeup_capable);
130462306a36Sopenharmony_ci		if (!pcie->ep_wakeup_capable) {
130562306a36Sopenharmony_ci			ret = regulator_bulk_disable(pcie->sr->num_supplies,
130662306a36Sopenharmony_ci						     pcie->sr->supplies);
130762306a36Sopenharmony_ci			if (ret) {
130862306a36Sopenharmony_ci				dev_err(dev, "Could not turn off regulators\n");
130962306a36Sopenharmony_ci				reset_control_reset(pcie->rescal);
131062306a36Sopenharmony_ci				return ret;
131162306a36Sopenharmony_ci			}
131262306a36Sopenharmony_ci		}
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci	clk_disable_unprepare(pcie->clk);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	return 0;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic int brcm_pcie_resume_noirq(struct device *dev)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	struct brcm_pcie *pcie = dev_get_drvdata(dev);
132262306a36Sopenharmony_ci	void __iomem *base;
132362306a36Sopenharmony_ci	u32 tmp;
132462306a36Sopenharmony_ci	int ret;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	base = pcie->base;
132762306a36Sopenharmony_ci	ret = clk_prepare_enable(pcie->clk);
132862306a36Sopenharmony_ci	if (ret)
132962306a36Sopenharmony_ci		return ret;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	ret = reset_control_reset(pcie->rescal);
133262306a36Sopenharmony_ci	if (ret)
133362306a36Sopenharmony_ci		goto err_disable_clk;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	ret = brcm_phy_start(pcie);
133662306a36Sopenharmony_ci	if (ret)
133762306a36Sopenharmony_ci		goto err_reset;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	/* Take bridge out of reset so we can access the SERDES reg */
134062306a36Sopenharmony_ci	pcie->bridge_sw_init_set(pcie, 0);
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	/* SERDES_IDDQ = 0 */
134362306a36Sopenharmony_ci	tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
134462306a36Sopenharmony_ci	u32p_replace_bits(&tmp, 0, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
134562306a36Sopenharmony_ci	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	/* wait for serdes to be stable */
134862306a36Sopenharmony_ci	udelay(100);
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	ret = brcm_pcie_setup(pcie);
135162306a36Sopenharmony_ci	if (ret)
135262306a36Sopenharmony_ci		goto err_reset;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	if (pcie->sr) {
135562306a36Sopenharmony_ci		if (pcie->ep_wakeup_capable) {
135662306a36Sopenharmony_ci			/*
135762306a36Sopenharmony_ci			 * We are resuming from a suspend.  In the suspend we
135862306a36Sopenharmony_ci			 * did not disable the power supplies, so there is
135962306a36Sopenharmony_ci			 * no need to enable them (and falsely increase their
136062306a36Sopenharmony_ci			 * usage count).
136162306a36Sopenharmony_ci			 */
136262306a36Sopenharmony_ci			pcie->ep_wakeup_capable = false;
136362306a36Sopenharmony_ci		} else {
136462306a36Sopenharmony_ci			ret = regulator_bulk_enable(pcie->sr->num_supplies,
136562306a36Sopenharmony_ci						    pcie->sr->supplies);
136662306a36Sopenharmony_ci			if (ret) {
136762306a36Sopenharmony_ci				dev_err(dev, "Could not turn on regulators\n");
136862306a36Sopenharmony_ci				goto err_reset;
136962306a36Sopenharmony_ci			}
137062306a36Sopenharmony_ci		}
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	ret = brcm_pcie_start_link(pcie);
137462306a36Sopenharmony_ci	if (ret)
137562306a36Sopenharmony_ci		goto err_regulator;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	if (pcie->msi)
137862306a36Sopenharmony_ci		brcm_msi_set_regs(pcie->msi);
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci	return 0;
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_cierr_regulator:
138362306a36Sopenharmony_ci	if (pcie->sr)
138462306a36Sopenharmony_ci		regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);
138562306a36Sopenharmony_cierr_reset:
138662306a36Sopenharmony_ci	reset_control_rearm(pcie->rescal);
138762306a36Sopenharmony_cierr_disable_clk:
138862306a36Sopenharmony_ci	clk_disable_unprepare(pcie->clk);
138962306a36Sopenharmony_ci	return ret;
139062306a36Sopenharmony_ci}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_cistatic void __brcm_pcie_remove(struct brcm_pcie *pcie)
139362306a36Sopenharmony_ci{
139462306a36Sopenharmony_ci	brcm_msi_remove(pcie);
139562306a36Sopenharmony_ci	brcm_pcie_turn_off(pcie);
139662306a36Sopenharmony_ci	if (brcm_phy_stop(pcie))
139762306a36Sopenharmony_ci		dev_err(pcie->dev, "Could not stop phy\n");
139862306a36Sopenharmony_ci	if (reset_control_rearm(pcie->rescal))
139962306a36Sopenharmony_ci		dev_err(pcie->dev, "Could not rearm rescal reset\n");
140062306a36Sopenharmony_ci	clk_disable_unprepare(pcie->clk);
140162306a36Sopenharmony_ci}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_cistatic void brcm_pcie_remove(struct platform_device *pdev)
140462306a36Sopenharmony_ci{
140562306a36Sopenharmony_ci	struct brcm_pcie *pcie = platform_get_drvdata(pdev);
140662306a36Sopenharmony_ci	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	pci_stop_root_bus(bridge->bus);
140962306a36Sopenharmony_ci	pci_remove_root_bus(bridge->bus);
141062306a36Sopenharmony_ci	__brcm_pcie_remove(pcie);
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_cistatic const int pcie_offsets[] = {
141462306a36Sopenharmony_ci	[RGR1_SW_INIT_1] = 0x9210,
141562306a36Sopenharmony_ci	[EXT_CFG_INDEX]  = 0x9000,
141662306a36Sopenharmony_ci	[EXT_CFG_DATA]   = 0x9004,
141762306a36Sopenharmony_ci};
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_cistatic const int pcie_offsets_bmips_7425[] = {
142062306a36Sopenharmony_ci	[RGR1_SW_INIT_1] = 0x8010,
142162306a36Sopenharmony_ci	[EXT_CFG_INDEX]  = 0x8300,
142262306a36Sopenharmony_ci	[EXT_CFG_DATA]   = 0x8304,
142362306a36Sopenharmony_ci};
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_cistatic const struct pcie_cfg_data generic_cfg = {
142662306a36Sopenharmony_ci	.offsets	= pcie_offsets,
142762306a36Sopenharmony_ci	.type		= GENERIC,
142862306a36Sopenharmony_ci	.perst_set	= brcm_pcie_perst_set_generic,
142962306a36Sopenharmony_ci	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
143062306a36Sopenharmony_ci};
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_cistatic const struct pcie_cfg_data bcm7425_cfg = {
143362306a36Sopenharmony_ci	.offsets	= pcie_offsets_bmips_7425,
143462306a36Sopenharmony_ci	.type		= BCM7425,
143562306a36Sopenharmony_ci	.perst_set	= brcm_pcie_perst_set_generic,
143662306a36Sopenharmony_ci	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
143762306a36Sopenharmony_ci};
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_cistatic const struct pcie_cfg_data bcm7435_cfg = {
144062306a36Sopenharmony_ci	.offsets	= pcie_offsets,
144162306a36Sopenharmony_ci	.type		= BCM7435,
144262306a36Sopenharmony_ci	.perst_set	= brcm_pcie_perst_set_generic,
144362306a36Sopenharmony_ci	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
144462306a36Sopenharmony_ci};
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_cistatic const struct pcie_cfg_data bcm4908_cfg = {
144762306a36Sopenharmony_ci	.offsets	= pcie_offsets,
144862306a36Sopenharmony_ci	.type		= BCM4908,
144962306a36Sopenharmony_ci	.perst_set	= brcm_pcie_perst_set_4908,
145062306a36Sopenharmony_ci	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
145162306a36Sopenharmony_ci};
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic const int pcie_offset_bcm7278[] = {
145462306a36Sopenharmony_ci	[RGR1_SW_INIT_1] = 0xc010,
145562306a36Sopenharmony_ci	[EXT_CFG_INDEX] = 0x9000,
145662306a36Sopenharmony_ci	[EXT_CFG_DATA] = 0x9004,
145762306a36Sopenharmony_ci};
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_cistatic const struct pcie_cfg_data bcm7278_cfg = {
146062306a36Sopenharmony_ci	.offsets	= pcie_offset_bcm7278,
146162306a36Sopenharmony_ci	.type		= BCM7278,
146262306a36Sopenharmony_ci	.perst_set	= brcm_pcie_perst_set_7278,
146362306a36Sopenharmony_ci	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_7278,
146462306a36Sopenharmony_ci};
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic const struct pcie_cfg_data bcm2711_cfg = {
146762306a36Sopenharmony_ci	.offsets	= pcie_offsets,
146862306a36Sopenharmony_ci	.type		= BCM2711,
146962306a36Sopenharmony_ci	.perst_set	= brcm_pcie_perst_set_generic,
147062306a36Sopenharmony_ci	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
147162306a36Sopenharmony_ci};
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic const struct of_device_id brcm_pcie_match[] = {
147462306a36Sopenharmony_ci	{ .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg },
147562306a36Sopenharmony_ci	{ .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg },
147662306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg },
147762306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7278-pcie", .data = &bcm7278_cfg },
147862306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7216-pcie", .data = &bcm7278_cfg },
147962306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7445-pcie", .data = &generic_cfg },
148062306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7435-pcie", .data = &bcm7435_cfg },
148162306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7425-pcie", .data = &bcm7425_cfg },
148262306a36Sopenharmony_ci	{},
148362306a36Sopenharmony_ci};
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cistatic struct pci_ops brcm_pcie_ops = {
148662306a36Sopenharmony_ci	.map_bus = brcm_pcie_map_bus,
148762306a36Sopenharmony_ci	.read = pci_generic_config_read,
148862306a36Sopenharmony_ci	.write = pci_generic_config_write,
148962306a36Sopenharmony_ci	.add_bus = brcm_pcie_add_bus,
149062306a36Sopenharmony_ci	.remove_bus = brcm_pcie_remove_bus,
149162306a36Sopenharmony_ci};
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_cistatic struct pci_ops brcm7425_pcie_ops = {
149462306a36Sopenharmony_ci	.map_bus = brcm7425_pcie_map_bus,
149562306a36Sopenharmony_ci	.read = pci_generic_config_read32,
149662306a36Sopenharmony_ci	.write = pci_generic_config_write32,
149762306a36Sopenharmony_ci	.add_bus = brcm_pcie_add_bus,
149862306a36Sopenharmony_ci	.remove_bus = brcm_pcie_remove_bus,
149962306a36Sopenharmony_ci};
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_cistatic int brcm_pcie_probe(struct platform_device *pdev)
150262306a36Sopenharmony_ci{
150362306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node, *msi_np;
150462306a36Sopenharmony_ci	struct pci_host_bridge *bridge;
150562306a36Sopenharmony_ci	const struct pcie_cfg_data *data;
150662306a36Sopenharmony_ci	struct brcm_pcie *pcie;
150762306a36Sopenharmony_ci	int ret;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*pcie));
151062306a36Sopenharmony_ci	if (!bridge)
151162306a36Sopenharmony_ci		return -ENOMEM;
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	data = of_device_get_match_data(&pdev->dev);
151462306a36Sopenharmony_ci	if (!data) {
151562306a36Sopenharmony_ci		pr_err("failed to look up compatible string\n");
151662306a36Sopenharmony_ci		return -EINVAL;
151762306a36Sopenharmony_ci	}
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	pcie = pci_host_bridge_priv(bridge);
152062306a36Sopenharmony_ci	pcie->dev = &pdev->dev;
152162306a36Sopenharmony_ci	pcie->np = np;
152262306a36Sopenharmony_ci	pcie->reg_offsets = data->offsets;
152362306a36Sopenharmony_ci	pcie->type = data->type;
152462306a36Sopenharmony_ci	pcie->perst_set = data->perst_set;
152562306a36Sopenharmony_ci	pcie->bridge_sw_init_set = data->bridge_sw_init_set;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	pcie->base = devm_platform_ioremap_resource(pdev, 0);
152862306a36Sopenharmony_ci	if (IS_ERR(pcie->base))
152962306a36Sopenharmony_ci		return PTR_ERR(pcie->base);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	pcie->clk = devm_clk_get_optional(&pdev->dev, "sw_pcie");
153262306a36Sopenharmony_ci	if (IS_ERR(pcie->clk))
153362306a36Sopenharmony_ci		return PTR_ERR(pcie->clk);
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	ret = of_pci_get_max_link_speed(np);
153662306a36Sopenharmony_ci	pcie->gen = (ret < 0) ? 0 : ret;
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	ret = clk_prepare_enable(pcie->clk);
154162306a36Sopenharmony_ci	if (ret) {
154262306a36Sopenharmony_ci		dev_err(&pdev->dev, "could not enable clock\n");
154362306a36Sopenharmony_ci		return ret;
154462306a36Sopenharmony_ci	}
154562306a36Sopenharmony_ci	pcie->rescal = devm_reset_control_get_optional_shared(&pdev->dev, "rescal");
154662306a36Sopenharmony_ci	if (IS_ERR(pcie->rescal)) {
154762306a36Sopenharmony_ci		clk_disable_unprepare(pcie->clk);
154862306a36Sopenharmony_ci		return PTR_ERR(pcie->rescal);
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci	pcie->perst_reset = devm_reset_control_get_optional_exclusive(&pdev->dev, "perst");
155162306a36Sopenharmony_ci	if (IS_ERR(pcie->perst_reset)) {
155262306a36Sopenharmony_ci		clk_disable_unprepare(pcie->clk);
155362306a36Sopenharmony_ci		return PTR_ERR(pcie->perst_reset);
155462306a36Sopenharmony_ci	}
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	ret = reset_control_reset(pcie->rescal);
155762306a36Sopenharmony_ci	if (ret)
155862306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to deassert 'rescal'\n");
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	ret = brcm_phy_start(pcie);
156162306a36Sopenharmony_ci	if (ret) {
156262306a36Sopenharmony_ci		reset_control_rearm(pcie->rescal);
156362306a36Sopenharmony_ci		clk_disable_unprepare(pcie->clk);
156462306a36Sopenharmony_ci		return ret;
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	ret = brcm_pcie_setup(pcie);
156862306a36Sopenharmony_ci	if (ret)
156962306a36Sopenharmony_ci		goto fail;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	pcie->hw_rev = readl(pcie->base + PCIE_MISC_REVISION);
157262306a36Sopenharmony_ci	if (pcie->type == BCM4908 && pcie->hw_rev >= BRCM_PCIE_HW_REV_3_20) {
157362306a36Sopenharmony_ci		dev_err(pcie->dev, "hardware revision with unsupported PERST# setup\n");
157462306a36Sopenharmony_ci		ret = -ENODEV;
157562306a36Sopenharmony_ci		goto fail;
157662306a36Sopenharmony_ci	}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	msi_np = of_parse_phandle(pcie->np, "msi-parent", 0);
157962306a36Sopenharmony_ci	if (pci_msi_enabled() && msi_np == pcie->np) {
158062306a36Sopenharmony_ci		ret = brcm_pcie_enable_msi(pcie);
158162306a36Sopenharmony_ci		if (ret) {
158262306a36Sopenharmony_ci			dev_err(pcie->dev, "probe of internal MSI failed");
158362306a36Sopenharmony_ci			goto fail;
158462306a36Sopenharmony_ci		}
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	bridge->ops = pcie->type == BCM7425 ? &brcm7425_pcie_ops : &brcm_pcie_ops;
158862306a36Sopenharmony_ci	bridge->sysdata = pcie;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	platform_set_drvdata(pdev, pcie);
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	ret = pci_host_probe(bridge);
159362306a36Sopenharmony_ci	if (!ret && !brcm_pcie_link_up(pcie))
159462306a36Sopenharmony_ci		ret = -ENODEV;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	if (ret) {
159762306a36Sopenharmony_ci		brcm_pcie_remove(pdev);
159862306a36Sopenharmony_ci		return ret;
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	return 0;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_cifail:
160462306a36Sopenharmony_ci	__brcm_pcie_remove(pcie);
160562306a36Sopenharmony_ci	return ret;
160662306a36Sopenharmony_ci}
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, brcm_pcie_match);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_cistatic const struct dev_pm_ops brcm_pcie_pm_ops = {
161162306a36Sopenharmony_ci	.suspend_noirq = brcm_pcie_suspend_noirq,
161262306a36Sopenharmony_ci	.resume_noirq = brcm_pcie_resume_noirq,
161362306a36Sopenharmony_ci};
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_cistatic struct platform_driver brcm_pcie_driver = {
161662306a36Sopenharmony_ci	.probe = brcm_pcie_probe,
161762306a36Sopenharmony_ci	.remove_new = brcm_pcie_remove,
161862306a36Sopenharmony_ci	.driver = {
161962306a36Sopenharmony_ci		.name = "brcm-pcie",
162062306a36Sopenharmony_ci		.of_match_table = brcm_pcie_match,
162162306a36Sopenharmony_ci		.pm = &brcm_pcie_pm_ops,
162262306a36Sopenharmony_ci	},
162362306a36Sopenharmony_ci};
162462306a36Sopenharmony_cimodule_platform_driver(brcm_pcie_driver);
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
162762306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom STB PCIe RC driver");
162862306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom");
1629