diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c index 7b427f14fbab..894bc63ca413 100644 --- a/drivers/pci/controller/pci-msm.c +++ b/drivers/pci/controller/pci-msm.c @@ -96,6 +96,17 @@ #define PCIE20_PARF_INT_ALL_2_MASK (0X508) #define MSM_PCIE_BW_MGT_INT_STATUS (BIT(25)) +#define PCIE20_PARF_DEBUG_CNT_IN_L0S (0xc10) +#define PCIE20_PARF_DEBUG_CNT_IN_L1 (0xc0c) +#define PCIE20_PARF_DEBUG_CNT_IN_L1SUB_L1 (0xc84) +#define PCIE20_PARF_DEBUG_CNT_IN_L1SUB_L2 (0xc88) +#define PCIE20_PARF_PM_STTS_1 (0x28) +#define PCIE20_PARF_L1SS_SLEEP_MODE_HANDLER_STATUS (0x4D0) +#define PCIE20_PARF_L1SS_SLEEP_MODE_HANDLER_CFG (0x4D4) +#define PCIE20_PARF_CORE_ERRORS (0x3C0) +#define PCIE20_LINK_DOWN_AXI_ECAM_BLOCK_STATUS (0x630) +#define PCIE20_PARF_STATUS (0x230) + #define PCIE20_PARF_CLKREQ_OVERRIDE (0x2b0) #define PCIE20_PARF_CLKREQ_IN_VALUE (BIT(3)) #define PCIE20_PARF_CLKREQ_IN_ENABLE (BIT(1)) @@ -156,6 +167,12 @@ #define PCIE20_AER_ROOT_ERR_STATUS_REG (0x130) #define PCIE20_AER_ERR_SRC_ID_REG (0x134) +#define PCIE20_L1SUB_CONTROL1_REG (0x204) +#define PCIE20_TX_P_FC_CREDIT_STATUS_OFF (0x730) +#define PCIE20_TX_NP_FC_CREDIT_STATUS_OFF (0x734) +#define PCIE20_TX_CPL_FC_CREDIT_STATUS_OFF (0x738) +#define PCIE20_QUEUE_STATUS_OFF (0x73C) + #define RD (0) #define WR (1) #define MSM_PCIE_ERROR (-1) @@ -1025,6 +1042,15 @@ struct msm_pcie_dev_t { bool panic_on_aer; struct aer_stats *aer_stats; void (*rumi_init)(struct msm_pcie_dev_t *pcie_dev); + + u32 phy_debug_reg_len; + u32 *phy_debug_reg; + + u32 parf_debug_reg_len; + u32 *parf_debug_reg; + + u32 dbi_debug_reg_len; + u32 *dbi_debug_reg; }; struct msm_root_dev_t { @@ -1179,6 +1205,12 @@ static const struct msm_pcie_irq_info_t msm_pcie_irq_info[MSM_PCIE_MAX_IRQ] = { {"int_global_int", 0} }; +enum msm_pcie_reg_dump_type_t { + MSM_PCIE_DUMP_PARF_REG = 0x0, + MSM_PCIE_DUMP_DBI_REG, + MSM_PCIE_DUMP_PHY_REG, +}; + static int msm_pcie_drv_send_rpmsg(struct msm_pcie_dev_t *pcie_dev, struct msm_pcie_drv_msg *msg); static void msm_pcie_config_sid(struct msm_pcie_dev_t *dev); @@ -1198,6 +1230,115 @@ static void msm_pcie_config_link_pm(struct msm_pcie_dev_t *dev, bool enable); static int msm_pcie_set_link_width(struct msm_pcie_dev_t *pcie_dev, u16 target_link_width); +static u32 msm_pcie_reg_copy(struct msm_pcie_dev_t *dev, + u8 *buf, u32 size, u8 reg_len, + enum msm_pcie_reg_dump_type_t type) +{ + u32 ret = 0, val, i; + u32 *seq = NULL; + u32 seq_len = 0; + void __iomem *base; + + PCIE_DUMP(dev, "RC%d buf=0x%x size=%u, reg_len=%u\n", + dev->rc_idx, buf, size, reg_len); + if (type == MSM_PCIE_DUMP_PARF_REG) { + seq = dev->parf_debug_reg; + seq_len = dev->parf_debug_reg_len; + base = dev->parf; + } else if (type == MSM_PCIE_DUMP_DBI_REG) { + seq = dev->dbi_debug_reg; + seq_len = dev->dbi_debug_reg_len; + base = dev->dm_core; + } else if (type == MSM_PCIE_DUMP_PHY_REG) { + seq = dev->phy_debug_reg; + seq_len = dev->phy_debug_reg_len; + base = dev->phy; + } else { + return ret; + } + + if (seq) { + i = seq_len; + while (i && (ret + reg_len <= size)) { + PCIE_DUMP(dev, "RC%d *seq:%u\n", + dev->rc_idx, *seq); + val = readl_relaxed(base + *seq); + memcpy(buf, &val, reg_len); + i--; + buf += reg_len; + ret += reg_len; + seq++; + } + } + return ret; +} + +int msm_pcie_reg_dump(struct pci_dev *pci_dev, u8 *buff, u32 len) +{ + struct pci_dev *root_pci_dev; + struct msm_pcie_dev_t *pcie_dev; + u32 offset = 0; + + if (!pci_dev) + return -EINVAL; + + root_pci_dev = pcie_find_root_port(pci_dev); + if (!root_pci_dev) + return -ENODEV; + + pcie_dev = PCIE_BUS_PRIV_DATA(root_pci_dev->bus); + + if (!pcie_dev) { + pr_err("PCIe: did not find RC for pci endpoint device.\n"); + return -ENODEV; + } + + PCIE_DUMP(pcie_dev, "RC%d hang event dump buff=0x%x len=%u\n", + pcie_dev->rc_idx, buff, len); + + if (pcie_dev->link_status == MSM_PCIE_LINK_DOWN) { + pr_err("PCIe: the link is in down state\n"); + return -ENODEV; + } + + if (pcie_dev->suspending) { + pr_err("PCIe: the device is in suspend state\n"); + return -ENODEV; + } + + offset = msm_pcie_reg_copy(pcie_dev, buff, len, + 4, MSM_PCIE_DUMP_PARF_REG); + + buff += offset; + len -= offset; + + /* check PHY status before dumping DBI registers */ + if (!(readl_relaxed(pcie_dev->phy + pcie_dev->phy_status_offset) & + BIT(pcie_dev->phy_status_bit))) { + + PCIE_DUMP(pcie_dev, "RC%d Dump DBI registers\n", + pcie_dev->rc_idx); + offset = msm_pcie_reg_copy(pcie_dev, buff, len, + 4, MSM_PCIE_DUMP_DBI_REG); + } else { + /* PHY status bit is set to 1 so dump 0's in dbi buffer space */ + PCIE_DUMP(pcie_dev, "RC%d PHY is off, skip DBI\n", + pcie_dev->rc_idx); + memset(buff, 0, pcie_dev->dbi_debug_reg_len * 4); + offset = pcie_dev->dbi_debug_reg_len * 4; + } + + buff += offset; + len -= offset; + msm_pcie_reg_copy(pcie_dev, buff, len, + 1, MSM_PCIE_DUMP_PHY_REG); + + PCIE_DUMP(pcie_dev, "RC%d hang event Exit\n", pcie_dev->rc_idx); + + return 0; +} +EXPORT_SYMBOL(msm_pcie_reg_dump); + static void msm_pcie_write_reg(void __iomem *base, u32 offset, u32 value) { writel_relaxed(value, base + offset); @@ -3975,6 +4116,123 @@ static int msm_pcie_get_phy(struct msm_pcie_dev_t *pcie_dev) return 0; } +static int msm_pcie_get_phy_status_reg(struct msm_pcie_dev_t *pcie_dev) +{ + int ret, size = 0; + struct platform_device *pdev = pcie_dev->pdev; + + PCIE_DBG(pcie_dev, + "PCIe: RC%d: Enter\n", + pcie_dev->rc_idx); + of_get_property(pdev->dev.of_node, "qcom,phy-debug-reg", &size); + if (!size) { + PCIE_DBG(pcie_dev, + "PCIe: RC%d: phy debug registers not present in DT\n", + pcie_dev->rc_idx); + pcie_dev->phy_debug_reg = NULL; + return 0; + } + + pcie_dev->phy_debug_reg = kmalloc(size, GFP_KERNEL); + if (!pcie_dev->phy_debug_reg) + return -ENOMEM; + + pcie_dev->phy_debug_reg_len = size / sizeof(*pcie_dev->phy_debug_reg); + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,phy-debug-reg", + (unsigned int *)pcie_dev->phy_debug_reg, + size / sizeof(*pcie_dev->phy_debug_reg)); + if (ret) { + kfree(pcie_dev->phy_debug_reg); + pcie_dev->phy_debug_reg = NULL; + return -EINVAL; + } + + PCIE_DBG(pcie_dev, + "PCIe: RC%d: no of phy dbg regs:%u size:%u\n", + pcie_dev->rc_idx, size/sizeof(u32), size); + return 0; +} + +static int msm_pcie_get_parf_status_reg(struct msm_pcie_dev_t *pcie_dev) +{ + int ret, size = 0; + struct platform_device *pdev = pcie_dev->pdev; + + PCIE_DBG(pcie_dev, + "PCIe: RC%d: Enter\n", pcie_dev->rc_idx); + + of_get_property(pdev->dev.of_node, "qcom,parf-debug-reg", &size); + if (!size) { + PCIE_DBG(pcie_dev, + "PCIe: RC%d: parf debug registers not present in DT\n", + pcie_dev->rc_idx); + pcie_dev->parf_debug_reg = NULL; + return 0; + } + + pcie_dev->parf_debug_reg = kmalloc(size, GFP_KERNEL); + if (!pcie_dev->parf_debug_reg) + return -ENOMEM; + + pcie_dev->parf_debug_reg_len = size / sizeof(u32); + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,parf-debug-reg", + (unsigned int *)pcie_dev->parf_debug_reg, + size / sizeof(u32)); + if (ret) { + kfree(pcie_dev->parf_debug_reg); + pcie_dev->parf_debug_reg = NULL; + return -EINVAL; + } + + PCIE_DBG(pcie_dev, + "PCIe: RC%d: no of parf dbg regs:%u size:%u\n", + pcie_dev->rc_idx, size/sizeof(u32), size); + return 0; +} + +static int msm_pcie_get_dbi_status_reg(struct msm_pcie_dev_t *pcie_dev) +{ + int ret, size = 0; + struct platform_device *pdev = pcie_dev->pdev; + + PCIE_DBG(pcie_dev, + "PCIe: RC%d: Enter\n", + pcie_dev->rc_idx); + of_get_property(pdev->dev.of_node, "qcom,dbi-debug-reg", &size); + if (!size) { + PCIE_DBG(pcie_dev, + "PCIe: RC%d: dbi debug registers not present in DT\n", + pcie_dev->rc_idx); + pcie_dev->dbi_debug_reg = NULL; + return 0; + } + + pcie_dev->dbi_debug_reg = kmalloc(size, GFP_KERNEL); + if (!pcie_dev->dbi_debug_reg) + return -ENOMEM; + + pcie_dev->dbi_debug_reg_len = size / sizeof(u32); + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,dbi-debug-reg", + (unsigned int *)pcie_dev->dbi_debug_reg, + size / sizeof(u32)); + if (ret) { + kfree(pcie_dev->dbi_debug_reg); + pcie_dev->dbi_debug_reg = NULL; + return -EINVAL; + } + + PCIE_DBG(pcie_dev, + "PCIe: RC%d: no of dbi dbg regs:%u size:%u\n", + pcie_dev->rc_idx, size/sizeof(u32), size); + return 0; +} + static int msm_pcie_get_iommu_map(struct msm_pcie_dev_t *pcie_dev) { /* iommu map structure */ @@ -4201,6 +4459,18 @@ static int msm_pcie_get_resources(struct msm_pcie_dev_t *dev, if (ret) return ret; + ret = msm_pcie_get_parf_status_reg(dev); + if (ret) + return ret; + + ret = msm_pcie_get_dbi_status_reg(dev); + if (ret) + return ret; + + ret = msm_pcie_get_phy_status_reg(dev); + if (ret) + return ret; + PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx); return 0; @@ -4208,6 +4478,7 @@ static int msm_pcie_get_resources(struct msm_pcie_dev_t *dev, static void msm_pcie_release_resources(struct msm_pcie_dev_t *dev) { + dev->parf = NULL; dev->elbi = NULL; dev->iatu = NULL; @@ -4215,6 +4486,13 @@ static void msm_pcie_release_resources(struct msm_pcie_dev_t *dev) dev->conf = NULL; dev->tcsr = NULL; dev->rumi = NULL; + kfree(dev->parf_debug_reg); + kfree(dev->dbi_debug_reg); + kfree(dev->phy_debug_reg); + dev->parf_debug_reg = NULL; + dev->dbi_debug_reg = NULL; + dev->phy_debug_reg = NULL; + } static void msm_pcie_scale_link_bandwidth(struct msm_pcie_dev_t *pcie_dev, diff --git a/include/linux/msm_pcie.h b/include/linux/msm_pcie.h index 0eb8195611f0..ce7dc44e870b 100644 --- a/include/linux/msm_pcie.h +++ b/include/linux/msm_pcie.h @@ -204,6 +204,17 @@ int msm_pcie_enumerate(u32 rc_idx); int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base, u32 offset, u32 mask, u32 value); +/* + * msm_pcie_reg_dump - dump pcie regsters for debug + * @pci_dev: pci device structure + * @buffer: destination buffer address + * @len: length of buffer + * + * This functions dumps PCIE registers for debug. Sould be used when + * link is already enabled + */ +int msm_pcie_reg_dump(struct pci_dev *pci_dev, u8 *buff, u32 len); + #else /* !CONFIG_PCI_MSM */ static inline int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user, void *data, u32 options) @@ -256,6 +267,11 @@ static inline int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base, { return -ENODEV; } + +static inline int msm_pcie_reg_dump(struct pci_dev *pci_dev, u8 *buff, u32 len) +{ + return -ENODEV; +} #endif /* CONFIG_PCI_MSM */ #endif /* __MSM_PCIE_H */