PCI: qcom: Expose link transition counts via debugfs
authorManivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Thu, 16 Mar 2023 08:11:17 +0000 (13:41 +0530)
committerLorenzo Pieralisi <lpieralisi@kernel.org>
Tue, 11 Apr 2023 09:31:11 +0000 (11:31 +0200)
Qualcomm PCIe controllers have debug registers in the MHI region that
count PCIe link transitions. Expose them over debugfs to userspace to
help debug the low power issues.

Note that even though the registers are prefixed as PARF_, they don't
live under the "parf" register region. The register naming is following
the Qualcomm's internal documentation as like other registers.

While at it, let's arrange the local variables in probe function to follow
reverse XMAS tree order.

Link: https://lore.kernel.org/r/20230316081117.14288-20-manivannan.sadhasivam@linaro.org
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
drivers/pci/controller/dwc/pcie-qcom.c

index 7daff0421b86ba65b0dbb5f9e713dd4838f72ef2..f3e606b038c31052674f103f95829dea51815b4b 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <linux/clk.h>
 #include <linux/crc8.h>
+#include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
 #include <linux/interconnect.h>
 #define AXI_MSTR_RESP_COMP_CTRL1               0x81c
 #define MISC_CONTROL_1_REG                     0x8bc
 
+/* MHI registers */
+#define PARF_DEBUG_CNT_PM_LINKST_IN_L2         0xc04
+#define PARF_DEBUG_CNT_PM_LINKST_IN_L1         0xc0c
+#define PARF_DEBUG_CNT_PM_LINKST_IN_L0S                0xc10
+#define PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L1     0xc84
+#define PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L2     0xc88
+
 /* PARF_SYS_CTRL register fields */
 #define MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN       BIT(29)
 #define MST_WAKEUP_EN                          BIT(13)
@@ -229,11 +237,13 @@ struct qcom_pcie {
        struct dw_pcie *pci;
        void __iomem *parf;                     /* DT parf */
        void __iomem *elbi;                     /* DT elbi */
+       void __iomem *mhi;
        union qcom_pcie_resources res;
        struct phy *phy;
        struct gpio_desc *reset;
        struct icc_path *icc_mem;
        const struct qcom_pcie_cfg *cfg;
+       struct dentry *debugfs;
 };
 
 #define to_qcom_pcie(x)                dev_get_drvdata((x)->dev)
@@ -1382,13 +1392,51 @@ static void qcom_pcie_icc_update(struct qcom_pcie *pcie)
        }
 }
 
+static int qcom_pcie_link_transition_count(struct seq_file *s, void *data)
+{
+       struct qcom_pcie *pcie = (struct qcom_pcie *)dev_get_drvdata(s->private);
+
+       seq_printf(s, "L0s transition count: %u\n",
+                  readl_relaxed(pcie->mhi + PARF_DEBUG_CNT_PM_LINKST_IN_L0S));
+
+       seq_printf(s, "L1 transition count: %u\n",
+                  readl_relaxed(pcie->mhi + PARF_DEBUG_CNT_PM_LINKST_IN_L1));
+
+       seq_printf(s, "L1.1 transition count: %u\n",
+                  readl_relaxed(pcie->mhi + PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L1));
+
+       seq_printf(s, "L1.2 transition count: %u\n",
+                  readl_relaxed(pcie->mhi + PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L2));
+
+       seq_printf(s, "L2 transition count: %u\n",
+                  readl_relaxed(pcie->mhi + PARF_DEBUG_CNT_PM_LINKST_IN_L2));
+
+       return 0;
+}
+
+static void qcom_pcie_init_debugfs(struct qcom_pcie *pcie)
+{
+       struct dw_pcie *pci = pcie->pci;
+       struct device *dev = pci->dev;
+       char *name;
+
+       name = devm_kasprintf(dev, GFP_KERNEL, "%pOFP", dev->of_node);
+       if (!name)
+               return;
+
+       pcie->debugfs = debugfs_create_dir(name, NULL);
+       debugfs_create_devm_seqfile(dev, "link_transition_count", pcie->debugfs,
+                                   qcom_pcie_link_transition_count);
+}
+
 static int qcom_pcie_probe(struct platform_device *pdev)
 {
+       const struct qcom_pcie_cfg *pcie_cfg;
        struct device *dev = &pdev->dev;
+       struct qcom_pcie *pcie;
        struct dw_pcie_rp *pp;
+       struct resource *res;
        struct dw_pcie *pci;
-       struct qcom_pcie *pcie;
-       const struct qcom_pcie_cfg *pcie_cfg;
        int ret;
 
        pcie_cfg = of_device_get_match_data(dev);
@@ -1436,6 +1484,16 @@ static int qcom_pcie_probe(struct platform_device *pdev)
                goto err_pm_runtime_put;
        }
 
+       /* MHI region is optional */
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mhi");
+       if (res) {
+               pcie->mhi = devm_ioremap_resource(dev, res);
+               if (IS_ERR(pcie->mhi)) {
+                       ret = PTR_ERR(pcie->mhi);
+                       goto err_pm_runtime_put;
+               }
+       }
+
        pcie->phy = devm_phy_optional_get(dev, "pciephy");
        if (IS_ERR(pcie->phy)) {
                ret = PTR_ERR(pcie->phy);
@@ -1466,6 +1524,9 @@ static int qcom_pcie_probe(struct platform_device *pdev)
 
        qcom_pcie_icc_update(pcie);
 
+       if (pcie->mhi)
+               qcom_pcie_init_debugfs(pcie);
+
        return 0;
 
 err_phy_exit: