18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* Copyright (C) 2009 - 2019 Broadcom */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 58c2ecf20Sopenharmony_ci#include <linux/bitops.h> 68c2ecf20Sopenharmony_ci#include <linux/clk.h> 78c2ecf20Sopenharmony_ci#include <linux/compiler.h> 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/ioport.h> 138c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 148c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/list.h> 178c2ecf20Sopenharmony_ci#include <linux/log2.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/msi.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 228c2ecf20Sopenharmony_ci#include <linux/of_pci.h> 238c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 248c2ecf20Sopenharmony_ci#include <linux/pci.h> 258c2ecf20Sopenharmony_ci#include <linux/printk.h> 268c2ecf20Sopenharmony_ci#include <linux/reset.h> 278c2ecf20Sopenharmony_ci#include <linux/sizes.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/string.h> 308c2ecf20Sopenharmony_ci#include <linux/types.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include "../pci.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* BRCM_PCIE_CAP_REGS - Offset for the mandatory capability config regs */ 358c2ecf20Sopenharmony_ci#define BRCM_PCIE_CAP_REGS 0x00ac 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Broadcom STB PCIe Register Offsets */ 388c2ecf20Sopenharmony_ci#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1 0x0188 398c2ecf20Sopenharmony_ci#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK 0xc 408c2ecf20Sopenharmony_ci#define PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN 0x0 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define PCIE_RC_CFG_PRIV1_ID_VAL3 0x043c 438c2ecf20Sopenharmony_ci#define PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK 0xffffff 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc 468c2ecf20Sopenharmony_ci#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define PCIE_RC_DL_MDIO_ADDR 0x1100 498c2ecf20Sopenharmony_ci#define PCIE_RC_DL_MDIO_WR_DATA 0x1104 508c2ecf20Sopenharmony_ci#define PCIE_RC_DL_MDIO_RD_DATA 0x1108 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define PCIE_MISC_MISC_CTRL 0x4008 538c2ecf20Sopenharmony_ci#define PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000 548c2ecf20Sopenharmony_ci#define PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000 558c2ecf20Sopenharmony_ci#define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK 0xf8000000 588c2ecf20Sopenharmony_ci#define PCIE_MISC_MISC_CTRL_SCB1_SIZE_MASK 0x07c00000 598c2ecf20Sopenharmony_ci#define PCIE_MISC_MISC_CTRL_SCB2_SIZE_MASK 0x0000001f 608c2ecf20Sopenharmony_ci#define SCB_SIZE_MASK(x) PCIE_MISC_MISC_CTRL_SCB ## x ## _SIZE_MASK 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c 638c2ecf20Sopenharmony_ci#define PCIE_MEM_WIN0_LO(win) \ 648c2ecf20Sopenharmony_ci PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO + ((win) * 8) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI 0x4010 678c2ecf20Sopenharmony_ci#define PCIE_MEM_WIN0_HI(win) \ 688c2ecf20Sopenharmony_ci PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI + ((win) * 8) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c 718c2ecf20Sopenharmony_ci#define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034 748c2ecf20Sopenharmony_ci#define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f 758c2ecf20Sopenharmony_ci#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c 788c2ecf20Sopenharmony_ci#define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044 818c2ecf20Sopenharmony_ci#define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define PCIE_MISC_MSI_DATA_CONFIG 0x404c 848c2ecf20Sopenharmony_ci#define PCIE_MISC_MSI_DATA_CONFIG_VAL_32 0xffe06540 858c2ecf20Sopenharmony_ci#define PCIE_MISC_MSI_DATA_CONFIG_VAL_8 0xfff86540 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define PCIE_MISC_PCIE_CTRL 0x4064 888c2ecf20Sopenharmony_ci#define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1 898c2ecf20Sopenharmony_ci#define PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK 0x4 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define PCIE_MISC_PCIE_STATUS 0x4068 928c2ecf20Sopenharmony_ci#define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK 0x80 938c2ecf20Sopenharmony_ci#define PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK 0x20 948c2ecf20Sopenharmony_ci#define PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK 0x10 958c2ecf20Sopenharmony_ci#define PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK 0x40 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define PCIE_MISC_REVISION 0x406c 988c2ecf20Sopenharmony_ci#define BRCM_PCIE_HW_REV_33 0x0303 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070 1018c2ecf20Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK 0xfff00000 1028c2ecf20Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK 0xfff0 1038c2ecf20Sopenharmony_ci#define PCIE_MEM_WIN0_BASE_LIMIT(win) \ 1048c2ecf20Sopenharmony_ci PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT + ((win) * 4) 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI 0x4080 1078c2ecf20Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK 0xff 1088c2ecf20Sopenharmony_ci#define PCIE_MEM_WIN0_BASE_HI(win) \ 1098c2ecf20Sopenharmony_ci PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI + ((win) * 8) 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI 0x4084 1128c2ecf20Sopenharmony_ci#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK 0xff 1138c2ecf20Sopenharmony_ci#define PCIE_MEM_WIN0_LIMIT_HI(win) \ 1148c2ecf20Sopenharmony_ci PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8) 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204 1178c2ecf20Sopenharmony_ci#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2 1188c2ecf20Sopenharmony_ci#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define PCIE_INTR2_CPU_BASE 0x4300 1228c2ecf20Sopenharmony_ci#define PCIE_MSI_INTR2_BASE 0x4500 1238c2ecf20Sopenharmony_ci/* Offsets from PCIE_INTR2_CPU_BASE and PCIE_MSI_INTR2_BASE */ 1248c2ecf20Sopenharmony_ci#define MSI_INT_STATUS 0x0 1258c2ecf20Sopenharmony_ci#define MSI_INT_CLR 0x8 1268c2ecf20Sopenharmony_ci#define MSI_INT_MASK_SET 0x10 1278c2ecf20Sopenharmony_ci#define MSI_INT_MASK_CLR 0x14 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci#define PCIE_EXT_CFG_DATA 0x8000 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci#define PCIE_EXT_CFG_INDEX 0x9000 1328c2ecf20Sopenharmony_ci#define PCIE_EXT_BUSNUM_SHIFT 20 1338c2ecf20Sopenharmony_ci#define PCIE_EXT_SLOT_SHIFT 15 1348c2ecf20Sopenharmony_ci#define PCIE_EXT_FUNC_SHIFT 12 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci#define PCIE_RGR1_SW_INIT_1_PERST_MASK 0x1 1378c2ecf20Sopenharmony_ci#define PCIE_RGR1_SW_INIT_1_PERST_SHIFT 0x0 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define RGR1_SW_INIT_1_INIT_GENERIC_MASK 0x2 1408c2ecf20Sopenharmony_ci#define RGR1_SW_INIT_1_INIT_GENERIC_SHIFT 0x1 1418c2ecf20Sopenharmony_ci#define RGR1_SW_INIT_1_INIT_7278_MASK 0x1 1428c2ecf20Sopenharmony_ci#define RGR1_SW_INIT_1_INIT_7278_SHIFT 0x0 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* PCIe parameters */ 1458c2ecf20Sopenharmony_ci#define BRCM_NUM_PCIE_OUT_WINS 0x4 1468c2ecf20Sopenharmony_ci#define BRCM_INT_PCI_MSI_NR 32 1478c2ecf20Sopenharmony_ci#define BRCM_INT_PCI_MSI_LEGACY_NR 8 1488c2ecf20Sopenharmony_ci#define BRCM_INT_PCI_MSI_SHIFT 0 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci/* MSI target adresses */ 1518c2ecf20Sopenharmony_ci#define BRCM_MSI_TARGET_ADDR_LT_4GB 0x0fffffffcULL 1528c2ecf20Sopenharmony_ci#define BRCM_MSI_TARGET_ADDR_GT_4GB 0xffffffffcULL 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/* MDIO registers */ 1558c2ecf20Sopenharmony_ci#define MDIO_PORT0 0x0 1568c2ecf20Sopenharmony_ci#define MDIO_DATA_MASK 0x7fffffff 1578c2ecf20Sopenharmony_ci#define MDIO_PORT_MASK 0xf0000 1588c2ecf20Sopenharmony_ci#define MDIO_REGAD_MASK 0xffff 1598c2ecf20Sopenharmony_ci#define MDIO_CMD_MASK 0xfff00000 1608c2ecf20Sopenharmony_ci#define MDIO_CMD_READ 0x1 1618c2ecf20Sopenharmony_ci#define MDIO_CMD_WRITE 0x0 1628c2ecf20Sopenharmony_ci#define MDIO_DATA_DONE_MASK 0x80000000 1638c2ecf20Sopenharmony_ci#define MDIO_RD_DONE(x) (((x) & MDIO_DATA_DONE_MASK) ? 1 : 0) 1648c2ecf20Sopenharmony_ci#define MDIO_WT_DONE(x) (((x) & MDIO_DATA_DONE_MASK) ? 0 : 1) 1658c2ecf20Sopenharmony_ci#define SSC_REGS_ADDR 0x1100 1668c2ecf20Sopenharmony_ci#define SET_ADDR_OFFSET 0x1f 1678c2ecf20Sopenharmony_ci#define SSC_CNTL_OFFSET 0x2 1688c2ecf20Sopenharmony_ci#define SSC_CNTL_OVRD_EN_MASK 0x8000 1698c2ecf20Sopenharmony_ci#define SSC_CNTL_OVRD_VAL_MASK 0x4000 1708c2ecf20Sopenharmony_ci#define SSC_STATUS_OFFSET 0x1 1718c2ecf20Sopenharmony_ci#define SSC_STATUS_SSC_MASK 0x400 1728c2ecf20Sopenharmony_ci#define SSC_STATUS_PLL_LOCK_MASK 0x800 1738c2ecf20Sopenharmony_ci#define PCIE_BRCM_MAX_MEMC 3 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci#define IDX_ADDR(pcie) (pcie->reg_offsets[EXT_CFG_INDEX]) 1768c2ecf20Sopenharmony_ci#define DATA_ADDR(pcie) (pcie->reg_offsets[EXT_CFG_DATA]) 1778c2ecf20Sopenharmony_ci#define PCIE_RGR1_SW_INIT_1(pcie) (pcie->reg_offsets[RGR1_SW_INIT_1]) 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* Rescal registers */ 1808c2ecf20Sopenharmony_ci#define PCIE_DVT_PMU_PCIE_PHY_CTRL 0xc700 1818c2ecf20Sopenharmony_ci#define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS 0x3 1828c2ecf20Sopenharmony_ci#define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_MASK 0x4 1838c2ecf20Sopenharmony_ci#define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_SHIFT 0x2 1848c2ecf20Sopenharmony_ci#define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_MASK 0x2 1858c2ecf20Sopenharmony_ci#define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT 0x1 1868c2ecf20Sopenharmony_ci#define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_MASK 0x1 1878c2ecf20Sopenharmony_ci#define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT 0x0 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* Forward declarations */ 1908c2ecf20Sopenharmony_cistruct brcm_pcie; 1918c2ecf20Sopenharmony_cistatic inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val); 1928c2ecf20Sopenharmony_cistatic inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val); 1938c2ecf20Sopenharmony_cistatic inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val); 1948c2ecf20Sopenharmony_cistatic inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cienum { 1978c2ecf20Sopenharmony_ci RGR1_SW_INIT_1, 1988c2ecf20Sopenharmony_ci EXT_CFG_INDEX, 1998c2ecf20Sopenharmony_ci EXT_CFG_DATA, 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cienum { 2038c2ecf20Sopenharmony_ci RGR1_SW_INIT_1_INIT_MASK, 2048c2ecf20Sopenharmony_ci RGR1_SW_INIT_1_INIT_SHIFT, 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cienum pcie_type { 2088c2ecf20Sopenharmony_ci GENERIC, 2098c2ecf20Sopenharmony_ci BCM7278, 2108c2ecf20Sopenharmony_ci BCM2711, 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistruct pcie_cfg_data { 2148c2ecf20Sopenharmony_ci const int *offsets; 2158c2ecf20Sopenharmony_ci const enum pcie_type type; 2168c2ecf20Sopenharmony_ci void (*perst_set)(struct brcm_pcie *pcie, u32 val); 2178c2ecf20Sopenharmony_ci void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val); 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic const int pcie_offsets[] = { 2218c2ecf20Sopenharmony_ci [RGR1_SW_INIT_1] = 0x9210, 2228c2ecf20Sopenharmony_ci [EXT_CFG_INDEX] = 0x9000, 2238c2ecf20Sopenharmony_ci [EXT_CFG_DATA] = 0x9004, 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic const struct pcie_cfg_data generic_cfg = { 2278c2ecf20Sopenharmony_ci .offsets = pcie_offsets, 2288c2ecf20Sopenharmony_ci .type = GENERIC, 2298c2ecf20Sopenharmony_ci .perst_set = brcm_pcie_perst_set_generic, 2308c2ecf20Sopenharmony_ci .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, 2318c2ecf20Sopenharmony_ci}; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic const int pcie_offset_bcm7278[] = { 2348c2ecf20Sopenharmony_ci [RGR1_SW_INIT_1] = 0xc010, 2358c2ecf20Sopenharmony_ci [EXT_CFG_INDEX] = 0x9000, 2368c2ecf20Sopenharmony_ci [EXT_CFG_DATA] = 0x9004, 2378c2ecf20Sopenharmony_ci}; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic const struct pcie_cfg_data bcm7278_cfg = { 2408c2ecf20Sopenharmony_ci .offsets = pcie_offset_bcm7278, 2418c2ecf20Sopenharmony_ci .type = BCM7278, 2428c2ecf20Sopenharmony_ci .perst_set = brcm_pcie_perst_set_7278, 2438c2ecf20Sopenharmony_ci .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_7278, 2448c2ecf20Sopenharmony_ci}; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic const struct pcie_cfg_data bcm2711_cfg = { 2478c2ecf20Sopenharmony_ci .offsets = pcie_offsets, 2488c2ecf20Sopenharmony_ci .type = BCM2711, 2498c2ecf20Sopenharmony_ci .perst_set = brcm_pcie_perst_set_generic, 2508c2ecf20Sopenharmony_ci .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, 2518c2ecf20Sopenharmony_ci}; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistruct brcm_msi { 2548c2ecf20Sopenharmony_ci struct device *dev; 2558c2ecf20Sopenharmony_ci void __iomem *base; 2568c2ecf20Sopenharmony_ci struct device_node *np; 2578c2ecf20Sopenharmony_ci struct irq_domain *msi_domain; 2588c2ecf20Sopenharmony_ci struct irq_domain *inner_domain; 2598c2ecf20Sopenharmony_ci struct mutex lock; /* guards the alloc/free operations */ 2608c2ecf20Sopenharmony_ci u64 target_addr; 2618c2ecf20Sopenharmony_ci int irq; 2628c2ecf20Sopenharmony_ci /* used indicates which MSI interrupts have been alloc'd */ 2638c2ecf20Sopenharmony_ci unsigned long used; 2648c2ecf20Sopenharmony_ci bool legacy; 2658c2ecf20Sopenharmony_ci /* Some chips have MSIs in bits [31..24] of a shared register. */ 2668c2ecf20Sopenharmony_ci int legacy_shift; 2678c2ecf20Sopenharmony_ci int nr; /* No. of MSI available, depends on chip */ 2688c2ecf20Sopenharmony_ci /* This is the base pointer for interrupt status/set/clr regs */ 2698c2ecf20Sopenharmony_ci void __iomem *intr_base; 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* Internal PCIe Host Controller Information.*/ 2738c2ecf20Sopenharmony_cistruct brcm_pcie { 2748c2ecf20Sopenharmony_ci struct device *dev; 2758c2ecf20Sopenharmony_ci void __iomem *base; 2768c2ecf20Sopenharmony_ci struct clk *clk; 2778c2ecf20Sopenharmony_ci struct device_node *np; 2788c2ecf20Sopenharmony_ci bool ssc; 2798c2ecf20Sopenharmony_ci int gen; 2808c2ecf20Sopenharmony_ci u64 msi_target_addr; 2818c2ecf20Sopenharmony_ci struct brcm_msi *msi; 2828c2ecf20Sopenharmony_ci const int *reg_offsets; 2838c2ecf20Sopenharmony_ci enum pcie_type type; 2848c2ecf20Sopenharmony_ci struct reset_control *rescal; 2858c2ecf20Sopenharmony_ci int num_memc; 2868c2ecf20Sopenharmony_ci u64 memc_size[PCIE_BRCM_MAX_MEMC]; 2878c2ecf20Sopenharmony_ci u32 hw_rev; 2888c2ecf20Sopenharmony_ci void (*perst_set)(struct brcm_pcie *pcie, u32 val); 2898c2ecf20Sopenharmony_ci void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val); 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci/* 2938c2ecf20Sopenharmony_ci * This is to convert the size of the inbound "BAR" region to the 2948c2ecf20Sopenharmony_ci * non-linear values of PCIE_X_MISC_RC_BAR[123]_CONFIG_LO.SIZE 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_cistatic int brcm_pcie_encode_ibar_size(u64 size) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci int log2_in = ilog2(size); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (log2_in >= 12 && log2_in <= 15) 3018c2ecf20Sopenharmony_ci /* Covers 4KB to 32KB (inclusive) */ 3028c2ecf20Sopenharmony_ci return (log2_in - 12) + 0x1c; 3038c2ecf20Sopenharmony_ci else if (log2_in >= 16 && log2_in <= 35) 3048c2ecf20Sopenharmony_ci /* Covers 64KB to 32GB, (inclusive) */ 3058c2ecf20Sopenharmony_ci return log2_in - 15; 3068c2ecf20Sopenharmony_ci /* Something is awry so disable */ 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic u32 brcm_pcie_mdio_form_pkt(int port, int regad, int cmd) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci u32 pkt = 0; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci pkt |= FIELD_PREP(MDIO_PORT_MASK, port); 3158c2ecf20Sopenharmony_ci pkt |= FIELD_PREP(MDIO_REGAD_MASK, regad); 3168c2ecf20Sopenharmony_ci pkt |= FIELD_PREP(MDIO_CMD_MASK, cmd); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return pkt; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* negative return value indicates error */ 3228c2ecf20Sopenharmony_cistatic int brcm_pcie_mdio_read(void __iomem *base, u8 port, u8 regad, u32 *val) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci int tries; 3258c2ecf20Sopenharmony_ci u32 data; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_READ), 3288c2ecf20Sopenharmony_ci base + PCIE_RC_DL_MDIO_ADDR); 3298c2ecf20Sopenharmony_ci readl(base + PCIE_RC_DL_MDIO_ADDR); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci data = readl(base + PCIE_RC_DL_MDIO_RD_DATA); 3328c2ecf20Sopenharmony_ci for (tries = 0; !MDIO_RD_DONE(data) && tries < 10; tries++) { 3338c2ecf20Sopenharmony_ci udelay(10); 3348c2ecf20Sopenharmony_ci data = readl(base + PCIE_RC_DL_MDIO_RD_DATA); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci *val = FIELD_GET(MDIO_DATA_MASK, data); 3388c2ecf20Sopenharmony_ci return MDIO_RD_DONE(data) ? 0 : -EIO; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci/* negative return value indicates error */ 3428c2ecf20Sopenharmony_cistatic int brcm_pcie_mdio_write(void __iomem *base, u8 port, 3438c2ecf20Sopenharmony_ci u8 regad, u16 wrdata) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci int tries; 3468c2ecf20Sopenharmony_ci u32 data; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci writel(brcm_pcie_mdio_form_pkt(port, regad, MDIO_CMD_WRITE), 3498c2ecf20Sopenharmony_ci base + PCIE_RC_DL_MDIO_ADDR); 3508c2ecf20Sopenharmony_ci readl(base + PCIE_RC_DL_MDIO_ADDR); 3518c2ecf20Sopenharmony_ci writel(MDIO_DATA_DONE_MASK | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci data = readl(base + PCIE_RC_DL_MDIO_WR_DATA); 3548c2ecf20Sopenharmony_ci for (tries = 0; !MDIO_WT_DONE(data) && tries < 10; tries++) { 3558c2ecf20Sopenharmony_ci udelay(10); 3568c2ecf20Sopenharmony_ci data = readl(base + PCIE_RC_DL_MDIO_WR_DATA); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return MDIO_WT_DONE(data) ? 0 : -EIO; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* 3638c2ecf20Sopenharmony_ci * Configures device for Spread Spectrum Clocking (SSC) mode; a negative 3648c2ecf20Sopenharmony_ci * return value indicates error. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_cistatic int brcm_pcie_set_ssc(struct brcm_pcie *pcie) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci int pll, ssc; 3698c2ecf20Sopenharmony_ci int ret; 3708c2ecf20Sopenharmony_ci u32 tmp; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET, 3738c2ecf20Sopenharmony_ci SSC_REGS_ADDR); 3748c2ecf20Sopenharmony_ci if (ret < 0) 3758c2ecf20Sopenharmony_ci return ret; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, 3788c2ecf20Sopenharmony_ci SSC_CNTL_OFFSET, &tmp); 3798c2ecf20Sopenharmony_ci if (ret < 0) 3808c2ecf20Sopenharmony_ci return ret; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_EN_MASK); 3838c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, 1, SSC_CNTL_OVRD_VAL_MASK); 3848c2ecf20Sopenharmony_ci ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, 3858c2ecf20Sopenharmony_ci SSC_CNTL_OFFSET, tmp); 3868c2ecf20Sopenharmony_ci if (ret < 0) 3878c2ecf20Sopenharmony_ci return ret; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 3908c2ecf20Sopenharmony_ci ret = brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, 3918c2ecf20Sopenharmony_ci SSC_STATUS_OFFSET, &tmp); 3928c2ecf20Sopenharmony_ci if (ret < 0) 3938c2ecf20Sopenharmony_ci return ret; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci ssc = FIELD_GET(SSC_STATUS_SSC_MASK, tmp); 3968c2ecf20Sopenharmony_ci pll = FIELD_GET(SSC_STATUS_PLL_LOCK_MASK, tmp); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci return ssc && pll ? 0 : -EIO; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci/* Limits operation to a specific generation (1, 2, or 3) */ 4028c2ecf20Sopenharmony_cistatic void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci u16 lnkctl2 = readw(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); 4058c2ecf20Sopenharmony_ci u32 lnkcap = readl(pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci lnkcap = (lnkcap & ~PCI_EXP_LNKCAP_SLS) | gen; 4088c2ecf20Sopenharmony_ci writel(lnkcap, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCAP); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci lnkctl2 = (lnkctl2 & ~0xf) | gen; 4118c2ecf20Sopenharmony_ci writew(lnkctl2, pcie->base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKCTL2); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie, 4158c2ecf20Sopenharmony_ci unsigned int win, u64 cpu_addr, 4168c2ecf20Sopenharmony_ci u64 pcie_addr, u64 size) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci u32 cpu_addr_mb_high, limit_addr_mb_high; 4198c2ecf20Sopenharmony_ci phys_addr_t cpu_addr_mb, limit_addr_mb; 4208c2ecf20Sopenharmony_ci int high_addr_shift; 4218c2ecf20Sopenharmony_ci u32 tmp; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Set the base of the pcie_addr window */ 4248c2ecf20Sopenharmony_ci writel(lower_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_LO(win)); 4258c2ecf20Sopenharmony_ci writel(upper_32_bits(pcie_addr), pcie->base + PCIE_MEM_WIN0_HI(win)); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Write the addr base & limit lower bits (in MBs) */ 4288c2ecf20Sopenharmony_ci cpu_addr_mb = cpu_addr / SZ_1M; 4298c2ecf20Sopenharmony_ci limit_addr_mb = (cpu_addr + size - 1) / SZ_1M; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win)); 4328c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, cpu_addr_mb, 4338c2ecf20Sopenharmony_ci PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK); 4348c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, limit_addr_mb, 4358c2ecf20Sopenharmony_ci PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK); 4368c2ecf20Sopenharmony_ci writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win)); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* Write the cpu & limit addr upper bits */ 4398c2ecf20Sopenharmony_ci high_addr_shift = 4408c2ecf20Sopenharmony_ci HWEIGHT32(PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci cpu_addr_mb_high = cpu_addr_mb >> high_addr_shift; 4438c2ecf20Sopenharmony_ci tmp = readl(pcie->base + PCIE_MEM_WIN0_BASE_HI(win)); 4448c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, cpu_addr_mb_high, 4458c2ecf20Sopenharmony_ci PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_HI_BASE_MASK); 4468c2ecf20Sopenharmony_ci writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_HI(win)); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci limit_addr_mb_high = limit_addr_mb >> high_addr_shift; 4498c2ecf20Sopenharmony_ci tmp = readl(pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win)); 4508c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, limit_addr_mb_high, 4518c2ecf20Sopenharmony_ci PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI_LIMIT_MASK); 4528c2ecf20Sopenharmony_ci writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win)); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic struct irq_chip brcm_msi_irq_chip = { 4568c2ecf20Sopenharmony_ci .name = "BRCM STB PCIe MSI", 4578c2ecf20Sopenharmony_ci .irq_ack = irq_chip_ack_parent, 4588c2ecf20Sopenharmony_ci .irq_mask = pci_msi_mask_irq, 4598c2ecf20Sopenharmony_ci .irq_unmask = pci_msi_unmask_irq, 4608c2ecf20Sopenharmony_ci}; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_cistatic struct msi_domain_info brcm_msi_domain_info = { 4638c2ecf20Sopenharmony_ci /* Multi MSI is supported by the controller, but not by this driver */ 4648c2ecf20Sopenharmony_ci .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS), 4658c2ecf20Sopenharmony_ci .chip = &brcm_msi_irq_chip, 4668c2ecf20Sopenharmony_ci}; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic void brcm_pcie_msi_isr(struct irq_desc *desc) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 4718c2ecf20Sopenharmony_ci unsigned long status, virq; 4728c2ecf20Sopenharmony_ci struct brcm_msi *msi; 4738c2ecf20Sopenharmony_ci struct device *dev; 4748c2ecf20Sopenharmony_ci u32 bit; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 4778c2ecf20Sopenharmony_ci msi = irq_desc_get_handler_data(desc); 4788c2ecf20Sopenharmony_ci dev = msi->dev; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci status = readl(msi->intr_base + MSI_INT_STATUS); 4818c2ecf20Sopenharmony_ci status >>= msi->legacy_shift; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci for_each_set_bit(bit, &status, msi->nr) { 4848c2ecf20Sopenharmony_ci virq = irq_find_mapping(msi->inner_domain, bit); 4858c2ecf20Sopenharmony_ci if (virq) 4868c2ecf20Sopenharmony_ci generic_handle_irq(virq); 4878c2ecf20Sopenharmony_ci else 4888c2ecf20Sopenharmony_ci dev_dbg(dev, "unexpected MSI\n"); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct brcm_msi *msi = irq_data_get_irq_chip_data(data); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci msg->address_lo = lower_32_bits(msi->target_addr); 4998c2ecf20Sopenharmony_ci msg->address_hi = upper_32_bits(msi->target_addr); 5008c2ecf20Sopenharmony_ci msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | data->hwirq; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic int brcm_msi_set_affinity(struct irq_data *irq_data, 5048c2ecf20Sopenharmony_ci const struct cpumask *mask, bool force) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci return -EINVAL; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic void brcm_msi_ack_irq(struct irq_data *data) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci struct brcm_msi *msi = irq_data_get_irq_chip_data(data); 5128c2ecf20Sopenharmony_ci const int shift_amt = data->hwirq + msi->legacy_shift; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci writel(1 << shift_amt, msi->intr_base + MSI_INT_CLR); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic struct irq_chip brcm_msi_bottom_irq_chip = { 5198c2ecf20Sopenharmony_ci .name = "BRCM STB MSI", 5208c2ecf20Sopenharmony_ci .irq_compose_msi_msg = brcm_msi_compose_msi_msg, 5218c2ecf20Sopenharmony_ci .irq_set_affinity = brcm_msi_set_affinity, 5228c2ecf20Sopenharmony_ci .irq_ack = brcm_msi_ack_irq, 5238c2ecf20Sopenharmony_ci}; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic int brcm_msi_alloc(struct brcm_msi *msi) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci int hwirq; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci mutex_lock(&msi->lock); 5308c2ecf20Sopenharmony_ci hwirq = bitmap_find_free_region(&msi->used, msi->nr, 0); 5318c2ecf20Sopenharmony_ci mutex_unlock(&msi->lock); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return hwirq; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci mutex_lock(&msi->lock); 5398c2ecf20Sopenharmony_ci bitmap_release_region(&msi->used, hwirq, 0); 5408c2ecf20Sopenharmony_ci mutex_unlock(&msi->lock); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int brcm_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, 5448c2ecf20Sopenharmony_ci unsigned int nr_irqs, void *args) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct brcm_msi *msi = domain->host_data; 5478c2ecf20Sopenharmony_ci int hwirq; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci hwirq = brcm_msi_alloc(msi); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (hwirq < 0) 5528c2ecf20Sopenharmony_ci return hwirq; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci irq_domain_set_info(domain, virq, (irq_hw_number_t)hwirq, 5558c2ecf20Sopenharmony_ci &brcm_msi_bottom_irq_chip, domain->host_data, 5568c2ecf20Sopenharmony_ci handle_edge_irq, NULL, NULL); 5578c2ecf20Sopenharmony_ci return 0; 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic void brcm_irq_domain_free(struct irq_domain *domain, 5618c2ecf20Sopenharmony_ci unsigned int virq, unsigned int nr_irqs) 5628c2ecf20Sopenharmony_ci{ 5638c2ecf20Sopenharmony_ci struct irq_data *d = irq_domain_get_irq_data(domain, virq); 5648c2ecf20Sopenharmony_ci struct brcm_msi *msi = irq_data_get_irq_chip_data(d); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci brcm_msi_free(msi, d->hwirq); 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic const struct irq_domain_ops msi_domain_ops = { 5708c2ecf20Sopenharmony_ci .alloc = brcm_irq_domain_alloc, 5718c2ecf20Sopenharmony_ci .free = brcm_irq_domain_free, 5728c2ecf20Sopenharmony_ci}; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_cistatic int brcm_allocate_domains(struct brcm_msi *msi) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np); 5778c2ecf20Sopenharmony_ci struct device *dev = msi->dev; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci msi->inner_domain = irq_domain_add_linear(NULL, msi->nr, &msi_domain_ops, msi); 5808c2ecf20Sopenharmony_ci if (!msi->inner_domain) { 5818c2ecf20Sopenharmony_ci dev_err(dev, "failed to create IRQ domain\n"); 5828c2ecf20Sopenharmony_ci return -ENOMEM; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci msi->msi_domain = pci_msi_create_irq_domain(fwnode, 5868c2ecf20Sopenharmony_ci &brcm_msi_domain_info, 5878c2ecf20Sopenharmony_ci msi->inner_domain); 5888c2ecf20Sopenharmony_ci if (!msi->msi_domain) { 5898c2ecf20Sopenharmony_ci dev_err(dev, "failed to create MSI domain\n"); 5908c2ecf20Sopenharmony_ci irq_domain_remove(msi->inner_domain); 5918c2ecf20Sopenharmony_ci return -ENOMEM; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci return 0; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_cistatic void brcm_free_domains(struct brcm_msi *msi) 5988c2ecf20Sopenharmony_ci{ 5998c2ecf20Sopenharmony_ci irq_domain_remove(msi->msi_domain); 6008c2ecf20Sopenharmony_ci irq_domain_remove(msi->inner_domain); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic void brcm_msi_remove(struct brcm_pcie *pcie) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct brcm_msi *msi = pcie->msi; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci if (!msi) 6088c2ecf20Sopenharmony_ci return; 6098c2ecf20Sopenharmony_ci irq_set_chained_handler(msi->irq, NULL); 6108c2ecf20Sopenharmony_ci irq_set_handler_data(msi->irq, NULL); 6118c2ecf20Sopenharmony_ci brcm_free_domains(msi); 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic void brcm_msi_set_regs(struct brcm_msi *msi) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci u32 val = __GENMASK(31, msi->legacy_shift); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci writel(val, msi->intr_base + MSI_INT_MASK_CLR); 6198c2ecf20Sopenharmony_ci writel(val, msi->intr_base + MSI_INT_CLR); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* 6228c2ecf20Sopenharmony_ci * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI 6238c2ecf20Sopenharmony_ci * enable, which we set to 1. 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_ci writel(lower_32_bits(msi->target_addr) | 0x1, 6268c2ecf20Sopenharmony_ci msi->base + PCIE_MISC_MSI_BAR_CONFIG_LO); 6278c2ecf20Sopenharmony_ci writel(upper_32_bits(msi->target_addr), 6288c2ecf20Sopenharmony_ci msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci val = msi->legacy ? PCIE_MISC_MSI_DATA_CONFIG_VAL_8 : PCIE_MISC_MSI_DATA_CONFIG_VAL_32; 6318c2ecf20Sopenharmony_ci writel(val, msi->base + PCIE_MISC_MSI_DATA_CONFIG); 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_cistatic int brcm_pcie_enable_msi(struct brcm_pcie *pcie) 6358c2ecf20Sopenharmony_ci{ 6368c2ecf20Sopenharmony_ci struct brcm_msi *msi; 6378c2ecf20Sopenharmony_ci int irq, ret; 6388c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(dev->of_node, 1); 6418c2ecf20Sopenharmony_ci if (irq <= 0) { 6428c2ecf20Sopenharmony_ci dev_err(dev, "cannot map MSI interrupt\n"); 6438c2ecf20Sopenharmony_ci return -ENODEV; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci msi = devm_kzalloc(dev, sizeof(struct brcm_msi), GFP_KERNEL); 6478c2ecf20Sopenharmony_ci if (!msi) 6488c2ecf20Sopenharmony_ci return -ENOMEM; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci mutex_init(&msi->lock); 6518c2ecf20Sopenharmony_ci msi->dev = dev; 6528c2ecf20Sopenharmony_ci msi->base = pcie->base; 6538c2ecf20Sopenharmony_ci msi->np = pcie->np; 6548c2ecf20Sopenharmony_ci msi->target_addr = pcie->msi_target_addr; 6558c2ecf20Sopenharmony_ci msi->irq = irq; 6568c2ecf20Sopenharmony_ci msi->legacy = pcie->hw_rev < BRCM_PCIE_HW_REV_33; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (msi->legacy) { 6598c2ecf20Sopenharmony_ci msi->intr_base = msi->base + PCIE_INTR2_CPU_BASE; 6608c2ecf20Sopenharmony_ci msi->nr = BRCM_INT_PCI_MSI_LEGACY_NR; 6618c2ecf20Sopenharmony_ci msi->legacy_shift = 24; 6628c2ecf20Sopenharmony_ci } else { 6638c2ecf20Sopenharmony_ci msi->intr_base = msi->base + PCIE_MSI_INTR2_BASE; 6648c2ecf20Sopenharmony_ci msi->nr = BRCM_INT_PCI_MSI_NR; 6658c2ecf20Sopenharmony_ci msi->legacy_shift = 0; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci ret = brcm_allocate_domains(msi); 6698c2ecf20Sopenharmony_ci if (ret) 6708c2ecf20Sopenharmony_ci return ret; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(msi->irq, brcm_pcie_msi_isr, msi); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci brcm_msi_set_regs(msi); 6758c2ecf20Sopenharmony_ci pcie->msi = msi; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci return 0; 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci/* The controller is capable of serving in both RC and EP roles */ 6818c2ecf20Sopenharmony_cistatic bool brcm_pcie_rc_mode(struct brcm_pcie *pcie) 6828c2ecf20Sopenharmony_ci{ 6838c2ecf20Sopenharmony_ci void __iomem *base = pcie->base; 6848c2ecf20Sopenharmony_ci u32 val = readl(base + PCIE_MISC_PCIE_STATUS); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val); 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic bool brcm_pcie_link_up(struct brcm_pcie *pcie) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS); 6928c2ecf20Sopenharmony_ci u32 dla = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK, val); 6938c2ecf20Sopenharmony_ci u32 plu = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK, val); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci return dla && plu; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci/* Configuration space read/write support */ 6998c2ecf20Sopenharmony_cistatic inline int brcm_pcie_cfg_index(int busnr, int devfn, int reg) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci return ((PCI_SLOT(devfn) & 0x1f) << PCIE_EXT_SLOT_SHIFT) 7028c2ecf20Sopenharmony_ci | ((PCI_FUNC(devfn) & 0x07) << PCIE_EXT_FUNC_SHIFT) 7038c2ecf20Sopenharmony_ci | (busnr << PCIE_EXT_BUSNUM_SHIFT) 7048c2ecf20Sopenharmony_ci | (reg & ~3); 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn, 7088c2ecf20Sopenharmony_ci int where) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct brcm_pcie *pcie = bus->sysdata; 7118c2ecf20Sopenharmony_ci void __iomem *base = pcie->base; 7128c2ecf20Sopenharmony_ci int idx; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci /* Accesses to the RC go right to the RC registers if slot==0 */ 7158c2ecf20Sopenharmony_ci if (pci_is_root_bus(bus)) 7168c2ecf20Sopenharmony_ci return PCI_SLOT(devfn) ? NULL : base + where; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* For devices, write to the config space index register */ 7198c2ecf20Sopenharmony_ci idx = brcm_pcie_cfg_index(bus->number, devfn, 0); 7208c2ecf20Sopenharmony_ci writel(idx, pcie->base + PCIE_EXT_CFG_INDEX); 7218c2ecf20Sopenharmony_ci return base + PCIE_EXT_CFG_DATA + where; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic struct pci_ops brcm_pcie_ops = { 7258c2ecf20Sopenharmony_ci .map_bus = brcm_pcie_map_conf, 7268c2ecf20Sopenharmony_ci .read = pci_generic_config_read, 7278c2ecf20Sopenharmony_ci .write = pci_generic_config_write, 7288c2ecf20Sopenharmony_ci}; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci u32 tmp, mask = RGR1_SW_INIT_1_INIT_GENERIC_MASK; 7338c2ecf20Sopenharmony_ci u32 shift = RGR1_SW_INIT_1_INIT_GENERIC_SHIFT; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1(pcie)); 7368c2ecf20Sopenharmony_ci tmp = (tmp & ~mask) | ((val << shift) & mask); 7378c2ecf20Sopenharmony_ci writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie)); 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci u32 tmp, mask = RGR1_SW_INIT_1_INIT_7278_MASK; 7438c2ecf20Sopenharmony_ci u32 shift = RGR1_SW_INIT_1_INIT_7278_SHIFT; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1(pcie)); 7468c2ecf20Sopenharmony_ci tmp = (tmp & ~mask) | ((val << shift) & mask); 7478c2ecf20Sopenharmony_ci writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie)); 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci u32 tmp; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* Perst bit has moved and assert value is 0 */ 7558c2ecf20Sopenharmony_ci tmp = readl(pcie->base + PCIE_MISC_PCIE_CTRL); 7568c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, !val, PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK); 7578c2ecf20Sopenharmony_ci writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL); 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val) 7618c2ecf20Sopenharmony_ci{ 7628c2ecf20Sopenharmony_ci u32 tmp; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1(pcie)); 7658c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_PERST_MASK); 7668c2ecf20Sopenharmony_ci writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie)); 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie, 7708c2ecf20Sopenharmony_ci u64 *rc_bar2_size, 7718c2ecf20Sopenharmony_ci u64 *rc_bar2_offset) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); 7748c2ecf20Sopenharmony_ci struct resource_entry *entry; 7758c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 7768c2ecf20Sopenharmony_ci u64 lowest_pcie_addr = ~(u64)0; 7778c2ecf20Sopenharmony_ci int ret, i = 0; 7788c2ecf20Sopenharmony_ci u64 size = 0; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci resource_list_for_each_entry(entry, &bridge->dma_ranges) { 7818c2ecf20Sopenharmony_ci u64 pcie_beg = entry->res->start - entry->offset; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci size += entry->res->end - entry->res->start + 1; 7848c2ecf20Sopenharmony_ci if (pcie_beg < lowest_pcie_addr) 7858c2ecf20Sopenharmony_ci lowest_pcie_addr = pcie_beg; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (lowest_pcie_addr == ~(u64)0) { 7898c2ecf20Sopenharmony_ci dev_err(dev, "DT node has no dma-ranges\n"); 7908c2ecf20Sopenharmony_ci return -EINVAL; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci ret = of_property_read_variable_u64_array(pcie->np, "brcm,scb-sizes", pcie->memc_size, 1, 7948c2ecf20Sopenharmony_ci PCIE_BRCM_MAX_MEMC); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (ret <= 0) { 7978c2ecf20Sopenharmony_ci /* Make an educated guess */ 7988c2ecf20Sopenharmony_ci pcie->num_memc = 1; 7998c2ecf20Sopenharmony_ci pcie->memc_size[0] = 1ULL << fls64(size - 1); 8008c2ecf20Sopenharmony_ci } else { 8018c2ecf20Sopenharmony_ci pcie->num_memc = ret; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* Each memc is viewed through a "port" that is a power of 2 */ 8058c2ecf20Sopenharmony_ci for (i = 0, size = 0; i < pcie->num_memc; i++) 8068c2ecf20Sopenharmony_ci size += pcie->memc_size[i]; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* System memory starts at this address in PCIe-space */ 8098c2ecf20Sopenharmony_ci *rc_bar2_offset = lowest_pcie_addr; 8108c2ecf20Sopenharmony_ci /* The sum of all memc views must also be a power of 2 */ 8118c2ecf20Sopenharmony_ci *rc_bar2_size = 1ULL << fls64(size - 1); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci /* 8148c2ecf20Sopenharmony_ci * We validate the inbound memory view even though we should trust 8158c2ecf20Sopenharmony_ci * whatever the device-tree provides. This is because of an HW issue on 8168c2ecf20Sopenharmony_ci * early Raspberry Pi 4's revisions (bcm2711). It turns out its 8178c2ecf20Sopenharmony_ci * firmware has to dynamically edit dma-ranges due to a bug on the 8188c2ecf20Sopenharmony_ci * PCIe controller integration, which prohibits any access above the 8198c2ecf20Sopenharmony_ci * lower 3GB of memory. Given this, we decided to keep the dma-ranges 8208c2ecf20Sopenharmony_ci * in check, avoiding hard to debug device-tree related issues in the 8218c2ecf20Sopenharmony_ci * future: 8228c2ecf20Sopenharmony_ci * 8238c2ecf20Sopenharmony_ci * The PCIe host controller by design must set the inbound viewport to 8248c2ecf20Sopenharmony_ci * be a contiguous arrangement of all of the system's memory. In 8258c2ecf20Sopenharmony_ci * addition, its size mut be a power of two. To further complicate 8268c2ecf20Sopenharmony_ci * matters, the viewport must start on a pcie-address that is aligned 8278c2ecf20Sopenharmony_ci * on a multiple of its size. If a portion of the viewport does not 8288c2ecf20Sopenharmony_ci * represent system memory -- e.g. 3GB of memory requires a 4GB 8298c2ecf20Sopenharmony_ci * viewport -- we can map the outbound memory in or after 3GB and even 8308c2ecf20Sopenharmony_ci * though the viewport will overlap the outbound memory the controller 8318c2ecf20Sopenharmony_ci * will know to send outbound memory downstream and everything else 8328c2ecf20Sopenharmony_ci * upstream. 8338c2ecf20Sopenharmony_ci * 8348c2ecf20Sopenharmony_ci * For example: 8358c2ecf20Sopenharmony_ci * 8368c2ecf20Sopenharmony_ci * - The best-case scenario, memory up to 3GB, is to place the inbound 8378c2ecf20Sopenharmony_ci * region in the first 4GB of pcie-space, as some legacy devices can 8388c2ecf20Sopenharmony_ci * only address 32bits. We would also like to put the MSI under 4GB 8398c2ecf20Sopenharmony_ci * as well, since some devices require a 32bit MSI target address. 8408c2ecf20Sopenharmony_ci * 8418c2ecf20Sopenharmony_ci * - If the system memory is 4GB or larger we cannot start the inbound 8428c2ecf20Sopenharmony_ci * region at location 0 (since we have to allow some space for 8438c2ecf20Sopenharmony_ci * outbound memory @ 3GB). So instead it will start at the 1x 8448c2ecf20Sopenharmony_ci * multiple of its size 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci if (!*rc_bar2_size || (*rc_bar2_offset & (*rc_bar2_size - 1)) || 8478c2ecf20Sopenharmony_ci (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) { 8488c2ecf20Sopenharmony_ci dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n", 8498c2ecf20Sopenharmony_ci *rc_bar2_size, *rc_bar2_offset); 8508c2ecf20Sopenharmony_ci return -EINVAL; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci return 0; 8548c2ecf20Sopenharmony_ci} 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_cistatic int brcm_pcie_setup(struct brcm_pcie *pcie) 8578c2ecf20Sopenharmony_ci{ 8588c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); 8598c2ecf20Sopenharmony_ci u64 rc_bar2_offset, rc_bar2_size; 8608c2ecf20Sopenharmony_ci void __iomem *base = pcie->base; 8618c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 8628c2ecf20Sopenharmony_ci struct resource_entry *entry; 8638c2ecf20Sopenharmony_ci bool ssc_good = false; 8648c2ecf20Sopenharmony_ci struct resource *res; 8658c2ecf20Sopenharmony_ci int num_out_wins = 0; 8668c2ecf20Sopenharmony_ci u16 nlw, cls, lnksta; 8678c2ecf20Sopenharmony_ci int i, ret, memc; 8688c2ecf20Sopenharmony_ci u32 tmp, burst, aspm_support; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci /* Reset the bridge */ 8718c2ecf20Sopenharmony_ci pcie->bridge_sw_init_set(pcie, 1); 8728c2ecf20Sopenharmony_ci usleep_range(100, 200); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* Take the bridge out of reset */ 8758c2ecf20Sopenharmony_ci pcie->bridge_sw_init_set(pcie, 0); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); 8788c2ecf20Sopenharmony_ci tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK; 8798c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); 8808c2ecf20Sopenharmony_ci /* Wait for SerDes to be stable */ 8818c2ecf20Sopenharmony_ci usleep_range(100, 200); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* 8848c2ecf20Sopenharmony_ci * SCB_MAX_BURST_SIZE is a two bit field. For GENERIC chips it 8858c2ecf20Sopenharmony_ci * is encoded as 0=128, 1=256, 2=512, 3=Rsvd, for BCM7278 it 8868c2ecf20Sopenharmony_ci * is encoded as 0=Rsvd, 1=128, 2=256, 3=512. 8878c2ecf20Sopenharmony_ci */ 8888c2ecf20Sopenharmony_ci if (pcie->type == BCM2711) 8898c2ecf20Sopenharmony_ci burst = 0x0; /* 128B */ 8908c2ecf20Sopenharmony_ci else if (pcie->type == BCM7278) 8918c2ecf20Sopenharmony_ci burst = 0x3; /* 512 bytes */ 8928c2ecf20Sopenharmony_ci else 8938c2ecf20Sopenharmony_ci burst = 0x2; /* 512 bytes */ 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */ 8968c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_MISC_CTRL); 8978c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK); 8988c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK); 8998c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, burst, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK); 9008c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_MISC_CTRL); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size, 9038c2ecf20Sopenharmony_ci &rc_bar2_offset); 9048c2ecf20Sopenharmony_ci if (ret) 9058c2ecf20Sopenharmony_ci return ret; 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci tmp = lower_32_bits(rc_bar2_offset); 9088c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size), 9098c2ecf20Sopenharmony_ci PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK); 9108c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO); 9118c2ecf20Sopenharmony_ci writel(upper_32_bits(rc_bar2_offset), 9128c2ecf20Sopenharmony_ci base + PCIE_MISC_RC_BAR2_CONFIG_HI); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_MISC_CTRL); 9158c2ecf20Sopenharmony_ci for (memc = 0; memc < pcie->num_memc; memc++) { 9168c2ecf20Sopenharmony_ci u32 scb_size_val = ilog2(pcie->memc_size[memc]) - 15; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (memc == 0) 9198c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(0)); 9208c2ecf20Sopenharmony_ci else if (memc == 1) 9218c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(1)); 9228c2ecf20Sopenharmony_ci else if (memc == 2) 9238c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(2)); 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_MISC_CTRL); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* 9288c2ecf20Sopenharmony_ci * We ideally want the MSI target address to be located in the 32bit 9298c2ecf20Sopenharmony_ci * addressable memory area. Some devices might depend on it. This is 9308c2ecf20Sopenharmony_ci * possible either when the inbound window is located above the lower 9318c2ecf20Sopenharmony_ci * 4GB or when the inbound area is smaller than 4GB (taking into 9328c2ecf20Sopenharmony_ci * account the rounding-up we're forced to perform). 9338c2ecf20Sopenharmony_ci */ 9348c2ecf20Sopenharmony_ci if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G) 9358c2ecf20Sopenharmony_ci pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB; 9368c2ecf20Sopenharmony_ci else 9378c2ecf20Sopenharmony_ci pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* disable the PCIe->GISB memory window (RC_BAR1) */ 9408c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO); 9418c2ecf20Sopenharmony_ci tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK; 9428c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_RC_BAR1_CONFIG_LO); 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci /* disable the PCIe->SCB memory window (RC_BAR3) */ 9458c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_RC_BAR3_CONFIG_LO); 9468c2ecf20Sopenharmony_ci tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK; 9478c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (pcie->gen) 9508c2ecf20Sopenharmony_ci brcm_pcie_set_gen(pcie, pcie->gen); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci /* Unassert the fundamental reset */ 9538c2ecf20Sopenharmony_ci pcie->perst_set(pcie, 0); 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* 9568c2ecf20Sopenharmony_ci * Give the RC/EP time to wake up, before trying to configure RC. 9578c2ecf20Sopenharmony_ci * Intermittently check status for link-up, up to a total of 100ms. 9588c2ecf20Sopenharmony_ci */ 9598c2ecf20Sopenharmony_ci for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5) 9608c2ecf20Sopenharmony_ci msleep(5); 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci if (!brcm_pcie_link_up(pcie)) { 9638c2ecf20Sopenharmony_ci dev_err(dev, "link down\n"); 9648c2ecf20Sopenharmony_ci return -ENODEV; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if (!brcm_pcie_rc_mode(pcie)) { 9688c2ecf20Sopenharmony_ci dev_err(dev, "PCIe misconfigured; is in EP mode\n"); 9698c2ecf20Sopenharmony_ci return -EINVAL; 9708c2ecf20Sopenharmony_ci } 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci resource_list_for_each_entry(entry, &bridge->windows) { 9738c2ecf20Sopenharmony_ci res = entry->res; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci if (resource_type(res) != IORESOURCE_MEM) 9768c2ecf20Sopenharmony_ci continue; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (num_out_wins >= BRCM_NUM_PCIE_OUT_WINS) { 9798c2ecf20Sopenharmony_ci dev_err(pcie->dev, "too many outbound wins\n"); 9808c2ecf20Sopenharmony_ci return -EINVAL; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci brcm_pcie_set_outbound_win(pcie, num_out_wins, res->start, 9848c2ecf20Sopenharmony_ci res->start - entry->offset, 9858c2ecf20Sopenharmony_ci resource_size(res)); 9868c2ecf20Sopenharmony_ci num_out_wins++; 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci /* Don't advertise L0s capability if 'aspm-no-l0s' */ 9908c2ecf20Sopenharmony_ci aspm_support = PCIE_LINK_STATE_L1; 9918c2ecf20Sopenharmony_ci if (!of_property_read_bool(pcie->np, "aspm-no-l0s")) 9928c2ecf20Sopenharmony_ci aspm_support |= PCIE_LINK_STATE_L0S; 9938c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); 9948c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, aspm_support, 9958c2ecf20Sopenharmony_ci PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK); 9968c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci /* 9998c2ecf20Sopenharmony_ci * For config space accesses on the RC, show the right class for 10008c2ecf20Sopenharmony_ci * a PCIe-PCIe bridge (the default setting is to be EP mode). 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3); 10038c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, 0x060400, 10048c2ecf20Sopenharmony_ci PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK); 10058c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (pcie->ssc) { 10088c2ecf20Sopenharmony_ci ret = brcm_pcie_set_ssc(pcie); 10098c2ecf20Sopenharmony_ci if (ret == 0) 10108c2ecf20Sopenharmony_ci ssc_good = true; 10118c2ecf20Sopenharmony_ci else 10128c2ecf20Sopenharmony_ci dev_err(dev, "failed attempt to enter ssc mode\n"); 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA); 10168c2ecf20Sopenharmony_ci cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta); 10178c2ecf20Sopenharmony_ci nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta); 10188c2ecf20Sopenharmony_ci dev_info(dev, "link up, %s x%u %s\n", 10198c2ecf20Sopenharmony_ci pci_speed_string(pcie_link_speed[cls]), nlw, 10208c2ecf20Sopenharmony_ci ssc_good ? "(SSC)" : "(!SSC)"); 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* PCIe->SCB endian mode for BAR */ 10238c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1); 10248c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, PCIE_RC_CFG_VENDOR_SPCIFIC_REG1_LITTLE_ENDIAN, 10258c2ecf20Sopenharmony_ci PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1_ENDIAN_MODE_BAR2_MASK); 10268c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci /* 10298c2ecf20Sopenharmony_ci * Refclk from RC should be gated with CLKREQ# input when ASPM L0s,L1 10308c2ecf20Sopenharmony_ci * is enabled => setting the CLKREQ_DEBUG_ENABLE field to 1. 10318c2ecf20Sopenharmony_ci */ 10328c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); 10338c2ecf20Sopenharmony_ci tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK; 10348c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci return 0; 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci/* L23 is a low-power PCIe link state */ 10408c2ecf20Sopenharmony_cistatic void brcm_pcie_enter_l23(struct brcm_pcie *pcie) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci void __iomem *base = pcie->base; 10438c2ecf20Sopenharmony_ci int l23, i; 10448c2ecf20Sopenharmony_ci u32 tmp; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* Assert request for L23 */ 10478c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_PCIE_CTRL); 10488c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, 1, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK); 10498c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_PCIE_CTRL); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci /* Wait up to 36 msec for L23 */ 10528c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_PCIE_STATUS); 10538c2ecf20Sopenharmony_ci l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, tmp); 10548c2ecf20Sopenharmony_ci for (i = 0; i < 15 && !l23; i++) { 10558c2ecf20Sopenharmony_ci usleep_range(2000, 2400); 10568c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_PCIE_STATUS); 10578c2ecf20Sopenharmony_ci l23 = FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK, 10588c2ecf20Sopenharmony_ci tmp); 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (!l23) 10628c2ecf20Sopenharmony_ci dev_err(pcie->dev, "failed to enter low-power link state\n"); 10638c2ecf20Sopenharmony_ci} 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_cistatic int brcm_phy_cntl(struct brcm_pcie *pcie, const int start) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci static const u32 shifts[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = { 10688c2ecf20Sopenharmony_ci PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT, 10698c2ecf20Sopenharmony_ci PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT, 10708c2ecf20Sopenharmony_ci PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_SHIFT,}; 10718c2ecf20Sopenharmony_ci static const u32 masks[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = { 10728c2ecf20Sopenharmony_ci PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_MASK, 10738c2ecf20Sopenharmony_ci PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_MASK, 10748c2ecf20Sopenharmony_ci PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_MASK,}; 10758c2ecf20Sopenharmony_ci const int beg = start ? 0 : PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS - 1; 10768c2ecf20Sopenharmony_ci const int end = start ? PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS : -1; 10778c2ecf20Sopenharmony_ci u32 tmp, combined_mask = 0; 10788c2ecf20Sopenharmony_ci u32 val; 10798c2ecf20Sopenharmony_ci void __iomem *base = pcie->base; 10808c2ecf20Sopenharmony_ci int i, ret; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci for (i = beg; i != end; start ? i++ : i--) { 10838c2ecf20Sopenharmony_ci val = start ? BIT_MASK(shifts[i]) : 0; 10848c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_DVT_PMU_PCIE_PHY_CTRL); 10858c2ecf20Sopenharmony_ci tmp = (tmp & ~masks[i]) | (val & masks[i]); 10868c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_DVT_PMU_PCIE_PHY_CTRL); 10878c2ecf20Sopenharmony_ci usleep_range(50, 200); 10888c2ecf20Sopenharmony_ci combined_mask |= masks[i]; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_DVT_PMU_PCIE_PHY_CTRL); 10928c2ecf20Sopenharmony_ci val = start ? combined_mask : 0; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci ret = (tmp & combined_mask) == val ? 0 : -EIO; 10958c2ecf20Sopenharmony_ci if (ret) 10968c2ecf20Sopenharmony_ci dev_err(pcie->dev, "failed to %s phy\n", (start ? "start" : "stop")); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci return ret; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_cistatic inline int brcm_phy_start(struct brcm_pcie *pcie) 11028c2ecf20Sopenharmony_ci{ 11038c2ecf20Sopenharmony_ci return pcie->rescal ? brcm_phy_cntl(pcie, 1) : 0; 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic inline int brcm_phy_stop(struct brcm_pcie *pcie) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci return pcie->rescal ? brcm_phy_cntl(pcie, 0) : 0; 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_cistatic void brcm_pcie_turn_off(struct brcm_pcie *pcie) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci void __iomem *base = pcie->base; 11148c2ecf20Sopenharmony_ci int tmp; 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci if (brcm_pcie_link_up(pcie)) 11178c2ecf20Sopenharmony_ci brcm_pcie_enter_l23(pcie); 11188c2ecf20Sopenharmony_ci /* Assert fundamental reset */ 11198c2ecf20Sopenharmony_ci pcie->perst_set(pcie, 1); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* Deassert request for L23 in case it was asserted */ 11228c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_PCIE_CTRL); 11238c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, 0, PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK); 11248c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_PCIE_CTRL); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci /* Turn off SerDes */ 11278c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); 11288c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); 11298c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci /* Shutdown PCIe bridge */ 11328c2ecf20Sopenharmony_ci pcie->bridge_sw_init_set(pcie, 1); 11338c2ecf20Sopenharmony_ci} 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_cistatic int brcm_pcie_suspend(struct device *dev) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci struct brcm_pcie *pcie = dev_get_drvdata(dev); 11388c2ecf20Sopenharmony_ci int ret; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci brcm_pcie_turn_off(pcie); 11418c2ecf20Sopenharmony_ci ret = brcm_phy_stop(pcie); 11428c2ecf20Sopenharmony_ci clk_disable_unprepare(pcie->clk); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return ret; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic int brcm_pcie_resume(struct device *dev) 11488c2ecf20Sopenharmony_ci{ 11498c2ecf20Sopenharmony_ci struct brcm_pcie *pcie = dev_get_drvdata(dev); 11508c2ecf20Sopenharmony_ci void __iomem *base; 11518c2ecf20Sopenharmony_ci u32 tmp; 11528c2ecf20Sopenharmony_ci int ret; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci base = pcie->base; 11558c2ecf20Sopenharmony_ci clk_prepare_enable(pcie->clk); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci ret = brcm_phy_start(pcie); 11588c2ecf20Sopenharmony_ci if (ret) 11598c2ecf20Sopenharmony_ci goto err; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci /* Take bridge out of reset so we can access the SERDES reg */ 11628c2ecf20Sopenharmony_ci pcie->bridge_sw_init_set(pcie, 0); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* SERDES_IDDQ = 0 */ 11658c2ecf20Sopenharmony_ci tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); 11668c2ecf20Sopenharmony_ci u32p_replace_bits(&tmp, 0, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK); 11678c2ecf20Sopenharmony_ci writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci /* wait for serdes to be stable */ 11708c2ecf20Sopenharmony_ci udelay(100); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci ret = brcm_pcie_setup(pcie); 11738c2ecf20Sopenharmony_ci if (ret) 11748c2ecf20Sopenharmony_ci goto err; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci if (pcie->msi) 11778c2ecf20Sopenharmony_ci brcm_msi_set_regs(pcie->msi); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci return 0; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_cierr: 11828c2ecf20Sopenharmony_ci clk_disable_unprepare(pcie->clk); 11838c2ecf20Sopenharmony_ci return ret; 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic void __brcm_pcie_remove(struct brcm_pcie *pcie) 11878c2ecf20Sopenharmony_ci{ 11888c2ecf20Sopenharmony_ci brcm_msi_remove(pcie); 11898c2ecf20Sopenharmony_ci brcm_pcie_turn_off(pcie); 11908c2ecf20Sopenharmony_ci brcm_phy_stop(pcie); 11918c2ecf20Sopenharmony_ci reset_control_assert(pcie->rescal); 11928c2ecf20Sopenharmony_ci clk_disable_unprepare(pcie->clk); 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int brcm_pcie_remove(struct platform_device *pdev) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct brcm_pcie *pcie = platform_get_drvdata(pdev); 11988c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci pci_stop_root_bus(bridge->bus); 12018c2ecf20Sopenharmony_ci pci_remove_root_bus(bridge->bus); 12028c2ecf20Sopenharmony_ci __brcm_pcie_remove(pcie); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci return 0; 12058c2ecf20Sopenharmony_ci} 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_cistatic const struct of_device_id brcm_pcie_match[] = { 12088c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg }, 12098c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg }, 12108c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm7278-pcie", .data = &bcm7278_cfg }, 12118c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm7216-pcie", .data = &bcm7278_cfg }, 12128c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm7445-pcie", .data = &generic_cfg }, 12138c2ecf20Sopenharmony_ci {}, 12148c2ecf20Sopenharmony_ci}; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_cistatic int brcm_pcie_probe(struct platform_device *pdev) 12178c2ecf20Sopenharmony_ci{ 12188c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node, *msi_np; 12198c2ecf20Sopenharmony_ci struct pci_host_bridge *bridge; 12208c2ecf20Sopenharmony_ci const struct pcie_cfg_data *data; 12218c2ecf20Sopenharmony_ci struct brcm_pcie *pcie; 12228c2ecf20Sopenharmony_ci int ret; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci bridge = devm_pci_alloc_host_bridge(&pdev->dev, sizeof(*pcie)); 12258c2ecf20Sopenharmony_ci if (!bridge) 12268c2ecf20Sopenharmony_ci return -ENOMEM; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci data = of_device_get_match_data(&pdev->dev); 12298c2ecf20Sopenharmony_ci if (!data) { 12308c2ecf20Sopenharmony_ci pr_err("failed to look up compatible string\n"); 12318c2ecf20Sopenharmony_ci return -EINVAL; 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci pcie = pci_host_bridge_priv(bridge); 12358c2ecf20Sopenharmony_ci pcie->dev = &pdev->dev; 12368c2ecf20Sopenharmony_ci pcie->np = np; 12378c2ecf20Sopenharmony_ci pcie->reg_offsets = data->offsets; 12388c2ecf20Sopenharmony_ci pcie->type = data->type; 12398c2ecf20Sopenharmony_ci pcie->perst_set = data->perst_set; 12408c2ecf20Sopenharmony_ci pcie->bridge_sw_init_set = data->bridge_sw_init_set; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci pcie->base = devm_platform_ioremap_resource(pdev, 0); 12438c2ecf20Sopenharmony_ci if (IS_ERR(pcie->base)) 12448c2ecf20Sopenharmony_ci return PTR_ERR(pcie->base); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci pcie->clk = devm_clk_get_optional(&pdev->dev, "sw_pcie"); 12478c2ecf20Sopenharmony_ci if (IS_ERR(pcie->clk)) 12488c2ecf20Sopenharmony_ci return PTR_ERR(pcie->clk); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci ret = of_pci_get_max_link_speed(np); 12518c2ecf20Sopenharmony_ci pcie->gen = (ret < 0) ? 0 : ret; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc"); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pcie->clk); 12568c2ecf20Sopenharmony_ci if (ret) { 12578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not enable clock\n"); 12588c2ecf20Sopenharmony_ci return ret; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci pcie->rescal = devm_reset_control_get_optional_shared(&pdev->dev, "rescal"); 12618c2ecf20Sopenharmony_ci if (IS_ERR(pcie->rescal)) { 12628c2ecf20Sopenharmony_ci clk_disable_unprepare(pcie->clk); 12638c2ecf20Sopenharmony_ci return PTR_ERR(pcie->rescal); 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci ret = reset_control_deassert(pcie->rescal); 12678c2ecf20Sopenharmony_ci if (ret) 12688c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to deassert 'rescal'\n"); 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci ret = brcm_phy_start(pcie); 12718c2ecf20Sopenharmony_ci if (ret) { 12728c2ecf20Sopenharmony_ci reset_control_assert(pcie->rescal); 12738c2ecf20Sopenharmony_ci clk_disable_unprepare(pcie->clk); 12748c2ecf20Sopenharmony_ci return ret; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci ret = brcm_pcie_setup(pcie); 12788c2ecf20Sopenharmony_ci if (ret) 12798c2ecf20Sopenharmony_ci goto fail; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci pcie->hw_rev = readl(pcie->base + PCIE_MISC_REVISION); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci msi_np = of_parse_phandle(pcie->np, "msi-parent", 0); 12848c2ecf20Sopenharmony_ci if (pci_msi_enabled() && msi_np == pcie->np) { 12858c2ecf20Sopenharmony_ci ret = brcm_pcie_enable_msi(pcie); 12868c2ecf20Sopenharmony_ci if (ret) { 12878c2ecf20Sopenharmony_ci dev_err(pcie->dev, "probe of internal MSI failed"); 12888c2ecf20Sopenharmony_ci goto fail; 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci } 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci bridge->ops = &brcm_pcie_ops; 12938c2ecf20Sopenharmony_ci bridge->sysdata = pcie; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pcie); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci return pci_host_probe(bridge); 12988c2ecf20Sopenharmony_cifail: 12998c2ecf20Sopenharmony_ci __brcm_pcie_remove(pcie); 13008c2ecf20Sopenharmony_ci return ret; 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, brcm_pcie_match); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_cistatic const struct dev_pm_ops brcm_pcie_pm_ops = { 13068c2ecf20Sopenharmony_ci .suspend = brcm_pcie_suspend, 13078c2ecf20Sopenharmony_ci .resume = brcm_pcie_resume, 13088c2ecf20Sopenharmony_ci}; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_cistatic struct platform_driver brcm_pcie_driver = { 13118c2ecf20Sopenharmony_ci .probe = brcm_pcie_probe, 13128c2ecf20Sopenharmony_ci .remove = brcm_pcie_remove, 13138c2ecf20Sopenharmony_ci .driver = { 13148c2ecf20Sopenharmony_ci .name = "brcm-pcie", 13158c2ecf20Sopenharmony_ci .of_match_table = brcm_pcie_match, 13168c2ecf20Sopenharmony_ci .pm = &brcm_pcie_pm_ops, 13178c2ecf20Sopenharmony_ci }, 13188c2ecf20Sopenharmony_ci}; 13198c2ecf20Sopenharmony_cimodule_platform_driver(brcm_pcie_driver); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 13228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom STB PCIe RC driver"); 13238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 1324