18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2014 Hauke Mehrtens <hauke@hauke-m.de> 48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Broadcom Corporation 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/kernel.h> 88c2ecf20Sopenharmony_ci#include <linux/pci.h> 98c2ecf20Sopenharmony_ci#include <linux/msi.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/mbus.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-gic-v3.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/of_address.h> 198c2ecf20Sopenharmony_ci#include <linux/of_pci.h> 208c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 218c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 228c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "pcie-iproc.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define EP_PERST_SOURCE_SELECT_SHIFT 2 278c2ecf20Sopenharmony_ci#define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT) 288c2ecf20Sopenharmony_ci#define EP_MODE_SURVIVE_PERST_SHIFT 1 298c2ecf20Sopenharmony_ci#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT) 308c2ecf20Sopenharmony_ci#define RC_PCIE_RST_OUTPUT_SHIFT 0 318c2ecf20Sopenharmony_ci#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT) 328c2ecf20Sopenharmony_ci#define PAXC_RESET_MASK 0x7f 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define GIC_V3_CFG_SHIFT 0 358c2ecf20Sopenharmony_ci#define GIC_V3_CFG BIT(GIC_V3_CFG_SHIFT) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define MSI_ENABLE_CFG_SHIFT 0 388c2ecf20Sopenharmony_ci#define MSI_ENABLE_CFG BIT(MSI_ENABLE_CFG_SHIFT) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define CFG_IND_ADDR_MASK 0x00001ffc 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define CFG_ADDR_BUS_NUM_SHIFT 20 438c2ecf20Sopenharmony_ci#define CFG_ADDR_BUS_NUM_MASK 0x0ff00000 448c2ecf20Sopenharmony_ci#define CFG_ADDR_DEV_NUM_SHIFT 15 458c2ecf20Sopenharmony_ci#define CFG_ADDR_DEV_NUM_MASK 0x000f8000 468c2ecf20Sopenharmony_ci#define CFG_ADDR_FUNC_NUM_SHIFT 12 478c2ecf20Sopenharmony_ci#define CFG_ADDR_FUNC_NUM_MASK 0x00007000 488c2ecf20Sopenharmony_ci#define CFG_ADDR_REG_NUM_SHIFT 2 498c2ecf20Sopenharmony_ci#define CFG_ADDR_REG_NUM_MASK 0x00000ffc 508c2ecf20Sopenharmony_ci#define CFG_ADDR_CFG_TYPE_SHIFT 0 518c2ecf20Sopenharmony_ci#define CFG_ADDR_CFG_TYPE_MASK 0x00000003 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define SYS_RC_INTX_MASK 0xf 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define PCIE_PHYLINKUP_SHIFT 3 568c2ecf20Sopenharmony_ci#define PCIE_PHYLINKUP BIT(PCIE_PHYLINKUP_SHIFT) 578c2ecf20Sopenharmony_ci#define PCIE_DL_ACTIVE_SHIFT 2 588c2ecf20Sopenharmony_ci#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define APB_ERR_EN_SHIFT 0 618c2ecf20Sopenharmony_ci#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define CFG_RD_SUCCESS 0 648c2ecf20Sopenharmony_ci#define CFG_RD_UR 1 658c2ecf20Sopenharmony_ci#define CFG_RD_CRS 2 668c2ecf20Sopenharmony_ci#define CFG_RD_CA 3 678c2ecf20Sopenharmony_ci#define CFG_RETRY_STATUS 0xffff0001 688c2ecf20Sopenharmony_ci#define CFG_RETRY_STATUS_TIMEOUT_US 500000 /* 500 milliseconds */ 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* derive the enum index of the outbound/inbound mapping registers */ 718c2ecf20Sopenharmony_ci#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/* 748c2ecf20Sopenharmony_ci * Maximum number of outbound mapping window sizes that can be supported by any 758c2ecf20Sopenharmony_ci * OARR/OMAP mapping pair 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci#define MAX_NUM_OB_WINDOW_SIZES 4 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define OARR_VALID_SHIFT 0 808c2ecf20Sopenharmony_ci#define OARR_VALID BIT(OARR_VALID_SHIFT) 818c2ecf20Sopenharmony_ci#define OARR_SIZE_CFG_SHIFT 1 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* 848c2ecf20Sopenharmony_ci * Maximum number of inbound mapping region sizes that can be supported by an 858c2ecf20Sopenharmony_ci * IARR 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci#define MAX_NUM_IB_REGION_SIZES 9 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define IMAP_VALID_SHIFT 0 908c2ecf20Sopenharmony_ci#define IMAP_VALID BIT(IMAP_VALID_SHIFT) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define IPROC_PCI_PM_CAP 0x48 938c2ecf20Sopenharmony_ci#define IPROC_PCI_PM_CAP_MASK 0xffff 948c2ecf20Sopenharmony_ci#define IPROC_PCI_EXP_CAP 0xac 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define IPROC_PCIE_REG_INVALID 0xffff 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/** 998c2ecf20Sopenharmony_ci * iProc PCIe outbound mapping controller specific parameters 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * @window_sizes: list of supported outbound mapping window sizes in MB 1028c2ecf20Sopenharmony_ci * @nr_sizes: number of supported outbound mapping window sizes 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistruct iproc_pcie_ob_map { 1058c2ecf20Sopenharmony_ci resource_size_t window_sizes[MAX_NUM_OB_WINDOW_SIZES]; 1068c2ecf20Sopenharmony_ci unsigned int nr_sizes; 1078c2ecf20Sopenharmony_ci}; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic const struct iproc_pcie_ob_map paxb_ob_map[] = { 1108c2ecf20Sopenharmony_ci { 1118c2ecf20Sopenharmony_ci /* OARR0/OMAP0 */ 1128c2ecf20Sopenharmony_ci .window_sizes = { 128, 256 }, 1138c2ecf20Sopenharmony_ci .nr_sizes = 2, 1148c2ecf20Sopenharmony_ci }, 1158c2ecf20Sopenharmony_ci { 1168c2ecf20Sopenharmony_ci /* OARR1/OMAP1 */ 1178c2ecf20Sopenharmony_ci .window_sizes = { 128, 256 }, 1188c2ecf20Sopenharmony_ci .nr_sizes = 2, 1198c2ecf20Sopenharmony_ci }, 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic const struct iproc_pcie_ob_map paxb_v2_ob_map[] = { 1238c2ecf20Sopenharmony_ci { 1248c2ecf20Sopenharmony_ci /* OARR0/OMAP0 */ 1258c2ecf20Sopenharmony_ci .window_sizes = { 128, 256 }, 1268c2ecf20Sopenharmony_ci .nr_sizes = 2, 1278c2ecf20Sopenharmony_ci }, 1288c2ecf20Sopenharmony_ci { 1298c2ecf20Sopenharmony_ci /* OARR1/OMAP1 */ 1308c2ecf20Sopenharmony_ci .window_sizes = { 128, 256 }, 1318c2ecf20Sopenharmony_ci .nr_sizes = 2, 1328c2ecf20Sopenharmony_ci }, 1338c2ecf20Sopenharmony_ci { 1348c2ecf20Sopenharmony_ci /* OARR2/OMAP2 */ 1358c2ecf20Sopenharmony_ci .window_sizes = { 128, 256, 512, 1024 }, 1368c2ecf20Sopenharmony_ci .nr_sizes = 4, 1378c2ecf20Sopenharmony_ci }, 1388c2ecf20Sopenharmony_ci { 1398c2ecf20Sopenharmony_ci /* OARR3/OMAP3 */ 1408c2ecf20Sopenharmony_ci .window_sizes = { 128, 256, 512, 1024 }, 1418c2ecf20Sopenharmony_ci .nr_sizes = 4, 1428c2ecf20Sopenharmony_ci }, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/** 1468c2ecf20Sopenharmony_ci * iProc PCIe inbound mapping type 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_cienum iproc_pcie_ib_map_type { 1498c2ecf20Sopenharmony_ci /* for DDR memory */ 1508c2ecf20Sopenharmony_ci IPROC_PCIE_IB_MAP_MEM = 0, 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* for device I/O memory */ 1538c2ecf20Sopenharmony_ci IPROC_PCIE_IB_MAP_IO, 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* invalid or unused */ 1568c2ecf20Sopenharmony_ci IPROC_PCIE_IB_MAP_INVALID 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/** 1608c2ecf20Sopenharmony_ci * iProc PCIe inbound mapping controller specific parameters 1618c2ecf20Sopenharmony_ci * 1628c2ecf20Sopenharmony_ci * @type: inbound mapping region type 1638c2ecf20Sopenharmony_ci * @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or 1648c2ecf20Sopenharmony_ci * SZ_1G 1658c2ecf20Sopenharmony_ci * @region_sizes: list of supported inbound mapping region sizes in KB, MB, or 1668c2ecf20Sopenharmony_ci * GB, depending on the size unit 1678c2ecf20Sopenharmony_ci * @nr_sizes: number of supported inbound mapping region sizes 1688c2ecf20Sopenharmony_ci * @nr_windows: number of supported inbound mapping windows for the region 1698c2ecf20Sopenharmony_ci * @imap_addr_offset: register offset between the upper and lower 32-bit 1708c2ecf20Sopenharmony_ci * IMAP address registers 1718c2ecf20Sopenharmony_ci * @imap_window_offset: register offset between each IMAP window 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cistruct iproc_pcie_ib_map { 1748c2ecf20Sopenharmony_ci enum iproc_pcie_ib_map_type type; 1758c2ecf20Sopenharmony_ci unsigned int size_unit; 1768c2ecf20Sopenharmony_ci resource_size_t region_sizes[MAX_NUM_IB_REGION_SIZES]; 1778c2ecf20Sopenharmony_ci unsigned int nr_sizes; 1788c2ecf20Sopenharmony_ci unsigned int nr_windows; 1798c2ecf20Sopenharmony_ci u16 imap_addr_offset; 1808c2ecf20Sopenharmony_ci u16 imap_window_offset; 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic const struct iproc_pcie_ib_map paxb_v2_ib_map[] = { 1848c2ecf20Sopenharmony_ci { 1858c2ecf20Sopenharmony_ci /* IARR0/IMAP0 */ 1868c2ecf20Sopenharmony_ci .type = IPROC_PCIE_IB_MAP_IO, 1878c2ecf20Sopenharmony_ci .size_unit = SZ_1K, 1888c2ecf20Sopenharmony_ci .region_sizes = { 32 }, 1898c2ecf20Sopenharmony_ci .nr_sizes = 1, 1908c2ecf20Sopenharmony_ci .nr_windows = 8, 1918c2ecf20Sopenharmony_ci .imap_addr_offset = 0x40, 1928c2ecf20Sopenharmony_ci .imap_window_offset = 0x4, 1938c2ecf20Sopenharmony_ci }, 1948c2ecf20Sopenharmony_ci { 1958c2ecf20Sopenharmony_ci /* IARR1/IMAP1 */ 1968c2ecf20Sopenharmony_ci .type = IPROC_PCIE_IB_MAP_MEM, 1978c2ecf20Sopenharmony_ci .size_unit = SZ_1M, 1988c2ecf20Sopenharmony_ci .region_sizes = { 8 }, 1998c2ecf20Sopenharmony_ci .nr_sizes = 1, 2008c2ecf20Sopenharmony_ci .nr_windows = 8, 2018c2ecf20Sopenharmony_ci .imap_addr_offset = 0x4, 2028c2ecf20Sopenharmony_ci .imap_window_offset = 0x8, 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci }, 2058c2ecf20Sopenharmony_ci { 2068c2ecf20Sopenharmony_ci /* IARR2/IMAP2 */ 2078c2ecf20Sopenharmony_ci .type = IPROC_PCIE_IB_MAP_MEM, 2088c2ecf20Sopenharmony_ci .size_unit = SZ_1M, 2098c2ecf20Sopenharmony_ci .region_sizes = { 64, 128, 256, 512, 1024, 2048, 4096, 8192, 2108c2ecf20Sopenharmony_ci 16384 }, 2118c2ecf20Sopenharmony_ci .nr_sizes = 9, 2128c2ecf20Sopenharmony_ci .nr_windows = 1, 2138c2ecf20Sopenharmony_ci .imap_addr_offset = 0x4, 2148c2ecf20Sopenharmony_ci .imap_window_offset = 0x8, 2158c2ecf20Sopenharmony_ci }, 2168c2ecf20Sopenharmony_ci { 2178c2ecf20Sopenharmony_ci /* IARR3/IMAP3 */ 2188c2ecf20Sopenharmony_ci .type = IPROC_PCIE_IB_MAP_MEM, 2198c2ecf20Sopenharmony_ci .size_unit = SZ_1G, 2208c2ecf20Sopenharmony_ci .region_sizes = { 1, 2, 4, 8, 16, 32 }, 2218c2ecf20Sopenharmony_ci .nr_sizes = 6, 2228c2ecf20Sopenharmony_ci .nr_windows = 8, 2238c2ecf20Sopenharmony_ci .imap_addr_offset = 0x4, 2248c2ecf20Sopenharmony_ci .imap_window_offset = 0x8, 2258c2ecf20Sopenharmony_ci }, 2268c2ecf20Sopenharmony_ci { 2278c2ecf20Sopenharmony_ci /* IARR4/IMAP4 */ 2288c2ecf20Sopenharmony_ci .type = IPROC_PCIE_IB_MAP_MEM, 2298c2ecf20Sopenharmony_ci .size_unit = SZ_1G, 2308c2ecf20Sopenharmony_ci .region_sizes = { 32, 64, 128, 256, 512 }, 2318c2ecf20Sopenharmony_ci .nr_sizes = 5, 2328c2ecf20Sopenharmony_ci .nr_windows = 8, 2338c2ecf20Sopenharmony_ci .imap_addr_offset = 0x4, 2348c2ecf20Sopenharmony_ci .imap_window_offset = 0x8, 2358c2ecf20Sopenharmony_ci }, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* 2398c2ecf20Sopenharmony_ci * iProc PCIe host registers 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_cienum iproc_pcie_reg { 2428c2ecf20Sopenharmony_ci /* clock/reset signal control */ 2438c2ecf20Sopenharmony_ci IPROC_PCIE_CLK_CTRL = 0, 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * To allow MSI to be steered to an external MSI controller (e.g., ARM 2478c2ecf20Sopenharmony_ci * GICv3 ITS) 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci IPROC_PCIE_MSI_GIC_MODE, 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * IPROC_PCIE_MSI_BASE_ADDR and IPROC_PCIE_MSI_WINDOW_SIZE define the 2538c2ecf20Sopenharmony_ci * window where the MSI posted writes are written, for the writes to be 2548c2ecf20Sopenharmony_ci * interpreted as MSI writes. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci IPROC_PCIE_MSI_BASE_ADDR, 2578c2ecf20Sopenharmony_ci IPROC_PCIE_MSI_WINDOW_SIZE, 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* 2608c2ecf20Sopenharmony_ci * To hold the address of the register where the MSI writes are 2618c2ecf20Sopenharmony_ci * programed. When ARM GICv3 ITS is used, this should be programmed 2628c2ecf20Sopenharmony_ci * with the address of the GITS_TRANSLATER register. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci IPROC_PCIE_MSI_ADDR_LO, 2658c2ecf20Sopenharmony_ci IPROC_PCIE_MSI_ADDR_HI, 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* enable MSI */ 2688c2ecf20Sopenharmony_ci IPROC_PCIE_MSI_EN_CFG, 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* allow access to root complex configuration space */ 2718c2ecf20Sopenharmony_ci IPROC_PCIE_CFG_IND_ADDR, 2728c2ecf20Sopenharmony_ci IPROC_PCIE_CFG_IND_DATA, 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* allow access to device configuration space */ 2758c2ecf20Sopenharmony_ci IPROC_PCIE_CFG_ADDR, 2768c2ecf20Sopenharmony_ci IPROC_PCIE_CFG_DATA, 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* enable INTx */ 2798c2ecf20Sopenharmony_ci IPROC_PCIE_INTX_EN, 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* outbound address mapping */ 2828c2ecf20Sopenharmony_ci IPROC_PCIE_OARR0, 2838c2ecf20Sopenharmony_ci IPROC_PCIE_OMAP0, 2848c2ecf20Sopenharmony_ci IPROC_PCIE_OARR1, 2858c2ecf20Sopenharmony_ci IPROC_PCIE_OMAP1, 2868c2ecf20Sopenharmony_ci IPROC_PCIE_OARR2, 2878c2ecf20Sopenharmony_ci IPROC_PCIE_OMAP2, 2888c2ecf20Sopenharmony_ci IPROC_PCIE_OARR3, 2898c2ecf20Sopenharmony_ci IPROC_PCIE_OMAP3, 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* inbound address mapping */ 2928c2ecf20Sopenharmony_ci IPROC_PCIE_IARR0, 2938c2ecf20Sopenharmony_ci IPROC_PCIE_IMAP0, 2948c2ecf20Sopenharmony_ci IPROC_PCIE_IARR1, 2958c2ecf20Sopenharmony_ci IPROC_PCIE_IMAP1, 2968c2ecf20Sopenharmony_ci IPROC_PCIE_IARR2, 2978c2ecf20Sopenharmony_ci IPROC_PCIE_IMAP2, 2988c2ecf20Sopenharmony_ci IPROC_PCIE_IARR3, 2998c2ecf20Sopenharmony_ci IPROC_PCIE_IMAP3, 3008c2ecf20Sopenharmony_ci IPROC_PCIE_IARR4, 3018c2ecf20Sopenharmony_ci IPROC_PCIE_IMAP4, 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* config read status */ 3048c2ecf20Sopenharmony_ci IPROC_PCIE_CFG_RD_STATUS, 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* link status */ 3078c2ecf20Sopenharmony_ci IPROC_PCIE_LINK_STATUS, 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* enable APB error for unsupported requests */ 3108c2ecf20Sopenharmony_ci IPROC_PCIE_APB_ERR_EN, 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* total number of core registers */ 3138c2ecf20Sopenharmony_ci IPROC_PCIE_MAX_NUM_REG, 3148c2ecf20Sopenharmony_ci}; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci/* iProc PCIe PAXB BCMA registers */ 3178c2ecf20Sopenharmony_cistatic const u16 iproc_pcie_reg_paxb_bcma[IPROC_PCIE_MAX_NUM_REG] = { 3188c2ecf20Sopenharmony_ci [IPROC_PCIE_CLK_CTRL] = 0x000, 3198c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_IND_ADDR] = 0x120, 3208c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_IND_DATA] = 0x124, 3218c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_ADDR] = 0x1f8, 3228c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_DATA] = 0x1fc, 3238c2ecf20Sopenharmony_ci [IPROC_PCIE_INTX_EN] = 0x330, 3248c2ecf20Sopenharmony_ci [IPROC_PCIE_LINK_STATUS] = 0xf0c, 3258c2ecf20Sopenharmony_ci}; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci/* iProc PCIe PAXB registers */ 3288c2ecf20Sopenharmony_cistatic const u16 iproc_pcie_reg_paxb[IPROC_PCIE_MAX_NUM_REG] = { 3298c2ecf20Sopenharmony_ci [IPROC_PCIE_CLK_CTRL] = 0x000, 3308c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_IND_ADDR] = 0x120, 3318c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_IND_DATA] = 0x124, 3328c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_ADDR] = 0x1f8, 3338c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_DATA] = 0x1fc, 3348c2ecf20Sopenharmony_ci [IPROC_PCIE_INTX_EN] = 0x330, 3358c2ecf20Sopenharmony_ci [IPROC_PCIE_OARR0] = 0xd20, 3368c2ecf20Sopenharmony_ci [IPROC_PCIE_OMAP0] = 0xd40, 3378c2ecf20Sopenharmony_ci [IPROC_PCIE_OARR1] = 0xd28, 3388c2ecf20Sopenharmony_ci [IPROC_PCIE_OMAP1] = 0xd48, 3398c2ecf20Sopenharmony_ci [IPROC_PCIE_LINK_STATUS] = 0xf0c, 3408c2ecf20Sopenharmony_ci [IPROC_PCIE_APB_ERR_EN] = 0xf40, 3418c2ecf20Sopenharmony_ci}; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/* iProc PCIe PAXB v2 registers */ 3448c2ecf20Sopenharmony_cistatic const u16 iproc_pcie_reg_paxb_v2[IPROC_PCIE_MAX_NUM_REG] = { 3458c2ecf20Sopenharmony_ci [IPROC_PCIE_CLK_CTRL] = 0x000, 3468c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_IND_ADDR] = 0x120, 3478c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_IND_DATA] = 0x124, 3488c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_ADDR] = 0x1f8, 3498c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_DATA] = 0x1fc, 3508c2ecf20Sopenharmony_ci [IPROC_PCIE_INTX_EN] = 0x330, 3518c2ecf20Sopenharmony_ci [IPROC_PCIE_OARR0] = 0xd20, 3528c2ecf20Sopenharmony_ci [IPROC_PCIE_OMAP0] = 0xd40, 3538c2ecf20Sopenharmony_ci [IPROC_PCIE_OARR1] = 0xd28, 3548c2ecf20Sopenharmony_ci [IPROC_PCIE_OMAP1] = 0xd48, 3558c2ecf20Sopenharmony_ci [IPROC_PCIE_OARR2] = 0xd60, 3568c2ecf20Sopenharmony_ci [IPROC_PCIE_OMAP2] = 0xd68, 3578c2ecf20Sopenharmony_ci [IPROC_PCIE_OARR3] = 0xdf0, 3588c2ecf20Sopenharmony_ci [IPROC_PCIE_OMAP3] = 0xdf8, 3598c2ecf20Sopenharmony_ci [IPROC_PCIE_IARR0] = 0xd00, 3608c2ecf20Sopenharmony_ci [IPROC_PCIE_IMAP0] = 0xc00, 3618c2ecf20Sopenharmony_ci [IPROC_PCIE_IARR1] = 0xd08, 3628c2ecf20Sopenharmony_ci [IPROC_PCIE_IMAP1] = 0xd70, 3638c2ecf20Sopenharmony_ci [IPROC_PCIE_IARR2] = 0xd10, 3648c2ecf20Sopenharmony_ci [IPROC_PCIE_IMAP2] = 0xcc0, 3658c2ecf20Sopenharmony_ci [IPROC_PCIE_IARR3] = 0xe00, 3668c2ecf20Sopenharmony_ci [IPROC_PCIE_IMAP3] = 0xe08, 3678c2ecf20Sopenharmony_ci [IPROC_PCIE_IARR4] = 0xe68, 3688c2ecf20Sopenharmony_ci [IPROC_PCIE_IMAP4] = 0xe70, 3698c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_RD_STATUS] = 0xee0, 3708c2ecf20Sopenharmony_ci [IPROC_PCIE_LINK_STATUS] = 0xf0c, 3718c2ecf20Sopenharmony_ci [IPROC_PCIE_APB_ERR_EN] = 0xf40, 3728c2ecf20Sopenharmony_ci}; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci/* iProc PCIe PAXC v1 registers */ 3758c2ecf20Sopenharmony_cistatic const u16 iproc_pcie_reg_paxc[IPROC_PCIE_MAX_NUM_REG] = { 3768c2ecf20Sopenharmony_ci [IPROC_PCIE_CLK_CTRL] = 0x000, 3778c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0, 3788c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_IND_DATA] = 0x1f4, 3798c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_ADDR] = 0x1f8, 3808c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_DATA] = 0x1fc, 3818c2ecf20Sopenharmony_ci}; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/* iProc PCIe PAXC v2 registers */ 3848c2ecf20Sopenharmony_cistatic const u16 iproc_pcie_reg_paxc_v2[IPROC_PCIE_MAX_NUM_REG] = { 3858c2ecf20Sopenharmony_ci [IPROC_PCIE_MSI_GIC_MODE] = 0x050, 3868c2ecf20Sopenharmony_ci [IPROC_PCIE_MSI_BASE_ADDR] = 0x074, 3878c2ecf20Sopenharmony_ci [IPROC_PCIE_MSI_WINDOW_SIZE] = 0x078, 3888c2ecf20Sopenharmony_ci [IPROC_PCIE_MSI_ADDR_LO] = 0x07c, 3898c2ecf20Sopenharmony_ci [IPROC_PCIE_MSI_ADDR_HI] = 0x080, 3908c2ecf20Sopenharmony_ci [IPROC_PCIE_MSI_EN_CFG] = 0x09c, 3918c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0, 3928c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_IND_DATA] = 0x1f4, 3938c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_ADDR] = 0x1f8, 3948c2ecf20Sopenharmony_ci [IPROC_PCIE_CFG_DATA] = 0x1fc, 3958c2ecf20Sopenharmony_ci}; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* 3988c2ecf20Sopenharmony_ci * List of device IDs of controllers that have corrupted capability list that 3998c2ecf20Sopenharmony_ci * require SW fixup 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_cistatic const u16 iproc_pcie_corrupt_cap_did[] = { 4028c2ecf20Sopenharmony_ci 0x16cd, 4038c2ecf20Sopenharmony_ci 0x16f0, 4048c2ecf20Sopenharmony_ci 0xd802, 4058c2ecf20Sopenharmony_ci 0xd804 4068c2ecf20Sopenharmony_ci}; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic inline struct iproc_pcie *iproc_data(struct pci_bus *bus) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct iproc_pcie *pcie = bus->sysdata; 4118c2ecf20Sopenharmony_ci return pcie; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic inline bool iproc_pcie_reg_is_invalid(u16 reg_offset) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci return !!(reg_offset == IPROC_PCIE_REG_INVALID); 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic inline u16 iproc_pcie_reg_offset(struct iproc_pcie *pcie, 4208c2ecf20Sopenharmony_ci enum iproc_pcie_reg reg) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci return pcie->reg_offsets[reg]; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic inline u32 iproc_pcie_read_reg(struct iproc_pcie *pcie, 4268c2ecf20Sopenharmony_ci enum iproc_pcie_reg reg) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci u16 offset = iproc_pcie_reg_offset(pcie, reg); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (iproc_pcie_reg_is_invalid(offset)) 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return readl(pcie->base + offset); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic inline void iproc_pcie_write_reg(struct iproc_pcie *pcie, 4378c2ecf20Sopenharmony_ci enum iproc_pcie_reg reg, u32 val) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci u16 offset = iproc_pcie_reg_offset(pcie, reg); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci if (iproc_pcie_reg_is_invalid(offset)) 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci writel(val, pcie->base + offset); 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/** 4488c2ecf20Sopenharmony_ci * APB error forwarding can be disabled during access of configuration 4498c2ecf20Sopenharmony_ci * registers of the endpoint device, to prevent unsupported requests 4508c2ecf20Sopenharmony_ci * (typically seen during enumeration with multi-function devices) from 4518c2ecf20Sopenharmony_ci * triggering a system exception. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_cistatic inline void iproc_pcie_apb_err_disable(struct pci_bus *bus, 4548c2ecf20Sopenharmony_ci bool disable) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct iproc_pcie *pcie = iproc_data(bus); 4578c2ecf20Sopenharmony_ci u32 val; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (bus->number && pcie->has_apb_err_disable) { 4608c2ecf20Sopenharmony_ci val = iproc_pcie_read_reg(pcie, IPROC_PCIE_APB_ERR_EN); 4618c2ecf20Sopenharmony_ci if (disable) 4628c2ecf20Sopenharmony_ci val &= ~APB_ERR_EN; 4638c2ecf20Sopenharmony_ci else 4648c2ecf20Sopenharmony_ci val |= APB_ERR_EN; 4658c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_APB_ERR_EN, val); 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic void __iomem *iproc_pcie_map_ep_cfg_reg(struct iproc_pcie *pcie, 4708c2ecf20Sopenharmony_ci unsigned int busno, 4718c2ecf20Sopenharmony_ci unsigned int slot, 4728c2ecf20Sopenharmony_ci unsigned int fn, 4738c2ecf20Sopenharmony_ci int where) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci u16 offset; 4768c2ecf20Sopenharmony_ci u32 val; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci /* EP device access */ 4798c2ecf20Sopenharmony_ci val = (busno << CFG_ADDR_BUS_NUM_SHIFT) | 4808c2ecf20Sopenharmony_ci (slot << CFG_ADDR_DEV_NUM_SHIFT) | 4818c2ecf20Sopenharmony_ci (fn << CFG_ADDR_FUNC_NUM_SHIFT) | 4828c2ecf20Sopenharmony_ci (where & CFG_ADDR_REG_NUM_MASK) | 4838c2ecf20Sopenharmony_ci (1 & CFG_ADDR_CFG_TYPE_MASK); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val); 4868c2ecf20Sopenharmony_ci offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (iproc_pcie_reg_is_invalid(offset)) 4898c2ecf20Sopenharmony_ci return NULL; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci return (pcie->base + offset); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic unsigned int iproc_pcie_cfg_retry(struct iproc_pcie *pcie, 4958c2ecf20Sopenharmony_ci void __iomem *cfg_data_p) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci int timeout = CFG_RETRY_STATUS_TIMEOUT_US; 4988c2ecf20Sopenharmony_ci unsigned int data; 4998c2ecf20Sopenharmony_ci u32 status; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci /* 5028c2ecf20Sopenharmony_ci * As per PCIe spec r3.1, sec 2.3.2, CRS Software Visibility only 5038c2ecf20Sopenharmony_ci * affects config reads of the Vendor ID. For config writes or any 5048c2ecf20Sopenharmony_ci * other config reads, the Root may automatically reissue the 5058c2ecf20Sopenharmony_ci * configuration request again as a new request. 5068c2ecf20Sopenharmony_ci * 5078c2ecf20Sopenharmony_ci * For config reads, this hardware returns CFG_RETRY_STATUS data 5088c2ecf20Sopenharmony_ci * when it receives a CRS completion, regardless of the address of 5098c2ecf20Sopenharmony_ci * the read or the CRS Software Visibility Enable bit. As a 5108c2ecf20Sopenharmony_ci * partial workaround for this, we retry in software any read that 5118c2ecf20Sopenharmony_ci * returns CFG_RETRY_STATUS. 5128c2ecf20Sopenharmony_ci * 5138c2ecf20Sopenharmony_ci * Note that a non-Vendor ID config register may have a value of 5148c2ecf20Sopenharmony_ci * CFG_RETRY_STATUS. If we read that, we can't distinguish it from 5158c2ecf20Sopenharmony_ci * a CRS completion, so we will incorrectly retry the read and 5168c2ecf20Sopenharmony_ci * eventually return the wrong data (0xffffffff). 5178c2ecf20Sopenharmony_ci */ 5188c2ecf20Sopenharmony_ci data = readl(cfg_data_p); 5198c2ecf20Sopenharmony_ci while (data == CFG_RETRY_STATUS && timeout--) { 5208c2ecf20Sopenharmony_ci /* 5218c2ecf20Sopenharmony_ci * CRS state is set in CFG_RD status register 5228c2ecf20Sopenharmony_ci * This will handle the case where CFG_RETRY_STATUS is 5238c2ecf20Sopenharmony_ci * valid config data. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci status = iproc_pcie_read_reg(pcie, IPROC_PCIE_CFG_RD_STATUS); 5268c2ecf20Sopenharmony_ci if (status != CFG_RD_CRS) 5278c2ecf20Sopenharmony_ci return data; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci udelay(1); 5308c2ecf20Sopenharmony_ci data = readl(cfg_data_p); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (data == CFG_RETRY_STATUS) 5348c2ecf20Sopenharmony_ci data = 0xffffffff; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return data; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_cistatic void iproc_pcie_fix_cap(struct iproc_pcie *pcie, int where, u32 *val) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci u32 i, dev_id; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci switch (where & ~0x3) { 5448c2ecf20Sopenharmony_ci case PCI_VENDOR_ID: 5458c2ecf20Sopenharmony_ci dev_id = *val >> 16; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* 5488c2ecf20Sopenharmony_ci * Activate fixup for those controllers that have corrupted 5498c2ecf20Sopenharmony_ci * capability list registers 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(iproc_pcie_corrupt_cap_did); i++) 5528c2ecf20Sopenharmony_ci if (dev_id == iproc_pcie_corrupt_cap_did[i]) 5538c2ecf20Sopenharmony_ci pcie->fix_paxc_cap = true; 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci case IPROC_PCI_PM_CAP: 5578c2ecf20Sopenharmony_ci if (pcie->fix_paxc_cap) { 5588c2ecf20Sopenharmony_ci /* advertise PM, force next capability to PCIe */ 5598c2ecf20Sopenharmony_ci *val &= ~IPROC_PCI_PM_CAP_MASK; 5608c2ecf20Sopenharmony_ci *val |= IPROC_PCI_EXP_CAP << 8 | PCI_CAP_ID_PM; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci break; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci case IPROC_PCI_EXP_CAP: 5658c2ecf20Sopenharmony_ci if (pcie->fix_paxc_cap) { 5668c2ecf20Sopenharmony_ci /* advertise root port, version 2, terminate here */ 5678c2ecf20Sopenharmony_ci *val = (PCI_EXP_TYPE_ROOT_PORT << 4 | 2) << 16 | 5688c2ecf20Sopenharmony_ci PCI_CAP_ID_EXP; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci case IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL: 5738c2ecf20Sopenharmony_ci /* Don't advertise CRS SV support */ 5748c2ecf20Sopenharmony_ci *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16); 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci default: 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn, 5838c2ecf20Sopenharmony_ci int where, int size, u32 *val) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci struct iproc_pcie *pcie = iproc_data(bus); 5868c2ecf20Sopenharmony_ci unsigned int slot = PCI_SLOT(devfn); 5878c2ecf20Sopenharmony_ci unsigned int fn = PCI_FUNC(devfn); 5888c2ecf20Sopenharmony_ci unsigned int busno = bus->number; 5898c2ecf20Sopenharmony_ci void __iomem *cfg_data_p; 5908c2ecf20Sopenharmony_ci unsigned int data; 5918c2ecf20Sopenharmony_ci int ret; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* root complex access */ 5948c2ecf20Sopenharmony_ci if (busno == 0) { 5958c2ecf20Sopenharmony_ci ret = pci_generic_config_read32(bus, devfn, where, size, val); 5968c2ecf20Sopenharmony_ci if (ret == PCIBIOS_SUCCESSFUL) 5978c2ecf20Sopenharmony_ci iproc_pcie_fix_cap(pcie, where, val); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci return ret; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci cfg_data_p = iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (!cfg_data_p) 6058c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci data = iproc_pcie_cfg_retry(pcie, cfg_data_p); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci *val = data; 6108c2ecf20Sopenharmony_ci if (size <= 2) 6118c2ecf20Sopenharmony_ci *val = (data >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* 6148c2ecf20Sopenharmony_ci * For PAXC and PAXCv2, the total number of PFs that one can enumerate 6158c2ecf20Sopenharmony_ci * depends on the firmware configuration. Unfortunately, due to an ASIC 6168c2ecf20Sopenharmony_ci * bug, unconfigured PFs cannot be properly hidden from the root 6178c2ecf20Sopenharmony_ci * complex. As a result, write access to these PFs will cause bus lock 6188c2ecf20Sopenharmony_ci * up on the embedded processor 6198c2ecf20Sopenharmony_ci * 6208c2ecf20Sopenharmony_ci * Since all unconfigured PFs are left with an incorrect, staled device 6218c2ecf20Sopenharmony_ci * ID of 0x168e (PCI_DEVICE_ID_NX2_57810), we try to catch those access 6228c2ecf20Sopenharmony_ci * early here and reject them all 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_ci#define DEVICE_ID_MASK 0xffff0000 6258c2ecf20Sopenharmony_ci#define DEVICE_ID_SHIFT 16 6268c2ecf20Sopenharmony_ci if (pcie->rej_unconfig_pf && 6278c2ecf20Sopenharmony_ci (where & CFG_ADDR_REG_NUM_MASK) == PCI_VENDOR_ID) 6288c2ecf20Sopenharmony_ci if ((*val & DEVICE_ID_MASK) == 6298c2ecf20Sopenharmony_ci (PCI_DEVICE_ID_NX2_57810 << DEVICE_ID_SHIFT)) 6308c2ecf20Sopenharmony_ci return PCIBIOS_FUNC_NOT_SUPPORTED; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci/** 6368c2ecf20Sopenharmony_ci * Note access to the configuration registers are protected at the higher layer 6378c2ecf20Sopenharmony_ci * by 'pci_lock' in drivers/pci/access.c 6388c2ecf20Sopenharmony_ci */ 6398c2ecf20Sopenharmony_cistatic void __iomem *iproc_pcie_map_cfg_bus(struct iproc_pcie *pcie, 6408c2ecf20Sopenharmony_ci int busno, unsigned int devfn, 6418c2ecf20Sopenharmony_ci int where) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci unsigned slot = PCI_SLOT(devfn); 6448c2ecf20Sopenharmony_ci unsigned fn = PCI_FUNC(devfn); 6458c2ecf20Sopenharmony_ci u16 offset; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* root complex access */ 6488c2ecf20Sopenharmony_ci if (busno == 0) { 6498c2ecf20Sopenharmony_ci if (slot > 0 || fn > 0) 6508c2ecf20Sopenharmony_ci return NULL; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_IND_ADDR, 6538c2ecf20Sopenharmony_ci where & CFG_IND_ADDR_MASK); 6548c2ecf20Sopenharmony_ci offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_IND_DATA); 6558c2ecf20Sopenharmony_ci if (iproc_pcie_reg_is_invalid(offset)) 6568c2ecf20Sopenharmony_ci return NULL; 6578c2ecf20Sopenharmony_ci else 6588c2ecf20Sopenharmony_ci return (pcie->base + offset); 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci return iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where); 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic void __iomem *iproc_pcie_bus_map_cfg_bus(struct pci_bus *bus, 6658c2ecf20Sopenharmony_ci unsigned int devfn, 6668c2ecf20Sopenharmony_ci int where) 6678c2ecf20Sopenharmony_ci{ 6688c2ecf20Sopenharmony_ci return iproc_pcie_map_cfg_bus(iproc_data(bus), bus->number, devfn, 6698c2ecf20Sopenharmony_ci where); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int iproc_pci_raw_config_read32(struct iproc_pcie *pcie, 6738c2ecf20Sopenharmony_ci unsigned int devfn, int where, 6748c2ecf20Sopenharmony_ci int size, u32 *val) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci void __iomem *addr; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci addr = iproc_pcie_map_cfg_bus(pcie, 0, devfn, where & ~0x3); 6798c2ecf20Sopenharmony_ci if (!addr) { 6808c2ecf20Sopenharmony_ci *val = ~0; 6818c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci *val = readl(addr); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (size <= 2) 6878c2ecf20Sopenharmony_ci *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_cistatic int iproc_pci_raw_config_write32(struct iproc_pcie *pcie, 6938c2ecf20Sopenharmony_ci unsigned int devfn, int where, 6948c2ecf20Sopenharmony_ci int size, u32 val) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci void __iomem *addr; 6978c2ecf20Sopenharmony_ci u32 mask, tmp; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci addr = iproc_pcie_map_cfg_bus(pcie, 0, devfn, where & ~0x3); 7008c2ecf20Sopenharmony_ci if (!addr) 7018c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (size == 4) { 7048c2ecf20Sopenharmony_ci writel(val, addr); 7058c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8)); 7098c2ecf20Sopenharmony_ci tmp = readl(addr) & mask; 7108c2ecf20Sopenharmony_ci tmp |= val << ((where & 0x3) * 8); 7118c2ecf20Sopenharmony_ci writel(tmp, addr); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn, 7178c2ecf20Sopenharmony_ci int where, int size, u32 *val) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci int ret; 7208c2ecf20Sopenharmony_ci struct iproc_pcie *pcie = iproc_data(bus); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci iproc_pcie_apb_err_disable(bus, true); 7238c2ecf20Sopenharmony_ci if (pcie->iproc_cfg_read) 7248c2ecf20Sopenharmony_ci ret = iproc_pcie_config_read(bus, devfn, where, size, val); 7258c2ecf20Sopenharmony_ci else 7268c2ecf20Sopenharmony_ci ret = pci_generic_config_read32(bus, devfn, where, size, val); 7278c2ecf20Sopenharmony_ci iproc_pcie_apb_err_disable(bus, false); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci return ret; 7308c2ecf20Sopenharmony_ci} 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic int iproc_pcie_config_write32(struct pci_bus *bus, unsigned int devfn, 7338c2ecf20Sopenharmony_ci int where, int size, u32 val) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci int ret; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci iproc_pcie_apb_err_disable(bus, true); 7388c2ecf20Sopenharmony_ci ret = pci_generic_config_write32(bus, devfn, where, size, val); 7398c2ecf20Sopenharmony_ci iproc_pcie_apb_err_disable(bus, false); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci return ret; 7428c2ecf20Sopenharmony_ci} 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_cistatic struct pci_ops iproc_pcie_ops = { 7458c2ecf20Sopenharmony_ci .map_bus = iproc_pcie_bus_map_cfg_bus, 7468c2ecf20Sopenharmony_ci .read = iproc_pcie_config_read32, 7478c2ecf20Sopenharmony_ci .write = iproc_pcie_config_write32, 7488c2ecf20Sopenharmony_ci}; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic void iproc_pcie_perst_ctrl(struct iproc_pcie *pcie, bool assert) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci u32 val; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* 7558c2ecf20Sopenharmony_ci * PAXC and the internal emulated endpoint device downstream should not 7568c2ecf20Sopenharmony_ci * be reset. If firmware has been loaded on the endpoint device at an 7578c2ecf20Sopenharmony_ci * earlier boot stage, reset here causes issues. 7588c2ecf20Sopenharmony_ci */ 7598c2ecf20Sopenharmony_ci if (pcie->ep_is_internal) 7608c2ecf20Sopenharmony_ci return; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if (assert) { 7638c2ecf20Sopenharmony_ci val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); 7648c2ecf20Sopenharmony_ci val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST & 7658c2ecf20Sopenharmony_ci ~RC_PCIE_RST_OUTPUT; 7668c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); 7678c2ecf20Sopenharmony_ci udelay(250); 7688c2ecf20Sopenharmony_ci } else { 7698c2ecf20Sopenharmony_ci val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); 7708c2ecf20Sopenharmony_ci val |= RC_PCIE_RST_OUTPUT; 7718c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); 7728c2ecf20Sopenharmony_ci msleep(100); 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ciint iproc_pcie_shutdown(struct iproc_pcie *pcie) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci iproc_pcie_perst_ctrl(pcie, true); 7798c2ecf20Sopenharmony_ci msleep(500); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci return 0; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iproc_pcie_shutdown); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic int iproc_pcie_check_link(struct iproc_pcie *pcie) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 7888c2ecf20Sopenharmony_ci u32 hdr_type, link_ctrl, link_status, class, val; 7898c2ecf20Sopenharmony_ci bool link_is_active = false; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* 7928c2ecf20Sopenharmony_ci * PAXC connects to emulated endpoint devices directly and does not 7938c2ecf20Sopenharmony_ci * have a Serdes. Therefore skip the link detection logic here. 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_ci if (pcie->ep_is_internal) 7968c2ecf20Sopenharmony_ci return 0; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS); 7998c2ecf20Sopenharmony_ci if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) { 8008c2ecf20Sopenharmony_ci dev_err(dev, "PHY or data link is INACTIVE!\n"); 8018c2ecf20Sopenharmony_ci return -ENODEV; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci /* make sure we are not in EP mode */ 8058c2ecf20Sopenharmony_ci iproc_pci_raw_config_read32(pcie, 0, PCI_HEADER_TYPE, 1, &hdr_type); 8068c2ecf20Sopenharmony_ci if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) { 8078c2ecf20Sopenharmony_ci dev_err(dev, "in EP mode, hdr=%#02x\n", hdr_type); 8088c2ecf20Sopenharmony_ci return -EFAULT; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */ 8128c2ecf20Sopenharmony_ci#define PCI_BRIDGE_CTRL_REG_OFFSET 0x43c 8138c2ecf20Sopenharmony_ci#define PCI_CLASS_BRIDGE_MASK 0xffff00 8148c2ecf20Sopenharmony_ci#define PCI_CLASS_BRIDGE_SHIFT 8 8158c2ecf20Sopenharmony_ci iproc_pci_raw_config_read32(pcie, 0, PCI_BRIDGE_CTRL_REG_OFFSET, 8168c2ecf20Sopenharmony_ci 4, &class); 8178c2ecf20Sopenharmony_ci class &= ~PCI_CLASS_BRIDGE_MASK; 8188c2ecf20Sopenharmony_ci class |= (PCI_CLASS_BRIDGE_PCI << PCI_CLASS_BRIDGE_SHIFT); 8198c2ecf20Sopenharmony_ci iproc_pci_raw_config_write32(pcie, 0, PCI_BRIDGE_CTRL_REG_OFFSET, 8208c2ecf20Sopenharmony_ci 4, class); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* check link status to see if link is active */ 8238c2ecf20Sopenharmony_ci iproc_pci_raw_config_read32(pcie, 0, IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA, 8248c2ecf20Sopenharmony_ci 2, &link_status); 8258c2ecf20Sopenharmony_ci if (link_status & PCI_EXP_LNKSTA_NLW) 8268c2ecf20Sopenharmony_ci link_is_active = true; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci if (!link_is_active) { 8298c2ecf20Sopenharmony_ci /* try GEN 1 link speed */ 8308c2ecf20Sopenharmony_ci#define PCI_TARGET_LINK_SPEED_MASK 0xf 8318c2ecf20Sopenharmony_ci#define PCI_TARGET_LINK_SPEED_GEN2 0x2 8328c2ecf20Sopenharmony_ci#define PCI_TARGET_LINK_SPEED_GEN1 0x1 8338c2ecf20Sopenharmony_ci iproc_pci_raw_config_read32(pcie, 0, 8348c2ecf20Sopenharmony_ci IPROC_PCI_EXP_CAP + PCI_EXP_LNKCTL2, 8358c2ecf20Sopenharmony_ci 4, &link_ctrl); 8368c2ecf20Sopenharmony_ci if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) == 8378c2ecf20Sopenharmony_ci PCI_TARGET_LINK_SPEED_GEN2) { 8388c2ecf20Sopenharmony_ci link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK; 8398c2ecf20Sopenharmony_ci link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1; 8408c2ecf20Sopenharmony_ci iproc_pci_raw_config_write32(pcie, 0, 8418c2ecf20Sopenharmony_ci IPROC_PCI_EXP_CAP + PCI_EXP_LNKCTL2, 8428c2ecf20Sopenharmony_ci 4, link_ctrl); 8438c2ecf20Sopenharmony_ci msleep(100); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci iproc_pci_raw_config_read32(pcie, 0, 8468c2ecf20Sopenharmony_ci IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA, 8478c2ecf20Sopenharmony_ci 2, &link_status); 8488c2ecf20Sopenharmony_ci if (link_status & PCI_EXP_LNKSTA_NLW) 8498c2ecf20Sopenharmony_ci link_is_active = true; 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci dev_info(dev, "link: %s\n", link_is_active ? "UP" : "DOWN"); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return link_is_active ? 0 : -ENODEV; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic void iproc_pcie_enable(struct iproc_pcie *pcie) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, SYS_RC_INTX_MASK); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic inline bool iproc_pcie_ob_is_valid(struct iproc_pcie *pcie, 8648c2ecf20Sopenharmony_ci int window_idx) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci u32 val; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_OARR0, window_idx)); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return !!(val & OARR_VALID); 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx, 8748c2ecf20Sopenharmony_ci int size_idx, u64 axi_addr, u64 pci_addr) 8758c2ecf20Sopenharmony_ci{ 8768c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 8778c2ecf20Sopenharmony_ci u16 oarr_offset, omap_offset; 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci /* 8808c2ecf20Sopenharmony_ci * Derive the OARR/OMAP offset from the first pair (OARR0/OMAP0) based 8818c2ecf20Sopenharmony_ci * on window index. 8828c2ecf20Sopenharmony_ci */ 8838c2ecf20Sopenharmony_ci oarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OARR0, 8848c2ecf20Sopenharmony_ci window_idx)); 8858c2ecf20Sopenharmony_ci omap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OMAP0, 8868c2ecf20Sopenharmony_ci window_idx)); 8878c2ecf20Sopenharmony_ci if (iproc_pcie_reg_is_invalid(oarr_offset) || 8888c2ecf20Sopenharmony_ci iproc_pcie_reg_is_invalid(omap_offset)) 8898c2ecf20Sopenharmony_ci return -EINVAL; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci /* 8928c2ecf20Sopenharmony_ci * Program the OARR registers. The upper 32-bit OARR register is 8938c2ecf20Sopenharmony_ci * always right after the lower 32-bit OARR register. 8948c2ecf20Sopenharmony_ci */ 8958c2ecf20Sopenharmony_ci writel(lower_32_bits(axi_addr) | (size_idx << OARR_SIZE_CFG_SHIFT) | 8968c2ecf20Sopenharmony_ci OARR_VALID, pcie->base + oarr_offset); 8978c2ecf20Sopenharmony_ci writel(upper_32_bits(axi_addr), pcie->base + oarr_offset + 4); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci /* now program the OMAP registers */ 9008c2ecf20Sopenharmony_ci writel(lower_32_bits(pci_addr), pcie->base + omap_offset); 9018c2ecf20Sopenharmony_ci writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci dev_dbg(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n", 9048c2ecf20Sopenharmony_ci window_idx, oarr_offset, &axi_addr, &pci_addr); 9058c2ecf20Sopenharmony_ci dev_dbg(dev, "oarr lo 0x%x oarr hi 0x%x\n", 9068c2ecf20Sopenharmony_ci readl(pcie->base + oarr_offset), 9078c2ecf20Sopenharmony_ci readl(pcie->base + oarr_offset + 4)); 9088c2ecf20Sopenharmony_ci dev_dbg(dev, "omap lo 0x%x omap hi 0x%x\n", 9098c2ecf20Sopenharmony_ci readl(pcie->base + omap_offset), 9108c2ecf20Sopenharmony_ci readl(pcie->base + omap_offset + 4)); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci return 0; 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci/** 9168c2ecf20Sopenharmony_ci * Some iProc SoCs require the SW to configure the outbound address mapping 9178c2ecf20Sopenharmony_ci * 9188c2ecf20Sopenharmony_ci * Outbound address translation: 9198c2ecf20Sopenharmony_ci * 9208c2ecf20Sopenharmony_ci * iproc_pcie_address = axi_address - axi_offset 9218c2ecf20Sopenharmony_ci * OARR = iproc_pcie_address 9228c2ecf20Sopenharmony_ci * OMAP = pci_addr 9238c2ecf20Sopenharmony_ci * 9248c2ecf20Sopenharmony_ci * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_cistatic int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr, 9278c2ecf20Sopenharmony_ci u64 pci_addr, resource_size_t size) 9288c2ecf20Sopenharmony_ci{ 9298c2ecf20Sopenharmony_ci struct iproc_pcie_ob *ob = &pcie->ob; 9308c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 9318c2ecf20Sopenharmony_ci int ret = -EINVAL, window_idx, size_idx; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci if (axi_addr < ob->axi_offset) { 9348c2ecf20Sopenharmony_ci dev_err(dev, "axi address %pap less than offset %pap\n", 9358c2ecf20Sopenharmony_ci &axi_addr, &ob->axi_offset); 9368c2ecf20Sopenharmony_ci return -EINVAL; 9378c2ecf20Sopenharmony_ci } 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* 9408c2ecf20Sopenharmony_ci * Translate the AXI address to the internal address used by the iProc 9418c2ecf20Sopenharmony_ci * PCIe core before programming the OARR 9428c2ecf20Sopenharmony_ci */ 9438c2ecf20Sopenharmony_ci axi_addr -= ob->axi_offset; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* iterate through all OARR/OMAP mapping windows */ 9468c2ecf20Sopenharmony_ci for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) { 9478c2ecf20Sopenharmony_ci const struct iproc_pcie_ob_map *ob_map = 9488c2ecf20Sopenharmony_ci &pcie->ob_map[window_idx]; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci /* 9518c2ecf20Sopenharmony_ci * If current outbound window is already in use, move on to the 9528c2ecf20Sopenharmony_ci * next one. 9538c2ecf20Sopenharmony_ci */ 9548c2ecf20Sopenharmony_ci if (iproc_pcie_ob_is_valid(pcie, window_idx)) 9558c2ecf20Sopenharmony_ci continue; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* 9588c2ecf20Sopenharmony_ci * Iterate through all supported window sizes within the 9598c2ecf20Sopenharmony_ci * OARR/OMAP pair to find a match. Go through the window sizes 9608c2ecf20Sopenharmony_ci * in a descending order. 9618c2ecf20Sopenharmony_ci */ 9628c2ecf20Sopenharmony_ci for (size_idx = ob_map->nr_sizes - 1; size_idx >= 0; 9638c2ecf20Sopenharmony_ci size_idx--) { 9648c2ecf20Sopenharmony_ci resource_size_t window_size = 9658c2ecf20Sopenharmony_ci ob_map->window_sizes[size_idx] * SZ_1M; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci /* 9688c2ecf20Sopenharmony_ci * Keep iterating until we reach the last window and 9698c2ecf20Sopenharmony_ci * with the minimal window size at index zero. In this 9708c2ecf20Sopenharmony_ci * case, we take a compromise by mapping it using the 9718c2ecf20Sopenharmony_ci * minimum window size that can be supported 9728c2ecf20Sopenharmony_ci */ 9738c2ecf20Sopenharmony_ci if (size < window_size) { 9748c2ecf20Sopenharmony_ci if (size_idx > 0 || window_idx > 0) 9758c2ecf20Sopenharmony_ci continue; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* 9788c2ecf20Sopenharmony_ci * For the corner case of reaching the minimal 9798c2ecf20Sopenharmony_ci * window size that can be supported on the 9808c2ecf20Sopenharmony_ci * last window 9818c2ecf20Sopenharmony_ci */ 9828c2ecf20Sopenharmony_ci axi_addr = ALIGN_DOWN(axi_addr, window_size); 9838c2ecf20Sopenharmony_ci pci_addr = ALIGN_DOWN(pci_addr, window_size); 9848c2ecf20Sopenharmony_ci size = window_size; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci if (!IS_ALIGNED(axi_addr, window_size) || 9888c2ecf20Sopenharmony_ci !IS_ALIGNED(pci_addr, window_size)) { 9898c2ecf20Sopenharmony_ci dev_err(dev, 9908c2ecf20Sopenharmony_ci "axi %pap or pci %pap not aligned\n", 9918c2ecf20Sopenharmony_ci &axi_addr, &pci_addr); 9928c2ecf20Sopenharmony_ci return -EINVAL; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci /* 9968c2ecf20Sopenharmony_ci * Match found! Program both OARR and OMAP and mark 9978c2ecf20Sopenharmony_ci * them as a valid entry. 9988c2ecf20Sopenharmony_ci */ 9998c2ecf20Sopenharmony_ci ret = iproc_pcie_ob_write(pcie, window_idx, size_idx, 10008c2ecf20Sopenharmony_ci axi_addr, pci_addr); 10018c2ecf20Sopenharmony_ci if (ret) 10028c2ecf20Sopenharmony_ci goto err_ob; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci size -= window_size; 10058c2ecf20Sopenharmony_ci if (size == 0) 10068c2ecf20Sopenharmony_ci return 0; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* 10098c2ecf20Sopenharmony_ci * If we are here, we are done with the current window, 10108c2ecf20Sopenharmony_ci * but not yet finished all mappings. Need to move on 10118c2ecf20Sopenharmony_ci * to the next window. 10128c2ecf20Sopenharmony_ci */ 10138c2ecf20Sopenharmony_ci axi_addr += window_size; 10148c2ecf20Sopenharmony_ci pci_addr += window_size; 10158c2ecf20Sopenharmony_ci break; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cierr_ob: 10208c2ecf20Sopenharmony_ci dev_err(dev, "unable to configure outbound mapping\n"); 10218c2ecf20Sopenharmony_ci dev_err(dev, 10228c2ecf20Sopenharmony_ci "axi %pap, axi offset %pap, pci %pap, res size %pap\n", 10238c2ecf20Sopenharmony_ci &axi_addr, &ob->axi_offset, &pci_addr, &size); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci return ret; 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic int iproc_pcie_map_ranges(struct iproc_pcie *pcie, 10298c2ecf20Sopenharmony_ci struct list_head *resources) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 10328c2ecf20Sopenharmony_ci struct resource_entry *window; 10338c2ecf20Sopenharmony_ci int ret; 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci resource_list_for_each_entry(window, resources) { 10368c2ecf20Sopenharmony_ci struct resource *res = window->res; 10378c2ecf20Sopenharmony_ci u64 res_type = resource_type(res); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci switch (res_type) { 10408c2ecf20Sopenharmony_ci case IORESOURCE_IO: 10418c2ecf20Sopenharmony_ci case IORESOURCE_BUS: 10428c2ecf20Sopenharmony_ci break; 10438c2ecf20Sopenharmony_ci case IORESOURCE_MEM: 10448c2ecf20Sopenharmony_ci ret = iproc_pcie_setup_ob(pcie, res->start, 10458c2ecf20Sopenharmony_ci res->start - window->offset, 10468c2ecf20Sopenharmony_ci resource_size(res)); 10478c2ecf20Sopenharmony_ci if (ret) 10488c2ecf20Sopenharmony_ci return ret; 10498c2ecf20Sopenharmony_ci break; 10508c2ecf20Sopenharmony_ci default: 10518c2ecf20Sopenharmony_ci dev_err(dev, "invalid resource %pR\n", res); 10528c2ecf20Sopenharmony_ci return -EINVAL; 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci return 0; 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_cistatic inline bool iproc_pcie_ib_is_in_use(struct iproc_pcie *pcie, 10608c2ecf20Sopenharmony_ci int region_idx) 10618c2ecf20Sopenharmony_ci{ 10628c2ecf20Sopenharmony_ci const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx]; 10638c2ecf20Sopenharmony_ci u32 val; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_IARR0, region_idx)); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci return !!(val & (BIT(ib_map->nr_sizes) - 1)); 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_cistatic inline bool iproc_pcie_ib_check_type(const struct iproc_pcie_ib_map *ib_map, 10718c2ecf20Sopenharmony_ci enum iproc_pcie_ib_map_type type) 10728c2ecf20Sopenharmony_ci{ 10738c2ecf20Sopenharmony_ci return !!(ib_map->type == type); 10748c2ecf20Sopenharmony_ci} 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_cistatic int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx, 10778c2ecf20Sopenharmony_ci int size_idx, int nr_windows, u64 axi_addr, 10788c2ecf20Sopenharmony_ci u64 pci_addr, resource_size_t size) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 10818c2ecf20Sopenharmony_ci const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx]; 10828c2ecf20Sopenharmony_ci u16 iarr_offset, imap_offset; 10838c2ecf20Sopenharmony_ci u32 val; 10848c2ecf20Sopenharmony_ci int window_idx; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci iarr_offset = iproc_pcie_reg_offset(pcie, 10878c2ecf20Sopenharmony_ci MAP_REG(IPROC_PCIE_IARR0, region_idx)); 10888c2ecf20Sopenharmony_ci imap_offset = iproc_pcie_reg_offset(pcie, 10898c2ecf20Sopenharmony_ci MAP_REG(IPROC_PCIE_IMAP0, region_idx)); 10908c2ecf20Sopenharmony_ci if (iproc_pcie_reg_is_invalid(iarr_offset) || 10918c2ecf20Sopenharmony_ci iproc_pcie_reg_is_invalid(imap_offset)) 10928c2ecf20Sopenharmony_ci return -EINVAL; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci dev_dbg(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n", 10958c2ecf20Sopenharmony_ci region_idx, iarr_offset, &axi_addr, &pci_addr); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci /* 10988c2ecf20Sopenharmony_ci * Program the IARR registers. The upper 32-bit IARR register is 10998c2ecf20Sopenharmony_ci * always right after the lower 32-bit IARR register. 11008c2ecf20Sopenharmony_ci */ 11018c2ecf20Sopenharmony_ci writel(lower_32_bits(pci_addr) | BIT(size_idx), 11028c2ecf20Sopenharmony_ci pcie->base + iarr_offset); 11038c2ecf20Sopenharmony_ci writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci dev_dbg(dev, "iarr lo 0x%x iarr hi 0x%x\n", 11068c2ecf20Sopenharmony_ci readl(pcie->base + iarr_offset), 11078c2ecf20Sopenharmony_ci readl(pcie->base + iarr_offset + 4)); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci /* 11108c2ecf20Sopenharmony_ci * Now program the IMAP registers. Each IARR region may have one or 11118c2ecf20Sopenharmony_ci * more IMAP windows. 11128c2ecf20Sopenharmony_ci */ 11138c2ecf20Sopenharmony_ci size >>= ilog2(nr_windows); 11148c2ecf20Sopenharmony_ci for (window_idx = 0; window_idx < nr_windows; window_idx++) { 11158c2ecf20Sopenharmony_ci val = readl(pcie->base + imap_offset); 11168c2ecf20Sopenharmony_ci val |= lower_32_bits(axi_addr) | IMAP_VALID; 11178c2ecf20Sopenharmony_ci writel(val, pcie->base + imap_offset); 11188c2ecf20Sopenharmony_ci writel(upper_32_bits(axi_addr), 11198c2ecf20Sopenharmony_ci pcie->base + imap_offset + ib_map->imap_addr_offset); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci dev_dbg(dev, "imap window [%d] lo 0x%x hi 0x%x\n", 11228c2ecf20Sopenharmony_ci window_idx, readl(pcie->base + imap_offset), 11238c2ecf20Sopenharmony_ci readl(pcie->base + imap_offset + 11248c2ecf20Sopenharmony_ci ib_map->imap_addr_offset)); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci imap_offset += ib_map->imap_window_offset; 11278c2ecf20Sopenharmony_ci axi_addr += size; 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci return 0; 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_cistatic int iproc_pcie_setup_ib(struct iproc_pcie *pcie, 11348c2ecf20Sopenharmony_ci struct resource_entry *entry, 11358c2ecf20Sopenharmony_ci enum iproc_pcie_ib_map_type type) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 11388c2ecf20Sopenharmony_ci struct iproc_pcie_ib *ib = &pcie->ib; 11398c2ecf20Sopenharmony_ci int ret; 11408c2ecf20Sopenharmony_ci unsigned int region_idx, size_idx; 11418c2ecf20Sopenharmony_ci u64 axi_addr = entry->res->start; 11428c2ecf20Sopenharmony_ci u64 pci_addr = entry->res->start - entry->offset; 11438c2ecf20Sopenharmony_ci resource_size_t size = resource_size(entry->res); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci /* iterate through all IARR mapping regions */ 11468c2ecf20Sopenharmony_ci for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) { 11478c2ecf20Sopenharmony_ci const struct iproc_pcie_ib_map *ib_map = 11488c2ecf20Sopenharmony_ci &pcie->ib_map[region_idx]; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci /* 11518c2ecf20Sopenharmony_ci * If current inbound region is already in use or not a 11528c2ecf20Sopenharmony_ci * compatible type, move on to the next. 11538c2ecf20Sopenharmony_ci */ 11548c2ecf20Sopenharmony_ci if (iproc_pcie_ib_is_in_use(pcie, region_idx) || 11558c2ecf20Sopenharmony_ci !iproc_pcie_ib_check_type(ib_map, type)) 11568c2ecf20Sopenharmony_ci continue; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci /* iterate through all supported region sizes to find a match */ 11598c2ecf20Sopenharmony_ci for (size_idx = 0; size_idx < ib_map->nr_sizes; size_idx++) { 11608c2ecf20Sopenharmony_ci resource_size_t region_size = 11618c2ecf20Sopenharmony_ci ib_map->region_sizes[size_idx] * ib_map->size_unit; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (size != region_size) 11648c2ecf20Sopenharmony_ci continue; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci if (!IS_ALIGNED(axi_addr, region_size) || 11678c2ecf20Sopenharmony_ci !IS_ALIGNED(pci_addr, region_size)) { 11688c2ecf20Sopenharmony_ci dev_err(dev, 11698c2ecf20Sopenharmony_ci "axi %pap or pci %pap not aligned\n", 11708c2ecf20Sopenharmony_ci &axi_addr, &pci_addr); 11718c2ecf20Sopenharmony_ci return -EINVAL; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* Match found! Program IARR and all IMAP windows. */ 11758c2ecf20Sopenharmony_ci ret = iproc_pcie_ib_write(pcie, region_idx, size_idx, 11768c2ecf20Sopenharmony_ci ib_map->nr_windows, axi_addr, 11778c2ecf20Sopenharmony_ci pci_addr, size); 11788c2ecf20Sopenharmony_ci if (ret) 11798c2ecf20Sopenharmony_ci goto err_ib; 11808c2ecf20Sopenharmony_ci else 11818c2ecf20Sopenharmony_ci return 0; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci ret = -EINVAL; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_cierr_ib: 11888c2ecf20Sopenharmony_ci dev_err(dev, "unable to configure inbound mapping\n"); 11898c2ecf20Sopenharmony_ci dev_err(dev, "axi %pap, pci %pap, res size %pap\n", 11908c2ecf20Sopenharmony_ci &axi_addr, &pci_addr, &size); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci return ret; 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_cistatic int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); 11988c2ecf20Sopenharmony_ci struct resource_entry *entry; 11998c2ecf20Sopenharmony_ci int ret = 0; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci resource_list_for_each_entry(entry, &host->dma_ranges) { 12028c2ecf20Sopenharmony_ci /* Each range entry corresponds to an inbound mapping region */ 12038c2ecf20Sopenharmony_ci ret = iproc_pcie_setup_ib(pcie, entry, IPROC_PCIE_IB_MAP_MEM); 12048c2ecf20Sopenharmony_ci if (ret) 12058c2ecf20Sopenharmony_ci break; 12068c2ecf20Sopenharmony_ci } 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci return ret; 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_cistatic void iproc_pcie_invalidate_mapping(struct iproc_pcie *pcie) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct iproc_pcie_ib *ib = &pcie->ib; 12148c2ecf20Sopenharmony_ci struct iproc_pcie_ob *ob = &pcie->ob; 12158c2ecf20Sopenharmony_ci int idx; 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci if (pcie->ep_is_internal) 12188c2ecf20Sopenharmony_ci return; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (pcie->need_ob_cfg) { 12218c2ecf20Sopenharmony_ci /* iterate through all OARR mapping regions */ 12228c2ecf20Sopenharmony_ci for (idx = ob->nr_windows - 1; idx >= 0; idx--) { 12238c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, 12248c2ecf20Sopenharmony_ci MAP_REG(IPROC_PCIE_OARR0, idx), 0); 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci } 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if (pcie->need_ib_cfg) { 12298c2ecf20Sopenharmony_ci /* iterate through all IARR mapping regions */ 12308c2ecf20Sopenharmony_ci for (idx = 0; idx < ib->nr_regions; idx++) { 12318c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, 12328c2ecf20Sopenharmony_ci MAP_REG(IPROC_PCIE_IARR0, idx), 0); 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci} 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_cistatic int iproce_pcie_get_msi(struct iproc_pcie *pcie, 12388c2ecf20Sopenharmony_ci struct device_node *msi_node, 12398c2ecf20Sopenharmony_ci u64 *msi_addr) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 12428c2ecf20Sopenharmony_ci int ret; 12438c2ecf20Sopenharmony_ci struct resource res; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci /* 12468c2ecf20Sopenharmony_ci * Check if 'msi-map' points to ARM GICv3 ITS, which is the only 12478c2ecf20Sopenharmony_ci * supported external MSI controller that requires steering. 12488c2ecf20Sopenharmony_ci */ 12498c2ecf20Sopenharmony_ci if (!of_device_is_compatible(msi_node, "arm,gic-v3-its")) { 12508c2ecf20Sopenharmony_ci dev_err(dev, "unable to find compatible MSI controller\n"); 12518c2ecf20Sopenharmony_ci return -ENODEV; 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci /* derive GITS_TRANSLATER address from GICv3 */ 12558c2ecf20Sopenharmony_ci ret = of_address_to_resource(msi_node, 0, &res); 12568c2ecf20Sopenharmony_ci if (ret < 0) { 12578c2ecf20Sopenharmony_ci dev_err(dev, "unable to obtain MSI controller resources\n"); 12588c2ecf20Sopenharmony_ci return ret; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci *msi_addr = res.start + GITS_TRANSLATER; 12628c2ecf20Sopenharmony_ci return 0; 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_cistatic int iproc_pcie_paxb_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr) 12668c2ecf20Sopenharmony_ci{ 12678c2ecf20Sopenharmony_ci int ret; 12688c2ecf20Sopenharmony_ci struct resource_entry entry; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci memset(&entry, 0, sizeof(entry)); 12718c2ecf20Sopenharmony_ci entry.res = &entry.__res; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci msi_addr &= ~(SZ_32K - 1); 12748c2ecf20Sopenharmony_ci entry.res->start = msi_addr; 12758c2ecf20Sopenharmony_ci entry.res->end = msi_addr + SZ_32K - 1; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci ret = iproc_pcie_setup_ib(pcie, &entry, IPROC_PCIE_IB_MAP_IO); 12788c2ecf20Sopenharmony_ci return ret; 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr, 12828c2ecf20Sopenharmony_ci bool enable) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci u32 val; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if (!enable) { 12878c2ecf20Sopenharmony_ci /* 12888c2ecf20Sopenharmony_ci * Disable PAXC MSI steering. All write transfers will be 12898c2ecf20Sopenharmony_ci * treated as non-MSI transfers 12908c2ecf20Sopenharmony_ci */ 12918c2ecf20Sopenharmony_ci val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_EN_CFG); 12928c2ecf20Sopenharmony_ci val &= ~MSI_ENABLE_CFG; 12938c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_EN_CFG, val); 12948c2ecf20Sopenharmony_ci return; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci /* 12988c2ecf20Sopenharmony_ci * Program bits [43:13] of address of GITS_TRANSLATER register into 12998c2ecf20Sopenharmony_ci * bits [30:0] of the MSI base address register. In fact, in all iProc 13008c2ecf20Sopenharmony_ci * based SoCs, all I/O register bases are well below the 32-bit 13018c2ecf20Sopenharmony_ci * boundary, so we can safely assume bits [43:32] are always zeros. 13028c2ecf20Sopenharmony_ci */ 13038c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_BASE_ADDR, 13048c2ecf20Sopenharmony_ci (u32)(msi_addr >> 13)); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci /* use a default 8K window size */ 13078c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_WINDOW_SIZE, 0); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci /* steering MSI to GICv3 ITS */ 13108c2ecf20Sopenharmony_ci val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_GIC_MODE); 13118c2ecf20Sopenharmony_ci val |= GIC_V3_CFG; 13128c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_GIC_MODE, val); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* 13158c2ecf20Sopenharmony_ci * Program bits [43:2] of address of GITS_TRANSLATER register into the 13168c2ecf20Sopenharmony_ci * iProc MSI address registers. 13178c2ecf20Sopenharmony_ci */ 13188c2ecf20Sopenharmony_ci msi_addr >>= 2; 13198c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_HI, 13208c2ecf20Sopenharmony_ci upper_32_bits(msi_addr)); 13218c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_LO, 13228c2ecf20Sopenharmony_ci lower_32_bits(msi_addr)); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci /* enable MSI */ 13258c2ecf20Sopenharmony_ci val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_EN_CFG); 13268c2ecf20Sopenharmony_ci val |= MSI_ENABLE_CFG; 13278c2ecf20Sopenharmony_ci iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_EN_CFG, val); 13288c2ecf20Sopenharmony_ci} 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cistatic int iproc_pcie_msi_steer(struct iproc_pcie *pcie, 13318c2ecf20Sopenharmony_ci struct device_node *msi_node) 13328c2ecf20Sopenharmony_ci{ 13338c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 13348c2ecf20Sopenharmony_ci int ret; 13358c2ecf20Sopenharmony_ci u64 msi_addr; 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci ret = iproce_pcie_get_msi(pcie, msi_node, &msi_addr); 13388c2ecf20Sopenharmony_ci if (ret < 0) { 13398c2ecf20Sopenharmony_ci dev_err(dev, "msi steering failed\n"); 13408c2ecf20Sopenharmony_ci return ret; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci switch (pcie->type) { 13448c2ecf20Sopenharmony_ci case IPROC_PCIE_PAXB_V2: 13458c2ecf20Sopenharmony_ci ret = iproc_pcie_paxb_v2_msi_steer(pcie, msi_addr); 13468c2ecf20Sopenharmony_ci if (ret) 13478c2ecf20Sopenharmony_ci return ret; 13488c2ecf20Sopenharmony_ci break; 13498c2ecf20Sopenharmony_ci case IPROC_PCIE_PAXC_V2: 13508c2ecf20Sopenharmony_ci iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr, true); 13518c2ecf20Sopenharmony_ci break; 13528c2ecf20Sopenharmony_ci default: 13538c2ecf20Sopenharmony_ci return -EINVAL; 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci return 0; 13578c2ecf20Sopenharmony_ci} 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic int iproc_pcie_msi_enable(struct iproc_pcie *pcie) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci struct device_node *msi_node; 13628c2ecf20Sopenharmony_ci int ret; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci /* 13658c2ecf20Sopenharmony_ci * Either the "msi-parent" or the "msi-map" phandle needs to exist 13668c2ecf20Sopenharmony_ci * for us to obtain the MSI node. 13678c2ecf20Sopenharmony_ci */ 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci msi_node = of_parse_phandle(pcie->dev->of_node, "msi-parent", 0); 13708c2ecf20Sopenharmony_ci if (!msi_node) { 13718c2ecf20Sopenharmony_ci const __be32 *msi_map = NULL; 13728c2ecf20Sopenharmony_ci int len; 13738c2ecf20Sopenharmony_ci u32 phandle; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci msi_map = of_get_property(pcie->dev->of_node, "msi-map", &len); 13768c2ecf20Sopenharmony_ci if (!msi_map) 13778c2ecf20Sopenharmony_ci return -ENODEV; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci phandle = be32_to_cpup(msi_map + 1); 13808c2ecf20Sopenharmony_ci msi_node = of_find_node_by_phandle(phandle); 13818c2ecf20Sopenharmony_ci if (!msi_node) 13828c2ecf20Sopenharmony_ci return -ENODEV; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci /* 13868c2ecf20Sopenharmony_ci * Certain revisions of the iProc PCIe controller require additional 13878c2ecf20Sopenharmony_ci * configurations to steer the MSI writes towards an external MSI 13888c2ecf20Sopenharmony_ci * controller. 13898c2ecf20Sopenharmony_ci */ 13908c2ecf20Sopenharmony_ci if (pcie->need_msi_steer) { 13918c2ecf20Sopenharmony_ci ret = iproc_pcie_msi_steer(pcie, msi_node); 13928c2ecf20Sopenharmony_ci if (ret) 13938c2ecf20Sopenharmony_ci goto out_put_node; 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci /* 13978c2ecf20Sopenharmony_ci * If another MSI controller is being used, the call below should fail 13988c2ecf20Sopenharmony_ci * but that is okay 13998c2ecf20Sopenharmony_ci */ 14008c2ecf20Sopenharmony_ci ret = iproc_msi_init(pcie, msi_node); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ciout_put_node: 14038c2ecf20Sopenharmony_ci of_node_put(msi_node); 14048c2ecf20Sopenharmony_ci return ret; 14058c2ecf20Sopenharmony_ci} 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_cistatic void iproc_pcie_msi_disable(struct iproc_pcie *pcie) 14088c2ecf20Sopenharmony_ci{ 14098c2ecf20Sopenharmony_ci iproc_msi_exit(pcie); 14108c2ecf20Sopenharmony_ci} 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_cistatic int iproc_pcie_rev_init(struct iproc_pcie *pcie) 14138c2ecf20Sopenharmony_ci{ 14148c2ecf20Sopenharmony_ci struct device *dev = pcie->dev; 14158c2ecf20Sopenharmony_ci unsigned int reg_idx; 14168c2ecf20Sopenharmony_ci const u16 *regs; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci switch (pcie->type) { 14198c2ecf20Sopenharmony_ci case IPROC_PCIE_PAXB_BCMA: 14208c2ecf20Sopenharmony_ci regs = iproc_pcie_reg_paxb_bcma; 14218c2ecf20Sopenharmony_ci break; 14228c2ecf20Sopenharmony_ci case IPROC_PCIE_PAXB: 14238c2ecf20Sopenharmony_ci regs = iproc_pcie_reg_paxb; 14248c2ecf20Sopenharmony_ci pcie->has_apb_err_disable = true; 14258c2ecf20Sopenharmony_ci if (pcie->need_ob_cfg) { 14268c2ecf20Sopenharmony_ci pcie->ob_map = paxb_ob_map; 14278c2ecf20Sopenharmony_ci pcie->ob.nr_windows = ARRAY_SIZE(paxb_ob_map); 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci break; 14308c2ecf20Sopenharmony_ci case IPROC_PCIE_PAXB_V2: 14318c2ecf20Sopenharmony_ci regs = iproc_pcie_reg_paxb_v2; 14328c2ecf20Sopenharmony_ci pcie->iproc_cfg_read = true; 14338c2ecf20Sopenharmony_ci pcie->has_apb_err_disable = true; 14348c2ecf20Sopenharmony_ci if (pcie->need_ob_cfg) { 14358c2ecf20Sopenharmony_ci pcie->ob_map = paxb_v2_ob_map; 14368c2ecf20Sopenharmony_ci pcie->ob.nr_windows = ARRAY_SIZE(paxb_v2_ob_map); 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci pcie->ib.nr_regions = ARRAY_SIZE(paxb_v2_ib_map); 14398c2ecf20Sopenharmony_ci pcie->ib_map = paxb_v2_ib_map; 14408c2ecf20Sopenharmony_ci pcie->need_msi_steer = true; 14418c2ecf20Sopenharmony_ci dev_warn(dev, "reads of config registers that contain %#x return incorrect data\n", 14428c2ecf20Sopenharmony_ci CFG_RETRY_STATUS); 14438c2ecf20Sopenharmony_ci break; 14448c2ecf20Sopenharmony_ci case IPROC_PCIE_PAXC: 14458c2ecf20Sopenharmony_ci regs = iproc_pcie_reg_paxc; 14468c2ecf20Sopenharmony_ci pcie->ep_is_internal = true; 14478c2ecf20Sopenharmony_ci pcie->iproc_cfg_read = true; 14488c2ecf20Sopenharmony_ci pcie->rej_unconfig_pf = true; 14498c2ecf20Sopenharmony_ci break; 14508c2ecf20Sopenharmony_ci case IPROC_PCIE_PAXC_V2: 14518c2ecf20Sopenharmony_ci regs = iproc_pcie_reg_paxc_v2; 14528c2ecf20Sopenharmony_ci pcie->ep_is_internal = true; 14538c2ecf20Sopenharmony_ci pcie->iproc_cfg_read = true; 14548c2ecf20Sopenharmony_ci pcie->rej_unconfig_pf = true; 14558c2ecf20Sopenharmony_ci pcie->need_msi_steer = true; 14568c2ecf20Sopenharmony_ci break; 14578c2ecf20Sopenharmony_ci default: 14588c2ecf20Sopenharmony_ci dev_err(dev, "incompatible iProc PCIe interface\n"); 14598c2ecf20Sopenharmony_ci return -EINVAL; 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci pcie->reg_offsets = devm_kcalloc(dev, IPROC_PCIE_MAX_NUM_REG, 14638c2ecf20Sopenharmony_ci sizeof(*pcie->reg_offsets), 14648c2ecf20Sopenharmony_ci GFP_KERNEL); 14658c2ecf20Sopenharmony_ci if (!pcie->reg_offsets) 14668c2ecf20Sopenharmony_ci return -ENOMEM; 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci /* go through the register table and populate all valid registers */ 14698c2ecf20Sopenharmony_ci pcie->reg_offsets[0] = (pcie->type == IPROC_PCIE_PAXC_V2) ? 14708c2ecf20Sopenharmony_ci IPROC_PCIE_REG_INVALID : regs[0]; 14718c2ecf20Sopenharmony_ci for (reg_idx = 1; reg_idx < IPROC_PCIE_MAX_NUM_REG; reg_idx++) 14728c2ecf20Sopenharmony_ci pcie->reg_offsets[reg_idx] = regs[reg_idx] ? 14738c2ecf20Sopenharmony_ci regs[reg_idx] : IPROC_PCIE_REG_INVALID; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci return 0; 14768c2ecf20Sopenharmony_ci} 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ciint iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) 14798c2ecf20Sopenharmony_ci{ 14808c2ecf20Sopenharmony_ci struct device *dev; 14818c2ecf20Sopenharmony_ci int ret; 14828c2ecf20Sopenharmony_ci struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci dev = pcie->dev; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci ret = iproc_pcie_rev_init(pcie); 14878c2ecf20Sopenharmony_ci if (ret) { 14888c2ecf20Sopenharmony_ci dev_err(dev, "unable to initialize controller parameters\n"); 14898c2ecf20Sopenharmony_ci return ret; 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci ret = phy_init(pcie->phy); 14938c2ecf20Sopenharmony_ci if (ret) { 14948c2ecf20Sopenharmony_ci dev_err(dev, "unable to initialize PCIe PHY\n"); 14958c2ecf20Sopenharmony_ci return ret; 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci ret = phy_power_on(pcie->phy); 14998c2ecf20Sopenharmony_ci if (ret) { 15008c2ecf20Sopenharmony_ci dev_err(dev, "unable to power on PCIe PHY\n"); 15018c2ecf20Sopenharmony_ci goto err_exit_phy; 15028c2ecf20Sopenharmony_ci } 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci iproc_pcie_perst_ctrl(pcie, true); 15058c2ecf20Sopenharmony_ci iproc_pcie_perst_ctrl(pcie, false); 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci iproc_pcie_invalidate_mapping(pcie); 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (pcie->need_ob_cfg) { 15108c2ecf20Sopenharmony_ci ret = iproc_pcie_map_ranges(pcie, res); 15118c2ecf20Sopenharmony_ci if (ret) { 15128c2ecf20Sopenharmony_ci dev_err(dev, "map failed\n"); 15138c2ecf20Sopenharmony_ci goto err_power_off_phy; 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci if (pcie->need_ib_cfg) { 15188c2ecf20Sopenharmony_ci ret = iproc_pcie_map_dma_ranges(pcie); 15198c2ecf20Sopenharmony_ci if (ret && ret != -ENOENT) 15208c2ecf20Sopenharmony_ci goto err_power_off_phy; 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci ret = iproc_pcie_check_link(pcie); 15248c2ecf20Sopenharmony_ci if (ret) { 15258c2ecf20Sopenharmony_ci dev_err(dev, "no PCIe EP device detected\n"); 15268c2ecf20Sopenharmony_ci goto err_power_off_phy; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci iproc_pcie_enable(pcie); 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_PCI_MSI)) 15328c2ecf20Sopenharmony_ci if (iproc_pcie_msi_enable(pcie)) 15338c2ecf20Sopenharmony_ci dev_info(dev, "not using iProc MSI\n"); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci host->ops = &iproc_pcie_ops; 15368c2ecf20Sopenharmony_ci host->sysdata = pcie; 15378c2ecf20Sopenharmony_ci host->map_irq = pcie->map_irq; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci ret = pci_host_probe(host); 15408c2ecf20Sopenharmony_ci if (ret < 0) { 15418c2ecf20Sopenharmony_ci dev_err(dev, "failed to scan host: %d\n", ret); 15428c2ecf20Sopenharmony_ci goto err_power_off_phy; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci return 0; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cierr_power_off_phy: 15488c2ecf20Sopenharmony_ci phy_power_off(pcie->phy); 15498c2ecf20Sopenharmony_cierr_exit_phy: 15508c2ecf20Sopenharmony_ci phy_exit(pcie->phy); 15518c2ecf20Sopenharmony_ci return ret; 15528c2ecf20Sopenharmony_ci} 15538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iproc_pcie_setup); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ciint iproc_pcie_remove(struct iproc_pcie *pcie) 15568c2ecf20Sopenharmony_ci{ 15578c2ecf20Sopenharmony_ci struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci pci_stop_root_bus(host->bus); 15608c2ecf20Sopenharmony_ci pci_remove_root_bus(host->bus); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci iproc_pcie_msi_disable(pcie); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci phy_power_off(pcie->phy); 15658c2ecf20Sopenharmony_ci phy_exit(pcie->phy); 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci return 0; 15688c2ecf20Sopenharmony_ci} 15698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iproc_pcie_remove); 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci/* 15728c2ecf20Sopenharmony_ci * The MSI parsing logic in certain revisions of Broadcom PAXC based root 15738c2ecf20Sopenharmony_ci * complex does not work and needs to be disabled 15748c2ecf20Sopenharmony_ci */ 15758c2ecf20Sopenharmony_cistatic void quirk_paxc_disable_msi_parsing(struct pci_dev *pdev) 15768c2ecf20Sopenharmony_ci{ 15778c2ecf20Sopenharmony_ci struct iproc_pcie *pcie = iproc_data(pdev->bus); 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) 15808c2ecf20Sopenharmony_ci iproc_pcie_paxc_v2_msi_steer(pcie, 0, false); 15818c2ecf20Sopenharmony_ci} 15828c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, 15838c2ecf20Sopenharmony_ci quirk_paxc_disable_msi_parsing); 15848c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, 15858c2ecf20Sopenharmony_ci quirk_paxc_disable_msi_parsing); 15868c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, 15878c2ecf20Sopenharmony_ci quirk_paxc_disable_msi_parsing); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_cistatic void quirk_paxc_bridge(struct pci_dev *pdev) 15908c2ecf20Sopenharmony_ci{ 15918c2ecf20Sopenharmony_ci /* 15928c2ecf20Sopenharmony_ci * The PCI config space is shared with the PAXC root port and the first 15938c2ecf20Sopenharmony_ci * Ethernet device. So, we need to workaround this by telling the PCI 15948c2ecf20Sopenharmony_ci * code that the bridge is not an Ethernet device. 15958c2ecf20Sopenharmony_ci */ 15968c2ecf20Sopenharmony_ci if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) 15978c2ecf20Sopenharmony_ci pdev->class = PCI_CLASS_BRIDGE_PCI << 8; 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci /* 16008c2ecf20Sopenharmony_ci * MPSS is not being set properly (as it is currently 0). This is 16018c2ecf20Sopenharmony_ci * because that area of the PCI config space is hard coded to zero, and 16028c2ecf20Sopenharmony_ci * is not modifiable by firmware. Set this to 2 (e.g., 512 byte MPS) 16038c2ecf20Sopenharmony_ci * so that the MPS can be set to the real max value. 16048c2ecf20Sopenharmony_ci */ 16058c2ecf20Sopenharmony_ci pdev->pcie_mpss = 2; 16068c2ecf20Sopenharmony_ci} 16078c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16cd, quirk_paxc_bridge); 16088c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0x16f0, quirk_paxc_bridge); 16098c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd750, quirk_paxc_bridge); 16108c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd802, quirk_paxc_bridge); 16118c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_BROADCOM, 0xd804, quirk_paxc_bridge); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); 16148c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Broadcom iPROC PCIe common driver"); 16158c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1616