aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/controller/pcie-brcmstb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/controller/pcie-brcmstb.c')
-rw-r--r--drivers/pci/controller/pcie-brcmstb.c1070
1 files changed, 575 insertions, 495 deletions
diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c
index ed25178d02d7..169a4d82519a 100644
--- a/drivers/pci/controller/pcie-brcmstb.c
+++ b/drivers/pci/controller/pcie-brcmstb.c
@@ -5,6 +5,7 @@
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/compiler.h>
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -48,15 +49,6 @@
#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc
#define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00
-#define PCIE_RC_TL_VDM_CTL0 0x0a20
-#define PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK 0x10000
-#define PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK 0x20000
-#define PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK 0x40000
-
-#define PCIE_RC_TL_VDM_CTL1 0x0a0c
-#define PCIE_RC_TL_VDM_CTL1_VDM_VNDRID0_MASK 0x0000ffff
-#define PCIE_RC_TL_VDM_CTL1_VDM_VNDRID1_MASK 0xffff0000
-
#define PCIE_RC_CFG_PRIV1_ROOT_CAP 0x4f8
#define PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK 0xf8
@@ -68,6 +60,34 @@
#define PCIE_RC_PL_PHY_CTL_15_DIS_PLL_PD_MASK 0x400000
#define PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK 0xff
+#define PCIE_RC_PL_STATS_CTRL 0x1940
+#define PCIE_RC_PL_STATS_CTRL_EN_MASK 0x1
+#define PCIE_RC_PL_STATS_CTRL_LEN_MASK 0xfffffff0
+
+#define PCIE_RC_PL_STATS_TXTLP_LO 0x1944
+#define PCIE_RC_PL_STATS_TXTLP_HI 0x1948
+#define PCIE_RC_PL_STATS_TXDLLP_LO 0x194c
+#define PCIE_RC_PL_STATS_TXDLLP_HI 0x1950
+#define PCIE_RC_PL_STATS_RXTLP_LO 0x195c
+#define PCIE_RC_PL_STATS_RXTLP_HI 0x1960
+#define PCIE_RC_PL_STATS_RXDLLP_LO 0x1964
+#define PCIE_RC_PL_STATS_RXDLLP_HI 0x1968
+#define PCIE_RC_PL_STATS_RXPL_ERR 0x1974
+#define PCIE_RC_PL_STATS_RXDL_ERR 0x1978
+#define PCIE_RC_PL_STATS_RXTL_ERR 0x197c
+
+#define PCIE_RC_PL_LTSSM_STATS_3 0x19b0
+#define PCIE_RC_PL_LTSSM_STATS_3_TIME_L0S_MASK 0xffff0000
+#define PCIE_RC_PL_LTSSM_STATS_3_TIME_RECOV_MASK 0x0000ffff
+
+#define PCIE_RC_PL_LTSSM_STATS_CNT 0x19b4
+#define PCIE_RC_PL_LTSSM_STATS_CNT_L0S_FAIL_MASK 0xffff0000
+#define PCIE_RC_PL_LTSSM_STATS_CNT_RECOV_MASK 0x0000ffff
+
+#define PCIE_RC_PL_LTSSM_HIST_0 0x1cec
+#define PCIE_RC_PL_LTSSM_HIST(n) \
+ (PCIE_RC_PL_LTSSM_HIST_0 + ((n) * 4))
+
#define PCIE_MISC_MISC_CTRL 0x4008
#define PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK 0x80
#define PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK 0x400
@@ -98,15 +118,9 @@
#define PCIE_BRCM_MAX_INBOUND_WINS 16
#define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c
#define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f
-#define PCIE_MISC_RC_BAR1_CONFIG_HI 0x4030
-#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034
-#define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f
-#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038
+#define PCIE_MISC_RC_BAR4_CONFIG_LO 0x40d4
-#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
-#define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f
-#define PCIE_MISC_RC_BAR3_CONFIG_HI 0x4040
#define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044
#define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048
@@ -115,15 +129,12 @@
#define PCIE_MISC_MSI_DATA_CONFIG_VAL_32 0xffe06540
#define PCIE_MISC_MSI_DATA_CONFIG_VAL_8 0xfff86540
-#define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT 0x405c
-
#define PCIE_MISC_PCIE_CTRL 0x4064
#define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1
#define PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK 0x4
#define PCIE_MISC_PCIE_STATUS 0x4068
#define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK 0x80
-#define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK_2712 0x40
#define PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK 0x20
#define PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK 0x10
#define PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK 0x40
@@ -150,81 +161,21 @@
#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2
#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK 0x8
+#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_REFCLK_OVRD_ENABLE_MASK 0x10000
+#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_REFCLK_OVRD_OUT_MASK 0x100000
#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK 0x200000
#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000
#define PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x00800000
#define PCIE_CLKREQ_MASK \
(PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK | \
+ PCIE_MISC_HARD_PCIE_HARD_DEBUG_REFCLK_OVRD_ENABLE_MASK | \
+ PCIE_MISC_HARD_PCIE_HARD_DEBUG_REFCLK_OVRD_OUT_MASK | \
PCIE_MISC_HARD_PCIE_HARD_DEBUG_L1SS_ENABLE_MASK)
-
#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP 0x40ac
#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_EN_MASK BIT(0)
#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP 0x410c
-#define PCIE_MISC_CTRL_1 0x40A0
-#define PCIE_MISC_CTRL_1_OUTBOUND_TC_MASK 0xf
-#define PCIE_MISC_CTRL_1_OUTBOUND_NO_SNOOP_MASK BIT(3)
-#define PCIE_MISC_CTRL_1_OUTBOUND_RO_MASK BIT(4)
-#define PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK BIT(5)
-
-#define PCIE_MISC_UBUS_CTRL 0x40a4
-#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK BIT(13)
-#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK BIT(19)
-
-#define PCIE_MISC_UBUS_TIMEOUT 0x40A8
-
-#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP 0x40ac
-#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0)
-#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI 0x40b0
-
-#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP 0x40b4
-#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0)
-
-/* Additional RC BARs */
-#define PCIE_MISC_RC_BAR_CONFIG_LO_SIZE_MASK 0x1f
-#define PCIE_MISC_RC_BAR4_CONFIG_LO 0x40d4
-#define PCIE_MISC_RC_BAR4_CONFIG_HI 0x40d8
-/* ... */
-#define PCIE_MISC_RC_BAR10_CONFIG_LO 0x4104
-#define PCIE_MISC_RC_BAR10_CONFIG_HI 0x4108
-
-#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE 0x1
-#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK 0xfffff000
-#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK 0xff
-#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO 0x410c
-#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI 0x4110
-/* ... */
-#define PCIE_MISC_UBUS_BAR10_CONFIG_REMAP_LO 0x413c
-#define PCIE_MISC_UBUS_BAR10_CONFIG_REMAP_HI 0x4140
-
-/* AXI priority forwarding - automatic level-based */
-#define PCIE_MISC_TC_QUEUE_TO_QOS_MAP(x) (0x4160 - (x) * 4)
-/* Defined in quarter-fullness */
-#define QUEUE_THRESHOLD_34_TO_QOS_MAP_SHIFT 12
-#define QUEUE_THRESHOLD_23_TO_QOS_MAP_SHIFT 8
-#define QUEUE_THRESHOLD_12_TO_QOS_MAP_SHIFT 4
-#define QUEUE_THRESHOLD_01_TO_QOS_MAP_SHIFT 0
-#define QUEUE_THRESHOLD_MASK 0xf
-
-/* VDM messages indexing TCs to AXI priorities */
-/* Indexes 8-15 */
-#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI 0x4164
-/* Indexes 0-7 */
-#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO 0x4168
-#define VDM_PRIORITY_TO_QOS_MAP_SHIFT(x) (4 * (x))
-#define VDM_PRIORITY_TO_QOS_MAP_MASK 0xf
-
-#define PCIE_MISC_AXI_INTF_CTRL 0x416C
-#define AXI_EN_RCLK_QOS_ARRAY_FIX BIT(13)
-#define AXI_EN_QOS_UPDATE_TIMING_FIX BIT(12)
-#define AXI_DIS_QOS_GATING_IN_MASTER BIT(11)
-#define AXI_REQFIFO_EN_QOS_PROPAGATION BIT(7)
-#define AXI_BRIDGE_LOW_LATENCY_MODE BIT(6)
-#define AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK 0x3f
-
-#define PCIE_MISC_AXI_READ_ERROR_DATA 0x4170
-
#define PCIE_MSI_INTR2_BASE 0x4500
/* Offsets from INTR2_CPU and MSI_INTR2 BASE offsets */
@@ -292,6 +243,58 @@
#define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_MASK 0x1
#define PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT 0x0
+/* BCM7712/2712-specific registers */
+
+#define PCIE_RC_TL_VDM_CTL0 0x0a20
+#define PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK 0x10000
+#define PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK 0x20000
+#define PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK 0x40000
+
+#define PCIE_RC_TL_VDM_CTL1 0x0a0c
+#define PCIE_RC_TL_VDM_CTL1_VDM_VNDRID0_MASK 0x0000ffff
+#define PCIE_RC_TL_VDM_CTL1_VDM_VNDRID1_MASK 0xffff0000
+
+#define PCIE_MISC_CTRL_1 0x40A0
+#define PCIE_MISC_CTRL_1_OUTBOUND_TC_MASK 0xf
+#define PCIE_MISC_CTRL_1_OUTBOUND_NO_SNOOP_MASK BIT(3)
+#define PCIE_MISC_CTRL_1_OUTBOUND_RO_MASK BIT(4)
+#define PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK BIT(5)
+
+#define PCIE_MISC_UBUS_CTRL 0x40a4
+#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK BIT(13)
+#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK BIT(19)
+
+#define PCIE_MISC_UBUS_TIMEOUT 0x40a8
+
+#define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT 0x405c
+
+/* AXI priority forwarding - automatic level-based */
+#define PCIE_MISC_TC_QUEUE_TO_QOS_MAP(x) (0x4160 - (x) * 4)
+/* Defined in quarter-fullness */
+#define QUEUE_THRESHOLD_34_TO_QOS_MAP_SHIFT 12
+#define QUEUE_THRESHOLD_23_TO_QOS_MAP_SHIFT 8
+#define QUEUE_THRESHOLD_12_TO_QOS_MAP_SHIFT 4
+#define QUEUE_THRESHOLD_01_TO_QOS_MAP_SHIFT 0
+#define QUEUE_THRESHOLD_MASK 0xf
+
+/* VDM messages indexing TCs to AXI priorities */
+/* Indexes 8-15 */
+#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI 0x4164
+/* Indexes 0-7 */
+#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO 0x4168
+#define VDM_PRIORITY_TO_QOS_MAP_SHIFT(x) (4 * (x))
+#define VDM_PRIORITY_TO_QOS_MAP_MASK 0xf
+
+#define PCIE_MISC_AXI_INTF_CTRL 0x416C
+#define AXI_EN_RCLK_QOS_ARRAY_FIX BIT(13)
+#define AXI_EN_QOS_UPDATE_TIMING_FIX BIT(12)
+#define AXI_DIS_QOS_GATING_IN_MASTER BIT(11)
+#define AXI_REQFIFO_EN_QOS_PROPAGATION BIT(7)
+#define AXI_BRIDGE_LOW_LATENCY_MODE BIT(6)
+#define AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK 0x3f
+
+#define PCIE_MISC_AXI_READ_ERROR_DATA 0x4170
+
/* Forward declarations */
struct brcm_pcie;
@@ -311,7 +314,6 @@ enum pcie_soc_base {
BCM7425,
BCM7435,
BCM7712,
- BCM2712,
};
struct inbound_win {
@@ -320,6 +322,91 @@ struct inbound_win {
u64 cpu_addr;
};
+#define TRACE_BUF_LEN 64
+struct trace_entry {
+ u8 st;
+ ktime_t ts;
+};
+
+static const char *brcm_pcie_decode_ltssm_state(u8 state)
+{
+ switch (state) {
+ case 0x01:
+ return "L0";
+ case 0x02:
+ return "L1";
+ case 0x03:
+ return "L2";
+ case 0x04:
+ return "RxL0s_TxL0s";
+ case 0x05:
+ return "RxL0_TxL0s";
+ case 0x09:
+ return "Polling.Active";
+ case 0x0A:
+ return "Polling.Configuration";
+ case 0x0C:
+ return "Polling.Compliance";
+ case 0x10:
+ return "Configuration.Link";
+ case 0x11:
+ return "Configuration.Linkwidth.Accept";
+ case 0x12:
+ return "Configuration.Lanenum.Wait";
+ case 0x13:
+ return "Confguration.Lanenum.Accept";
+ case 0x14:
+ return "Confguration.Complete";
+ case 0x15:
+ return "Configuration.Idle";
+ case 0x20:
+ return "Recovery.RcvrLock";
+ case 0x21:
+ return "Recovery.RcvrCfg";
+ case 0x22:
+ return "Recovery.Idle";
+ case 0x23:
+ return "Recovery.Speed";
+ case 0x24:
+ return "Recovery.EQ_Phase0";
+ case 0x25:
+ return "Recovery.EQ_Phase1";
+ case 0x26:
+ return "Recovery.EQ_Phase2";
+ case 0x27:
+ return "Recovery.EQ_Phase3";
+ case 0x40:
+ return "Disable";
+ case 0x43:
+ return "Reset";
+ case 0x47:
+ return "Loopback.Entry";
+ case 0x48:
+ return "Loopback.Active";
+ case 0x49:
+ return "Loopback.Exit";
+ case 0x4C:
+ return "Loopback.Master.Entry";
+ case 0x4D:
+ return "Loopback.Master.Active";
+ case 0x4E:
+ return "Loopback.Master.Exit";
+ case 0x70:
+ return "Recovery.Speed_Change";
+ case 0x80:
+ return "Detect.Quiet";
+ case 0x81:
+ return "Detect.Active";
+ case 0x82:
+ return "Detect.Wait";
+ case 0x83:
+ return "Detect.Active2";
+ default:
+ break;
+ }
+ return NULL;
+};
+
/*
* The RESCAL block is tied to PCIe controller #1, regardless of the number of
* controllers, and turning off PCIe controller #1 prevents access to the RESCAL
@@ -329,6 +416,12 @@ struct inbound_win {
*/
#define CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN BIT(0)
+/*
+ * MDIO register map differences and required changes to the defaults mean that refclk
+ * spread-spectrum clocking isn't supportable.
+ */
+#define CFG_QUIRK_NO_SSC BIT(1)
+
struct pcie_cfg_data {
const int *offsets;
const enum pcie_soc_base soc_base;
@@ -354,7 +447,7 @@ struct brcm_msi {
struct mutex lock; /* guards the alloc/free operations */
u64 target_addr;
int irq;
- DECLARE_BITMAP(used, 64);
+ DECLARE_BITMAP(used, BRCM_INT_PCI_MSI_NR);
bool legacy;
/* Some chips have MSIs in bits [31..24] of a shared register. */
int legacy_shift;
@@ -383,6 +476,22 @@ struct brcm_pcie {
struct subdev_regulators *sr;
bool ep_wakeup_capable;
const struct pcie_cfg_data *cfg;
+ u32 tperst_clk_ms;
+ bool trace_ltssm;
+ struct trace_entry *ltssm_trace_buf;
+ /* Statistics exposed in debugfs */
+ struct dentry *debugfs_dir;
+ u64 tx_tlp;
+ u64 rx_tlp;
+ u64 tx_dllp;
+ u64 rx_dllp;
+ u32 pl_rx_err;
+ u32 dl_rx_err;
+ u32 tl_rx_err;
+ u16 l0s_exit_time;
+ u16 recov_time;
+ u16 l0s_fail_cnt;
+ u16 recov_cnt;
};
static inline bool is_bmips(const struct brcm_pcie *pcie)
@@ -492,35 +601,6 @@ static int brcm_pcie_set_ssc(struct brcm_pcie *pcie)
return ssc && pll ? 0 : -EIO;
}
-static void brcm_pcie_munge_pll(struct brcm_pcie *pcie)
-{
- //print "MDIO block 0x1600 written per Dannys instruction"
- //tmp = pcie_mdio_write(phyad, &h16&, &h50b9&)
- //tmp = pcie_mdio_write(phyad, &h17&, &hbd1a&)
- //tmp = pcie_mdio_write(phyad, &h1b&, &h5030&)
- //tmp = pcie_mdio_write(phyad, &h1e&, &h0007&)
-
- u32 tmp;
- int ret, i;
- u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e };
- u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007 };
-
- ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET,
- 0x1600);
- for (i = 0; i < ARRAY_SIZE(regs); i++) {
- brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp);
- dev_dbg(pcie->dev, "PCIE MDIO pre_refclk 0x%02x = 0x%04x\n",
- regs[i], tmp);
- }
- for (i = 0; i < ARRAY_SIZE(regs); i++) {
- brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, regs[i], data[i]);
- brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp);
- dev_dbg(pcie->dev, "PCIE MDIO post_refclk 0x%02x = 0x%04x\n",
- regs[i], tmp);
- }
- usleep_range(100, 200);
-}
-
/* Limits operation to a specific generation (1, 2, or 3) */
static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
{
@@ -578,73 +658,6 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
}
-static void brcm_pcie_set_tc_qos(struct brcm_pcie *pcie)
-{
- int i;
- u32 reg;
-
- if (pcie->soc_base != BCM2712)
- return;
-
- /* Disable broken QOS forwarding search. Set chicken bits for 2712D0 */
- reg = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL);
- reg &= ~AXI_REQFIFO_EN_QOS_PROPAGATION;
- reg |= AXI_EN_RCLK_QOS_ARRAY_FIX | AXI_EN_QOS_UPDATE_TIMING_FIX |
- AXI_DIS_QOS_GATING_IN_MASTER;
- writel(reg, pcie->base + PCIE_MISC_AXI_INTF_CTRL);
-
- /*
- * If the QOS_UPDATE_TIMING_FIX bit is Reserved-0, then this is a
- * 2712C1 chip, or a single-lane RC. Use the best-effort alternative
- * which is to partially throttle AXI requests in-flight to the SDC.
- */
- reg = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL);
- if (!(reg & AXI_EN_QOS_UPDATE_TIMING_FIX)) {
- reg &= ~AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK;
- reg |= 15;
- writel(reg, pcie->base + PCIE_MISC_AXI_INTF_CTRL);
- }
-
- /* Disable VDM reception by default - QoS map defaults to 0 */
- reg = readl(pcie->base + PCIE_MISC_CTRL_1);
- reg &= ~PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK;
- writel(reg, pcie->base + PCIE_MISC_CTRL_1);
-
- if (!of_property_read_u32(pcie->np, "brcm,fifo-qos-map", &pcie->qos_map)) {
- /*
- * Backpressure mode - bottom 4 nibbles are QoS for each
- * quartile of FIFO level. Each TC gets the same map, because
- * this mode is intended for nonrealtime EPs.
- */
-
- pcie->qos_map &= 0x0000ffff;
- for (i = 0; i < 8; i++)
- writel(pcie->qos_map, pcie->base + PCIE_MISC_TC_QUEUE_TO_QOS_MAP(i));
-
- return;
- }
-
- if (!of_property_read_u32(pcie->np, "brcm,vdm-qos-map", &pcie->qos_map)) {
-
- reg = readl(pcie->base + PCIE_MISC_CTRL_1);
- reg |= PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK;
- writel(reg, pcie->base + PCIE_MISC_CTRL_1);
-
- /* No forwarding means no point separating panic priorities from normal */
- writel(pcie->qos_map, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO);
- writel(pcie->qos_map, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI);
-
- /* Match Vendor ID of 0 */
- writel(0, pcie->base + PCIE_RC_TL_VDM_CTL1);
- /* Forward VDMs to priority interface - at least the rx counters work */
- reg = readl(pcie->base + PCIE_RC_TL_VDM_CTL0);
- reg |= PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK |
- PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK |
- PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK;
- writel(reg, pcie->base + PCIE_RC_TL_VDM_CTL0);
- }
-}
-
static struct irq_chip brcm_msi_irq_chip = {
.name = "BRCM STB PCIe MSI",
.irq_ack = irq_chip_ack_parent,
@@ -653,8 +666,8 @@ static struct irq_chip brcm_msi_irq_chip = {
};
static struct msi_domain_info brcm_msi_domain_info = {
- .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
- MSI_FLAG_NO_AFFINITY | MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX),
+ .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_NO_AFFINITY | MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX,
.chip = &brcm_msi_irq_chip,
};
@@ -673,23 +686,10 @@ static void brcm_pcie_msi_isr(struct irq_desc *desc)
status = readl(msi->intr_base + MSI_INT_STATUS);
status >>= msi->legacy_shift;
- for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR/*msi->nr*/) {
- unsigned long virq;
- bool found = false;
-
- virq = irq_find_mapping(msi->inner_domain, bit);
- if (virq) {
- found = true;
- dev_dbg(dev, "MSI -> %ld\n", virq);
- generic_handle_irq(virq);
- }
- virq = irq_find_mapping(msi->inner_domain, bit + 32);
- if (virq) {
- found = true;
- dev_dbg(dev, "MSI -> %ld\n", virq);
- generic_handle_irq(virq);
- }
- if (!found)
+ for_each_set_bit(bit, &status, msi->nr) {
+ int ret;
+ ret = generic_handle_domain_irq(msi->inner_domain, bit);
+ if (ret)
dev_dbg(dev, "unexpected MSI\n");
}
@@ -702,13 +702,13 @@ static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
msg->address_lo = lower_32_bits(msi->target_addr);
msg->address_hi = upper_32_bits(msi->target_addr);
- msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | (data->hwirq & 0x1f);
+ msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | data->hwirq;
}
static void brcm_msi_ack_irq(struct irq_data *data)
{
struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
- const int shift_amt = (data->hwirq & 0x1f) + msi->legacy_shift;
+ const int shift_amt = data->hwirq + msi->legacy_shift;
writel(1 << shift_amt, msi->intr_base + MSI_INT_CLR);
}
@@ -868,7 +868,7 @@ static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
msi->legacy_shift = 24;
} else {
msi->intr_base = msi->base + PCIE_MSI_INTR2_BASE;
- msi->nr = 64; //BRCM_INT_PCI_MSI_NR;
+ msi->nr = BRCM_INT_PCI_MSI_NR;
msi->legacy_shift = 0;
}
@@ -890,9 +890,6 @@ static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
void __iomem *base = pcie->base;
u32 val = readl(base + PCIE_MISC_PCIE_STATUS);
- if (pcie->soc_base == BCM2712)
- return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK_2712, val) | 1; //XXX
-
return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val);
}
@@ -985,21 +982,6 @@ static int brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val)
return 0;
}
-static int brcm_pcie_bridge_sw_init_set_2712(struct brcm_pcie *pcie, u32 val)
-{
- int ret;
-
- if (WARN_ONCE(!pcie->bridge_reset, "missing bridge reset controller\n"))
- return -EINVAL;
-
- if (val)
- ret = reset_control_assert(pcie->bridge_reset);
- else
- ret = reset_control_deassert(pcie->bridge_reset);
-
- return ret;
-}
-
static int brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val)
{
int ret;
@@ -1030,18 +1012,6 @@ static int brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val)
return 0;
}
-static int brcm_pcie_perst_set_2712(struct brcm_pcie *pcie, u32 val)
-{
- u32 tmp;
-
- /* Perst bit has moved and assert value is 0 */
- tmp = readl(pcie->base + PCIE_MISC_PCIE_CTRL);
- u32p_replace_bits(&tmp, !val, PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK);
- writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL);
-
- return 0;
-}
-
static int brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
{
u32 tmp;
@@ -1060,6 +1030,7 @@ static int brcm_pcie_post_setup_bcm2712(struct brcm_pcie *pcie)
static const u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e };
int ret, i;
u32 tmp;
+ u8 qos_map[8];
/* Allow a 54MHz (xosc) refclk source */
ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET, 0x1600);
@@ -1083,6 +1054,108 @@ static int brcm_pcie_post_setup_bcm2712(struct brcm_pcie *pcie)
tmp |= 0x12;
writel(tmp, pcie->base + PCIE_RC_PL_PHY_CTL_15);
+ /*
+ * BCM7712/2712 uses a UBUS-AXI bridge.
+ * Suppress AXI error responses and return 1s for read failures.
+ */
+ tmp = readl(pcie->base + PCIE_MISC_UBUS_CTRL);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK);
+ writel(tmp, pcie->base + PCIE_MISC_UBUS_CTRL);
+ writel(0xffffffff, pcie->base + PCIE_MISC_AXI_READ_ERROR_DATA);
+
+ /*
+ * Adjust timeouts. The UBUS timeout also affects Configuration Request
+ * Retry responses, as the request will get terminated if
+ * either timeout expires, so both have to be a large value
+ * (in clocks of 750MHz).
+ * Set UBUS timeout to 250ms, then set RC config retry timeout
+ * to be ~240ms.
+ *
+ * If CRSSVE=1 this will stop the core from blocking on a Retry
+ * response, but does require the device to be well-behaved...
+ */
+ writel(0xB2D0000, pcie->base + PCIE_MISC_UBUS_TIMEOUT);
+ writel(0xABA0000, pcie->base + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT);
+
+ /*
+ * BCM2712 has a configurable QoS mechanism that assigns TLP Traffic Classes
+ * to separate AXI IDs with a configurable priority scheme.
+ * Dynamic priority elevation is supported through reception of Type 1
+ * Vendor Defined Messages, but several bugs make this largely ineffective.
+ */
+
+ /* Disable broken forwarding search. Set chicken bits for 2712D0 */
+ tmp = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL);
+ tmp &= ~AXI_REQFIFO_EN_QOS_PROPAGATION;
+ tmp |= AXI_EN_RCLK_QOS_ARRAY_FIX | AXI_EN_QOS_UPDATE_TIMING_FIX |
+ AXI_DIS_QOS_GATING_IN_MASTER;
+ writel(tmp, pcie->base + PCIE_MISC_AXI_INTF_CTRL);
+
+ /*
+ * Work around spurious QoS=0 assignments to inbound traffic.
+ * If the QOS_UPDATE_TIMING_FIX bit is Reserved-0, then this is a
+ * 2712C1 chip, or a single-lane RC. Use the best-effort alternative
+ * which is to partially throttle AXI requests in-flight to SDRAM.
+ */
+ tmp = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL);
+ if (!(tmp & AXI_EN_QOS_UPDATE_TIMING_FIX)) {
+ tmp &= ~AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK;
+ tmp |= 15;
+ writel(tmp, pcie->base + PCIE_MISC_AXI_INTF_CTRL);
+ }
+
+ /* Disable VDM reception by default */
+ tmp = readl(pcie->base + PCIE_MISC_CTRL_1);
+ tmp &= ~PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK;
+ writel(tmp, pcie->base + PCIE_MISC_CTRL_1);
+
+ if (!of_property_read_u8_array(pcie->np, "brcm,fifo-qos-map", qos_map, 4)) {
+
+ /*
+ * Backpressure mode - each element is QoS for each
+ * quartile of FIFO level. Each TC gets the same map, because
+ * this mode is intended for nonrealtime EPs.
+ */
+ tmp = 0;
+ for (i = 0; i < 4; i++) {
+ /* Priorities range from 0-15 */
+ qos_map[i] &= 0x0f;
+ tmp |= qos_map[i] << (i * 4);
+ }
+ for (i = 0; i < 8; i++)
+ writel(tmp, pcie->base + PCIE_MISC_TC_QUEUE_TO_QOS_MAP(i));
+
+ } else if (!of_property_read_u8_array(pcie->np, "brcm,vdm-qos-map", qos_map, 8)) {
+ /* Enable VDM reception */
+ tmp = readl(pcie->base + PCIE_MISC_CTRL_1);
+ tmp |= PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK;
+ writel(tmp, pcie->base + PCIE_MISC_CTRL_1);
+
+ tmp = 0;
+ for (i = 0; i < 8; i++) {
+ /* Priorities range from 0-15 */
+ qos_map[i] &= 0x0f;
+ tmp |= qos_map[i] << (i * 4);
+ }
+ /* Broken forwarding means no point separating panic priorities from normal */
+ writel(tmp, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO);
+ writel(tmp, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI);
+
+ /* Match Vendor ID of 0 */
+ writel(0, pcie->base + PCIE_RC_TL_VDM_CTL1);
+
+ /*
+ * Forward VDMs to priority interface anyway -
+ * useful for debugging starvation through the received VDM count fields.
+ */
+ tmp = readl(pcie->base + PCIE_RC_TL_VDM_CTL0);
+ tmp |= PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK |
+ PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK |
+ PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK;
+ writel(tmp, pcie->base + PCIE_RC_TL_VDM_CTL0);
+ }
+
return 0;
}
@@ -1289,135 +1362,16 @@ static void set_inbound_win_registers(struct brcm_pcie *pcie,
}
}
-static int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
- u64 *rc_bar2_size,
- u64 *rc_bar2_offset)
-{
- struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
- struct resource_entry *entry;
- struct device *dev = pcie->dev;
- u64 lowest_pcie_addr = ~(u64)0;
- int ret, i = 0;
- u64 size = 0;
-
- resource_list_for_each_entry(entry, &bridge->dma_ranges) {
- u64 pcie_beg = entry->res->start - entry->offset;
-
- size += entry->res->end - entry->res->start + 1;
- if (pcie_beg < lowest_pcie_addr)
- lowest_pcie_addr = pcie_beg;
- if (pcie->soc_base == BCM2711 || pcie->soc_base == BCM2712)
- break; // Only consider the first entry
- }
-
- if (lowest_pcie_addr == ~(u64)0) {
- dev_err(dev, "DT node has no dma-ranges\n");
- return -EINVAL;
- }
-
- ret = of_property_read_variable_u64_array(pcie->np, "brcm,scb-sizes", pcie->memc_size, 1,
- PCIE_BRCM_MAX_MEMC);
-
- if (ret <= 0) {
- /* Make an educated guess */
- pcie->num_memc = 1;
- pcie->memc_size[0] = 1ULL << fls64(size - 1);
- } else {
- pcie->num_memc = ret;
- }
-
- /* Each memc is viewed through a "port" that is a power of 2 */
- for (i = 0, size = 0; i < pcie->num_memc; i++)
- size += pcie->memc_size[i];
-
- /* System memory starts at this address in PCIe-space */
- *rc_bar2_offset = lowest_pcie_addr;
- /* The sum of all memc views must also be a power of 2 */
- *rc_bar2_size = 1ULL << fls64(size - 1);
-
- /*
- * We validate the inbound memory view even though we should trust
- * whatever the device-tree provides. This is because of an HW issue on
- * early Raspberry Pi 4's revisions (bcm2711). It turns out its
- * firmware has to dynamically edit dma-ranges due to a bug on the
- * PCIe controller integration, which prohibits any access above the
- * lower 3GB of memory. Given this, we decided to keep the dma-ranges
- * in check, avoiding hard to debug device-tree related issues in the
- * future:
- *
- * The PCIe host controller by design must set the inbound viewport to
- * be a contiguous arrangement of all of the system's memory. In
- * addition, its size mut be a power of two. To further complicate
- * matters, the viewport must start on a pcie-address that is aligned
- * on a multiple of its size. If a portion of the viewport does not
- * represent system memory -- e.g. 3GB of memory requires a 4GB
- * viewport -- we can map the outbound memory in or after 3GB and even
- * though the viewport will overlap the outbound memory the controller
- * will know to send outbound memory downstream and everything else
- * upstream.
- *
- * For example:
- *
- * - The best-case scenario, memory up to 3GB, is to place the inbound
- * region in the first 4GB of pcie-space, as some legacy devices can
- * only address 32bits. We would also like to put the MSI under 4GB
- * as well, since some devices require a 32bit MSI target address.
- *
- * - If the system memory is 4GB or larger we cannot start the inbound
- * region at location 0 (since we have to allow some space for
- * outbound memory @ 3GB). So instead it will start at the 1x
- * multiple of its size
- */
- if (!*rc_bar2_size || (*rc_bar2_offset & (*rc_bar2_size - 1)) ||
- (*rc_bar2_offset < SZ_4G && *rc_bar2_offset > SZ_2G)) {
- dev_err(dev, "Invalid rc_bar2_offset/size: size 0x%llx, off 0x%llx\n",
- *rc_bar2_size, *rc_bar2_offset);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int brcm_pcie_get_rc_bar_n(struct brcm_pcie *pcie,
- int idx,
- u64 *rc_bar_cpu,
- u64 *rc_bar_size,
- u64 *rc_bar_pci)
-{
- struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
- struct resource_entry *entry;
- int i = 0;
-
- resource_list_for_each_entry(entry, &bridge->dma_ranges) {
- if (i == idx) {
- *rc_bar_cpu = entry->res->start;
- *rc_bar_size = entry->res->end - entry->res->start + 1;
- *rc_bar_pci = entry->res->start - entry->offset;
- return 0;
- }
-
- i++;
- }
-
- return -EINVAL;
-}
-
static int brcm_pcie_setup(struct brcm_pcie *pcie)
{
-#if 0
struct inbound_win inbound_wins[PCIE_BRCM_MAX_INBOUND_WINS];
-#endif
- u64 rc_bar2_offset, rc_bar2_size;
void __iomem *base = pcie->base;
struct pci_host_bridge *bridge;
struct resource_entry *entry;
u32 tmp, burst, aspm_support;
u8 num_out_wins = 0;
-#if 0
int num_inbound_wins = 0;
-#endif
int memc, ret;
- int count, i;
/* Reset the bridge */
ret = pcie->cfg->bridge_sw_init_set(pcie, 1);
@@ -1449,17 +1403,6 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
/* Wait for SerDes to be stable */
usleep_range(100, 200);
- if (pcie->soc_base == BCM2712) {
- /* Allow a 54MHz (xosc) refclk source */
- brcm_pcie_munge_pll(pcie);
- /* Fix for L1SS errata */
- tmp = readl(base + PCIE_RC_PL_PHY_CTL_15);
- tmp &= ~PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK;
- /* PM clock period is 18.52ns (round down) */
- tmp |= 0x12;
- writel(tmp, base + PCIE_RC_PL_PHY_CTL_15);
- }
-
/*
* SCB_MAX_BURST_SIZE is a two bit field. For GENERIC chips it
* is encoded as 0=128, 1=256, 2=512, 3=Rsvd, for BCM7278 it
@@ -1476,38 +1419,27 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
/*
* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN,
- * RCB_MPS_MODE
+ * RCB_MPS_MODE, RCB_64B_MODE
*/
tmp = readl(base + PCIE_MISC_MISC_CTRL);
u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK);
u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK);
u32p_replace_bits(&tmp, burst, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
- if (pcie->rcb_mps_mode)
- u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_MPS_MODE_MASK);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_PCIE_RCB_64B_MODE_MASK);
writel(tmp, base + PCIE_MISC_MISC_CTRL);
- brcm_pcie_set_tc_qos(pcie);
+ num_inbound_wins = brcm_pcie_get_inbound_wins(pcie, inbound_wins);
+ if (num_inbound_wins < 0)
+ return num_inbound_wins;
- ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size,
- &rc_bar2_offset);
- if (ret)
- return ret;
-
- tmp = lower_32_bits(rc_bar2_offset);
- u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(rc_bar2_size),
- PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK);
- writel(tmp, base + PCIE_MISC_RC_BAR2_CONFIG_LO);
- writel(upper_32_bits(rc_bar2_offset),
- base + PCIE_MISC_RC_BAR2_CONFIG_HI);
+ set_inbound_win_registers(pcie, inbound_wins, num_inbound_wins);
if (!brcm_pcie_rc_mode(pcie)) {
dev_err(pcie->dev, "PCIe RC controller misconfigured as Endpoint\n");
return -EINVAL;
}
- tmp = readl(base + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP);
- u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK);
- writel(tmp, base + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP);
tmp = readl(base + PCIE_MISC_MISC_CTRL);
for (memc = 0; memc < pcie->num_memc; memc++) {
u32 scb_size_val = ilog2(pcie->memc_size[memc]) - 15;
@@ -1521,29 +1453,6 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
}
writel(tmp, base + PCIE_MISC_MISC_CTRL);
- if (pcie->soc_base == BCM2712) {
- /* Suppress AXI error responses and return 1s for read failures */
- tmp = readl(base + PCIE_MISC_UBUS_CTRL);
- u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK);
- u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK);
- writel(tmp, base + PCIE_MISC_UBUS_CTRL);
- writel(0xffffffff, base + PCIE_MISC_AXI_READ_ERROR_DATA);
-
- /*
- * Adjust timeouts. The UBUS timeout also affects CRS
- * completion retries, as the request will get terminated if
- * either timeout expires, so both have to be a large value
- * (in clocks of 750MHz).
- * Set UBUS timeout to 250ms, then set RC config retry timeout
- * to be ~240ms.
- *
- * Setting CRSVis=1 will stop the core from blocking on a CRS
- * response, but does require the device to be well-behaved...
- */
- writel(0xB2D0000, base + PCIE_MISC_UBUS_TIMEOUT);
- writel(0xABA0000, base + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT);
- }
-
/*
* We ideally want the MSI target address to be located in the 32bit
* addressable memory area. Some devices might depend on it. This is
@@ -1551,58 +1460,22 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
* 4GB or when the inbound area is smaller than 4GB (taking into
* account the rounding-up we're forced to perform).
*/
- if (rc_bar2_offset >= SZ_4G || (rc_bar2_size + rc_bar2_offset) < SZ_4G)
+ if (inbound_wins[2].pci_offset >= SZ_4G ||
+ (inbound_wins[2].size + inbound_wins[2].pci_offset) < SZ_4G)
pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_LT_4GB;
else
pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
- /* disable the PCIe->GISB memory window (RC_BAR1) */
- tmp = readl(base + PCIE_MISC_RC_BAR1_CONFIG_LO);
- tmp &= ~PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK;
- writel(tmp, base + PCIE_MISC_RC_BAR1_CONFIG_LO);
-
- /* disable the PCIe->SCB memory window (RC_BAR3) */
- tmp = readl(base + PCIE_MISC_RC_BAR3_CONFIG_LO);
- tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK;
- writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO);
- /* Always advertise L1 capability */
- aspm_support = BIT(1);
- /* Advertise L0s capability unless 'aspm-no-l0s' is set */
+ /* Don't advertise L0s capability if 'aspm-no-l0s' */
+ aspm_support = PCIE_LINK_STATE_L1;
if (!of_property_read_bool(pcie->np, "aspm-no-l0s"))
- aspm_support |= BIT(0);
+ aspm_support |= PCIE_LINK_STATE_L0S;
tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
u32p_replace_bits(&tmp, aspm_support,
PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
- /* program additional inbound windows (RC_BAR4..RC_BAR10) */
- count = (pcie->soc_base == BCM2712) ? 7 : 0;
- for (i = 0; i < count; i++) {
- u64 bar_cpu, bar_size, bar_pci;
-
- ret = brcm_pcie_get_rc_bar_n(pcie, 1 + i, &bar_cpu, &bar_size,
- &bar_pci);
- if (ret)
- break;
-
- tmp = lower_32_bits(bar_pci);
- u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size),
- PCIE_MISC_RC_BAR_CONFIG_LO_SIZE_MASK);
- writel(tmp, base + PCIE_MISC_RC_BAR4_CONFIG_LO + i * 8);
- writel(upper_32_bits(bar_pci),
- base + PCIE_MISC_RC_BAR4_CONFIG_HI + i * 8);
-
- tmp = upper_32_bits(bar_cpu) &
- PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK;
- writel(tmp,
- base + PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI + i * 8);
- tmp = lower_32_bits(bar_cpu) &
- PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK;
- writel(tmp | PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE,
- base + PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO + i * 8);
- }
-
/*
* For config space accesses on the RC, show the right class for
* a PCIe-PCIe bridge (the default setting is to be EP mode).
@@ -1727,32 +1600,169 @@ static void brcm_config_clkreq(struct brcm_pcie *pcie)
} else {
/*
- * "safe" -- No power savings; refclk is driven by RC
+ * "safe" -- No power savings; refclk and CLKREQ# are driven by RC
* unconditionally.
*/
if (strcmp(mode, "safe") != 0)
dev_err(pcie->dev, err_msg);
mode = "safe";
+ clkreq_cntl |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_REFCLK_OVRD_OUT_MASK;
+ clkreq_cntl |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_REFCLK_OVRD_ENABLE_MASK;
+ /*
+ * Un-advertise L1ss as configuring an EP to enter L1.x with CLKREQ#
+ * physically unconnected will result in a dead link.
+ */
+ tmp = readl(pcie->base + PCIE_RC_CFG_PRIV1_ROOT_CAP);
+ u32p_replace_bits(&tmp, 2, PCIE_RC_CFG_PRIV1_ROOT_CAP_L1SS_MODE_MASK);
+ writel(tmp, pcie->base + PCIE_RC_CFG_PRIV1_ROOT_CAP);
}
writel(clkreq_cntl, pcie->base + HARD_DEBUG(pcie));
dev_info(pcie->dev, "clkreq-mode set to %s\n", mode);
}
+static void brcm_pcie_stats_trigger(struct brcm_pcie *pcie, u32 micros)
+{
+ u32 tmp;
+
+ /*
+ * A 0->1 transition on CTRL_EN is required to clear counters and start capture.
+ * A microseconds count of 0 starts continuous gathering.
+ */
+ tmp = readl(pcie->base + PCIE_RC_PL_STATS_CTRL);
+ u32p_replace_bits(&tmp, 0, PCIE_RC_PL_STATS_CTRL_EN_MASK);
+ writel(tmp, pcie->base + PCIE_RC_PL_STATS_CTRL);
+
+ if (micros >= (1 << 28))
+ micros = (1 << 28) - 1U;
+ u32p_replace_bits(&tmp, micros, PCIE_RC_PL_STATS_CTRL_LEN_MASK);
+ u32p_replace_bits(&tmp, 1, PCIE_RC_PL_STATS_CTRL_EN_MASK);
+
+ writel(tmp, pcie->base + PCIE_RC_PL_STATS_CTRL);
+}
+
+static void brcm_pcie_stats_capture(struct brcm_pcie *pcie)
+{
+ u32 tmp;
+
+ /* Snapshot the counters - capture engine may still be running */
+ pcie->tx_tlp = (u64)readl(pcie->base + PCIE_RC_PL_STATS_TXTLP_LO) +
+ ((u64)readl(pcie->base + PCIE_RC_PL_STATS_TXTLP_HI) << 32ULL);
+ pcie->rx_tlp = (u64)readl(pcie->base + PCIE_RC_PL_STATS_RXTLP_LO) +
+ ((u64)readl(pcie->base + PCIE_RC_PL_STATS_RXTLP_HI) << 32ULL);
+ pcie->tx_dllp = (u64)readl(pcie->base + PCIE_RC_PL_STATS_TXDLLP_LO) +
+ ((u64)readl(pcie->base + PCIE_RC_PL_STATS_TXDLLP_HI) << 32ULL);
+ pcie->rx_dllp = (u64)readl(pcie->base + PCIE_RC_PL_STATS_RXDLLP_LO) +
+ ((u64)readl(pcie->base + PCIE_RC_PL_STATS_RXDLLP_HI) << 32ULL);
+
+ pcie->pl_rx_err = readl(pcie->base + PCIE_RC_PL_STATS_RXPL_ERR);
+ pcie->dl_rx_err = readl(pcie->base + PCIE_RC_PL_STATS_RXDL_ERR);
+ pcie->tl_rx_err = readl(pcie->base + PCIE_RC_PL_STATS_RXTL_ERR);
+
+ tmp = readl(pcie->base + PCIE_RC_PL_LTSSM_STATS_3);
+ pcie->l0s_exit_time = FIELD_GET(PCIE_RC_PL_LTSSM_STATS_3_TIME_L0S_MASK, tmp);
+ pcie->recov_time = FIELD_GET(PCIE_RC_PL_LTSSM_STATS_3_TIME_RECOV_MASK, tmp);
+
+ tmp = readl(pcie->base + PCIE_RC_PL_LTSSM_STATS_CNT);
+ pcie->l0s_fail_cnt = FIELD_GET(PCIE_RC_PL_LTSSM_STATS_CNT_L0S_FAIL_MASK, tmp);
+ pcie->recov_cnt = FIELD_GET(PCIE_RC_PL_LTSSM_STATS_CNT_RECOV_MASK, tmp);
+}
+
+/*
+ * Dump the link state machine transitions for the first 100ms after fundamental reset release.
+ * Most link training completes in a far shorter time.
+ *
+ * The CPU-intensive nature of the capture means that this should only be used to
+ * diagnose fatal link startup failures.
+ */
+static void brcm_pcie_trace_link_start(struct brcm_pcie *pcie)
+{
+ struct device *dev = pcie->dev;
+ struct trace_entry *trace = pcie->ltssm_trace_buf;
+ int i = 0, j = 0;
+ u8 cur_state;
+ u32 ltssm_hist0, ltssm_hist1 = 0;
+ ktime_t start, timeout;
+
+ start = ktime_get();
+ timeout = ktime_add(start, ktime_set(0, NSEC_PER_MSEC * 100));
+ /*
+ * The LTSSM history registers are implemented as an "open FIFO" where register data
+ * shuffles along with each push - or moves under one's feet, if you prefer.
+ * We can't atomically read more than one 32bit value (covering 4 entries), so poll
+ * quickly while guessing the position of the first value we haven't seen yet.
+ */
+ do {
+ /*
+ * This delay appears to work around a HW bug where data can temporarily
+ * appear nibble-shifted.
+ */
+ ndelay(10);
+ /* Snapshot the FIFO state. Lowest different "byte" is latest data. */
+ ltssm_hist0 = readl(pcie->base + PCIE_RC_PL_LTSSM_HIST(3));
+ if (ltssm_hist0 == ltssm_hist1)
+ continue;
+ ltssm_hist1 = ltssm_hist0;
+
+ /*
+ * If the "fifo" has changed, we don't know by how much.
+ * Scan through byte-wise and look for states
+ */
+ for (j = 24; j >= 0; j -= 8) {
+ cur_state = (ltssm_hist0 >> j) & 0xff;
+ /* Unassigned entry */
+ if (cur_state == 0xff)
+ continue;
+ if (i > 0 && trace[i-1].st == cur_state) {
+ /*
+ * This is probably what we last saw.
+ * Next byte should be a new entry.
+ */
+ j -= 8;
+ break;
+ } else if (i == 0 && brcm_pcie_decode_ltssm_state(cur_state)) {
+ /* Probably a new valid entry */
+ break;
+ }
+ }
+
+ for (; j >= 0 && i < TRACE_BUF_LEN; j -= 8) {
+ cur_state = (ltssm_hist0 >> j) & 0xff;
+ trace[i].st = cur_state;
+ trace[i].ts = ktime_sub(ktime_get(), start);
+ i++;
+ }
+ if (i == TRACE_BUF_LEN)
+ break;
+ } while (!ktime_after(ktime_get(), timeout));
+
+ dev_info(dev, "LTSSM trace captured %d events (max %u):\n",
+ i, TRACE_BUF_LEN);
+ for (i = 0; i < TRACE_BUF_LEN; i++) {
+ if (!trace[i].st)
+ break;
+ dev_info(dev, "%llu : %02x - %s\n",
+ ktime_to_us(trace[i].ts),
+ trace[i].st,
+ brcm_pcie_decode_ltssm_state(trace[i].st));
+ }
+}
+
static int brcm_pcie_start_link(struct brcm_pcie *pcie)
{
struct device *dev = pcie->dev;
void __iomem *base = pcie->base;
- u16 nlw, cls, lnksta;
+ u16 nlw, cls, lnksta, tmp16;
bool ssc_good = false;
- u32 tmp;
- u16 tmp16;
int ret, i;
+ u32 tmp;
/* Limit the generation if specified */
if (pcie->gen)
brcm_pcie_set_gen(pcie, pcie->gen);
+ brcm_pcie_stats_trigger(pcie, 0);
+
/* Unassert the fundamental reset */
if (pcie->tperst_clk_ms) {
/*
@@ -1776,6 +1786,8 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie)
if (ret)
return ret;
+ if (pcie->trace_ltssm)
+ brcm_pcie_trace_link_start(pcie);
/*
* Wait for 100ms after PERST# deassertion; see PCIe CEM specification
* sections 2.2, PCIe r5.0, 6.6.1.
@@ -1812,6 +1824,9 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie)
pci_speed_string(pcie_link_speed[cls]), nlw,
ssc_good ? "(SSC)" : "(!SSC)");
+ /* Snapshot the boot-time stats */
+ brcm_pcie_stats_capture(pcie);
+
/*
* RootCtl bits are reset by perst_n, which undoes pci_enable_crs()
* called prior to pci_add_new_bus() during probe. Re-enable here.
@@ -1822,6 +1837,7 @@ static int brcm_pcie_start_link(struct brcm_pcie *pcie)
u16p_replace_bits(&tmp16, 1, PCI_EXP_RTCTL_CRSSVE);
writew(tmp16, base + BRCM_PCIE_CAP_REGS + PCI_EXP_RTCTL);
}
+
return 0;
}
@@ -2142,6 +2158,7 @@ err_disable_clk:
static void __brcm_pcie_remove(struct brcm_pcie *pcie)
{
+ debugfs_remove_recursive(pcie->debugfs_dir);
brcm_msi_remove(pcie);
brcm_pcie_turn_off(pcie);
if (brcm_phy_stop(pcie))
@@ -2193,13 +2210,6 @@ static const int pcie_offsets_bcm7712[] = {
[PCIE_INTR2_CPU_BASE] = 0x4400,
};
-static const int pcie_offsets_bcm2712[] = {
- [EXT_CFG_INDEX] = 0x9000,
- [EXT_CFG_DATA] = 0x9004,
- [PCIE_HARD_DEBUG] = 0x4304,
- [PCIE_INTR2_CPU_BASE] = 0x4400,
-};
-
static const struct pcie_cfg_data generic_cfg = {
.offsets = pcie_offsets,
.soc_base = GENERIC,
@@ -2222,7 +2232,7 @@ static const struct pcie_cfg_data bcm2712_cfg = {
.perst_set = brcm_pcie_perst_set_7278,
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
.post_setup = brcm_pcie_post_setup_bcm2712,
- .quirks = CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN,
+ .quirks = CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN | CFG_QUIRK_NO_SSC,
.num_inbound_wins = 10,
};
@@ -2275,13 +2285,6 @@ static const struct pcie_cfg_data bcm7712_cfg = {
.num_inbound_wins = 10,
};
-static const struct pcie_cfg_data bcm2712_cfg = {
- .offsets = pcie_offsets_bcm2712,
- .perst_set = brcm_pcie_perst_set_2712,
- .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_2712,
- .soc_base = BCM2712,
-};
-
static const struct of_device_id brcm_pcie_match[] = {
{ .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg },
{ .compatible = "brcm,bcm2712-pcie", .data = &bcm2712_cfg },
@@ -2293,7 +2296,6 @@ static const struct of_device_id brcm_pcie_match[] = {
{ .compatible = "brcm,bcm7435-pcie", .data = &bcm7435_cfg },
{ .compatible = "brcm,bcm7445-pcie", .data = &generic_cfg },
{ .compatible = "brcm,bcm7712-pcie", .data = &bcm7712_cfg },
- { .compatible = "brcm,bcm2712-pcie", .data = &bcm2712_cfg },
{},
};
@@ -2313,6 +2315,98 @@ static struct pci_ops brcm7425_pcie_ops = {
.remove_bus = brcm_pcie_remove_bus,
};
+static ssize_t debugfs_stats_trigger_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *m = filp->private_data;
+ struct brcm_pcie *pcie = m->private;
+ char kbuf[16] = {};
+ unsigned long micros;
+
+ if (count > sizeof(kbuf))
+ return -EOVERFLOW;
+
+ if (copy_from_user(kbuf, buf, count))
+ return -EINVAL;
+
+ if (kstrtol(kbuf, 0, &micros) < 0)
+ return -EINVAL;
+
+ if (micros >= (1 << 28))
+ return -ERANGE;
+
+ brcm_pcie_stats_trigger(pcie, micros);
+ return count;
+}
+
+static int debugfs_stats_trigger_show(struct seq_file *s, void *unused)
+{
+ struct brcm_pcie *pcie = s->private;
+ u32 tmp;
+
+ /* Return the state of the capture engine */
+ tmp = readl(pcie->base + PCIE_RC_PL_STATS_CTRL);
+ tmp = FIELD_GET(PCIE_RC_PL_STATS_CTRL_EN_MASK, tmp);
+ seq_printf(s, "%u\n", tmp);
+ return 0;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(debugfs_stats_trigger);
+
+static ssize_t debugfs_stats_snapshot_write(struct file *filp,
+ const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct seq_file *m = filp->private_data;
+ struct brcm_pcie *pcie = m->private;
+
+ /* Any write triggers a snapshot of the stats register set */
+ brcm_pcie_stats_capture(pcie);
+ return count;
+}
+
+static int debugfs_stats_snapshot_show(struct seq_file *s, void *unused)
+{
+ struct brcm_pcie *pcie = s->private;
+
+ seq_printf(s, "tx_tlp:\t\t%llu\n", pcie->tx_tlp);
+ seq_printf(s, "rx_tlp:\t\t%llu\n", pcie->rx_tlp);
+ seq_printf(s, "tx_dllp:\t%llu\n", pcie->tx_dllp);
+ seq_printf(s, "rx_dllp:\t%llu\n", pcie->rx_dllp);
+ seq_printf(s, "pl_rx_err:\t%u\n", pcie->pl_rx_err);
+ seq_printf(s, "dl_rx_err:\t%u\n", pcie->dl_rx_err);
+ seq_printf(s, "tl_rx_err:\t%u\n", pcie->tl_rx_err);
+ seq_printf(s, "l0s_exit_time:\t%u\n", pcie->l0s_exit_time);
+ seq_printf(s, "recov_time:\t%u\n", pcie->recov_time);
+ seq_printf(s, "l0s_fail_cnt\t%u\n", pcie->l0s_fail_cnt);
+ seq_printf(s, "recov_cnt:\t%u\n", pcie->recov_cnt);
+
+ return 0;
+}
+DEFINE_SHOW_STORE_ATTRIBUTE(debugfs_stats_snapshot);
+
+static void brcm_pcie_init_debugfs(struct brcm_pcie *pcie)
+{
+ char *name;
+
+ name = devm_kasprintf(pcie->dev, GFP_KERNEL, "%pOFP", pcie->dev->of_node);
+ if (!name)
+ return;
+
+ pcie->debugfs_dir = debugfs_create_dir(name, NULL);
+ if (!pcie->debugfs_dir)
+ return;
+
+ debugfs_create_file("stats_snapshot", 0644, pcie->debugfs_dir, pcie,
+ &debugfs_stats_snapshot_fops);
+ debugfs_create_file("stats_trigger", 0644, pcie->debugfs_dir, pcie,
+ &debugfs_stats_trigger_fops);
+}
+
+static bool trace_ltssm;
+module_param(trace_ltssm, bool, 0444);
+MODULE_PARM_DESC(trace_ltssm, "Capture and dump link states during link training");
+
static int brcm_pcie_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -2335,6 +2429,19 @@ static int brcm_pcie_probe(struct platform_device *pdev)
pcie->dev = &pdev->dev;
pcie->np = np;
pcie->cfg = data;
+ pcie->trace_ltssm = trace_ltssm;
+
+ brcm_pcie_init_debugfs(pcie);
+
+ if (pcie->trace_ltssm) {
+ pcie->ltssm_trace_buf = devm_kzalloc(&pdev->dev,
+ sizeof(struct trace_entry) * TRACE_BUF_LEN,
+ GFP_KERNEL);
+ if (!pcie->ltssm_trace_buf) {
+ dev_err(&pdev->dev, "could not allocate trace buffer\n");
+ return -ENOMEM;
+ }
+ }
pcie->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(pcie->base))
@@ -2347,9 +2454,9 @@ static int brcm_pcie_probe(struct platform_device *pdev)
ret = of_pci_get_max_link_speed(np);
pcie->gen = (ret < 0) ? 0 : ret;
- pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
- pcie->l1ss = of_property_read_bool(np, "brcm,enable-l1ss");
- pcie->rcb_mps_mode = of_property_read_bool(np, "brcm,enable-mps-rcb");
+ pcie->ssc = !(pcie->cfg->quirks & CFG_QUIRK_NO_SSC) &&
+ of_property_read_bool(np, "brcm,enable-ssc");
+
of_property_read_u32(np, "brcm,tperst-clk-ms", &pcie->tperst_clk_ms);
pcie->rescal = devm_reset_control_get_optional_shared(&pdev->dev, "rescal");
@@ -2430,33 +2537,6 @@ static int brcm_pcie_probe(struct platform_device *pdev)
dev_err(pcie->dev, "probe of internal MSI failed");
goto fail;
}
- } else if (pci_msi_enabled() && msi_np != pcie->np) {
- /* Use RC_BAR1 for MIP access */
- u64 msi_pci_addr;
- u64 msi_phys_addr;
-
- if (of_property_read_u64(msi_np, "brcm,msi-pci-addr", &msi_pci_addr)) {
- dev_err(pcie->dev, "Unable to find MSI PCI address\n");
- ret = -EINVAL;
- goto fail;
- }
-
- if (of_property_read_u64(msi_np, "reg", &msi_phys_addr)) {
- dev_err(pcie->dev, "Unable to find MSI physical address\n");
- ret = -EINVAL;
- goto fail;
- }
-
- writel(lower_32_bits(msi_pci_addr) | brcm_pcie_encode_ibar_size(0x1000),
- pcie->base + PCIE_MISC_RC_BAR1_CONFIG_LO);
- writel(upper_32_bits(msi_pci_addr),
- pcie->base + PCIE_MISC_RC_BAR1_CONFIG_HI);
-
- writel(lower_32_bits(msi_phys_addr) |
- PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK,
- pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP);
- writel(upper_32_bits(msi_phys_addr),
- pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI);
}
bridge->ops = pcie->cfg->soc_base == BCM7425 ?