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