diff options
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-qcom-ep.c')
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom-ep.c | 164 | 
1 files changed, 131 insertions, 33 deletions
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index ec99116ad05c..6d0d1b759ca2 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -10,6 +10,7 @@   */  #include <linux/clk.h> +#include <linux/debugfs.h>  #include <linux/delay.h>  #include <linux/gpio/consumer.h>  #include <linux/mfd/syscon.h> @@ -26,6 +27,7 @@  #define PARF_SYS_CTRL				0x00  #define PARF_DB_CTRL				0x10  #define PARF_PM_CTRL				0x20 +#define PARF_MHI_CLOCK_RESET_CTRL		0x174  #define PARF_MHI_BASE_ADDR_LOWER		0x178  #define PARF_MHI_BASE_ADDR_UPPER		0x17c  #define PARF_DEBUG_INT_EN			0x190 @@ -45,6 +47,11 @@  #define PARF_ATU_BASE_ADDR			0x634  #define PARF_ATU_BASE_ADDR_HI			0x638  #define PARF_SRIS_MODE				0x644 +#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  #define PARF_DEVICE_TYPE			0x1000  #define PARF_BDF_TO_SID_CFG			0x2c00 @@ -83,6 +90,9 @@  #define PARF_PM_CTRL_READY_ENTR_L23		BIT(2)  #define PARF_PM_CTRL_REQ_NOT_ENTR_L1		BIT(5) +/* PARF_MHI_CLOCK_RESET_CTRL fields */ +#define PARF_MSTR_AXI_CLK_EN			BIT(1) +  /* PARF_AXI_MSTR_RD_HALT_NO_WRITES register fields */  #define PARF_AXI_MSTR_RD_HALT_NO_WRITE_EN	BIT(0) @@ -95,6 +105,7 @@  /* PARF_SYS_CTRL register fields */  #define PARF_SYS_CTRL_AUX_PWR_DET		BIT(4)  #define PARF_SYS_CTRL_CORE_CLK_CGC_DIS		BIT(6) +#define PARF_SYS_CTRL_MSTR_ACLK_CGC_DIS		BIT(10)  #define PARF_SYS_CTRL_SLV_DBI_WAKE_DISABLE	BIT(11)  /* PARF_DB_CTRL register fields */ @@ -130,21 +141,33 @@ enum qcom_pcie_ep_link_status {  	QCOM_PCIE_EP_LINK_DOWN,  }; -static struct clk_bulk_data qcom_pcie_ep_clks[] = { -	{ .id = "cfg" }, -	{ .id = "aux" }, -	{ .id = "bus_master" }, -	{ .id = "bus_slave" }, -	{ .id = "ref" }, -	{ .id = "sleep" }, -	{ .id = "slave_q2a" }, -}; - +/** + * struct qcom_pcie_ep - Qualcomm PCIe Endpoint Controller + * @pci: Designware PCIe controller struct + * @parf: Qualcomm PCIe specific PARF register base + * @elbi: Designware PCIe specific ELBI register base + * @mmio: MMIO register base + * @perst_map: PERST regmap + * @mmio_res: MMIO region resource + * @core_reset: PCIe Endpoint core reset + * @reset: PERST# GPIO + * @wake: WAKE# GPIO + * @phy: PHY controller block + * @debugfs: PCIe Endpoint Debugfs directory + * @clks: PCIe clocks + * @num_clks: PCIe clocks count + * @perst_en: Flag for PERST enable + * @perst_sep_en: Flag for PERST separation enable + * @link_status: PCIe Link status + * @global_irq: Qualcomm PCIe specific Global IRQ + * @perst_irq: PERST# IRQ + */  struct qcom_pcie_ep {  	struct dw_pcie pci;  	void __iomem *parf;  	void __iomem *elbi; +	void __iomem *mmio;  	struct regmap *perst_map;  	struct resource *mmio_res; @@ -152,6 +175,10 @@ struct qcom_pcie_ep {  	struct gpio_desc *reset;  	struct gpio_desc *wake;  	struct phy *phy; +	struct dentry *debugfs; + +	struct clk_bulk_data *clks; +	int num_clks;  	u32 perst_en;  	u32 perst_sep_en; @@ -193,8 +220,10 @@ static int qcom_pcie_ep_core_reset(struct qcom_pcie_ep *pcie_ep)   */  static void qcom_pcie_ep_configure_tcsr(struct qcom_pcie_ep *pcie_ep)  { -	regmap_write(pcie_ep->perst_map, pcie_ep->perst_en, 0); -	regmap_write(pcie_ep->perst_map, pcie_ep->perst_sep_en, 0); +	if (pcie_ep->perst_map) { +		regmap_write(pcie_ep->perst_map, pcie_ep->perst_en, 0); +		regmap_write(pcie_ep->perst_map, pcie_ep->perst_sep_en, 0); +	}  }  static int qcom_pcie_dw_link_up(struct dw_pcie *pci) @@ -227,8 +256,7 @@ static int qcom_pcie_enable_resources(struct qcom_pcie_ep *pcie_ep)  {  	int ret; -	ret = clk_bulk_prepare_enable(ARRAY_SIZE(qcom_pcie_ep_clks), -				      qcom_pcie_ep_clks); +	ret = clk_bulk_prepare_enable(pcie_ep->num_clks, pcie_ep->clks);  	if (ret)  		return ret; @@ -249,8 +277,7 @@ static int qcom_pcie_enable_resources(struct qcom_pcie_ep *pcie_ep)  err_phy_exit:  	phy_exit(pcie_ep->phy);  err_disable_clk: -	clk_bulk_disable_unprepare(ARRAY_SIZE(qcom_pcie_ep_clks), -				   qcom_pcie_ep_clks); +	clk_bulk_disable_unprepare(pcie_ep->num_clks, pcie_ep->clks);  	return ret;  } @@ -259,8 +286,7 @@ static void qcom_pcie_disable_resources(struct qcom_pcie_ep *pcie_ep)  {  	phy_power_off(pcie_ep->phy);  	phy_exit(pcie_ep->phy); -	clk_bulk_disable_unprepare(ARRAY_SIZE(qcom_pcie_ep_clks), -				   qcom_pcie_ep_clks); +	clk_bulk_disable_unprepare(pcie_ep->num_clks, pcie_ep->clks);  }  static int qcom_pcie_perst_deassert(struct dw_pcie *pci) @@ -318,8 +344,14 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)  	val &= ~PARF_Q2A_FLUSH_EN;  	writel_relaxed(val, pcie_ep->parf + PARF_Q2A_FLUSH); -	/* Disable DBI Wakeup, core clock CGC and enable AUX power */ +	/* +	 * Disable Master AXI clock during idle.  Do not allow DBI access +	 * to take the core out of L1.  Disable core clock gating that +	 * gates PIPE clock from propagating to core clock.  Report to the +	 * host that Vaux is present. +	 */  	val = readl_relaxed(pcie_ep->parf + PARF_SYS_CTRL); +	val &= ~PARF_SYS_CTRL_MSTR_ACLK_CGC_DIS;  	val |= PARF_SYS_CTRL_SLV_DBI_WAKE_DISABLE |  	       PARF_SYS_CTRL_CORE_CLK_CGC_DIS |  	       PARF_SYS_CTRL_AUX_PWR_DET; @@ -375,6 +407,11 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)  		       pcie_ep->parf + PARF_MHI_BASE_ADDR_LOWER);  	writel_relaxed(0, pcie_ep->parf + PARF_MHI_BASE_ADDR_UPPER); +	/* Gate Master AXI clock to MHI bus during L1SS */ +	val = readl_relaxed(pcie_ep->parf + PARF_MHI_CLOCK_RESET_CTRL); +	val &= ~PARF_MSTR_AXI_CLK_EN; +	val = readl_relaxed(pcie_ep->parf + PARF_MHI_CLOCK_RESET_CTRL); +  	dw_pcie_ep_init_notify(&pcie_ep->pci.ep);  	/* Enable LTSSM */ @@ -437,11 +474,19 @@ static int qcom_pcie_ep_get_io_resources(struct platform_device *pdev,  	pcie_ep->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,  							 "mmio"); +	if (!pcie_ep->mmio_res) { +		dev_err(dev, "Failed to get mmio resource\n"); +		return -EINVAL; +	} + +	pcie_ep->mmio = devm_pci_remap_cfg_resource(dev, pcie_ep->mmio_res); +	if (IS_ERR(pcie_ep->mmio)) +		return PTR_ERR(pcie_ep->mmio);  	syscon = of_parse_phandle(dev->of_node, "qcom,perst-regs", 0);  	if (!syscon) { -		dev_err(dev, "Failed to parse qcom,perst-regs\n"); -		return -EINVAL; +		dev_dbg(dev, "PERST separation not available\n"); +		return 0;  	}  	pcie_ep->perst_map = syscon_node_to_regmap(syscon); @@ -474,14 +519,15 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev,  	ret = qcom_pcie_ep_get_io_resources(pdev, pcie_ep);  	if (ret) { -		dev_err(&pdev->dev, "Failed to get io resources %d\n", ret); +		dev_err(dev, "Failed to get io resources %d\n", ret);  		return ret;  	} -	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(qcom_pcie_ep_clks), -				qcom_pcie_ep_clks); -	if (ret) -		return ret; +	pcie_ep->num_clks = devm_clk_bulk_get_all(dev, &pcie_ep->clks); +	if (pcie_ep->num_clks < 0) { +		dev_err(dev, "Failed to get clocks\n"); +		return pcie_ep->num_clks; +	}  	pcie_ep->core_reset = devm_reset_control_get_exclusive(dev, "core");  	if (IS_ERR(pcie_ep->core_reset)) @@ -495,7 +541,7 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev,  	if (IS_ERR(pcie_ep->wake))  		return PTR_ERR(pcie_ep->wake); -	pcie_ep->phy = devm_phy_optional_get(&pdev->dev, "pciephy"); +	pcie_ep->phy = devm_phy_optional_get(dev, "pciephy");  	if (IS_ERR(pcie_ep->phy))  		ret = PTR_ERR(pcie_ep->phy); @@ -571,13 +617,13 @@ static irqreturn_t qcom_pcie_ep_perst_irq_thread(int irq, void *data)  static int qcom_pcie_ep_enable_irq_resources(struct platform_device *pdev,  					     struct qcom_pcie_ep *pcie_ep)  { -	int irq, ret; +	int ret; -	irq = platform_get_irq_byname(pdev, "global"); -	if (irq < 0) -		return irq; +	pcie_ep->global_irq = platform_get_irq_byname(pdev, "global"); +	if (pcie_ep->global_irq < 0) +		return pcie_ep->global_irq; -	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, +	ret = devm_request_threaded_irq(&pdev->dev, pcie_ep->global_irq, NULL,  					qcom_pcie_ep_global_irq_thread,  					IRQF_ONESHOT,  					"global_irq", pcie_ep); @@ -594,7 +640,7 @@ static int qcom_pcie_ep_enable_irq_resources(struct platform_device *pdev,  					"perst_irq", pcie_ep);  	if (ret) {  		dev_err(&pdev->dev, "Failed to request PERST IRQ\n"); -		disable_irq(irq); +		disable_irq(pcie_ep->global_irq);  		return ret;  	} @@ -617,6 +663,37 @@ static int qcom_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,  	}  } +static int qcom_pcie_ep_link_transition_count(struct seq_file *s, void *data) +{ +	struct qcom_pcie_ep *pcie_ep = (struct qcom_pcie_ep *) +				     dev_get_drvdata(s->private); + +	seq_printf(s, "L0s transition count: %u\n", +		   readl_relaxed(pcie_ep->mmio + PARF_DEBUG_CNT_PM_LINKST_IN_L0S)); + +	seq_printf(s, "L1 transition count: %u\n", +		   readl_relaxed(pcie_ep->mmio + PARF_DEBUG_CNT_PM_LINKST_IN_L1)); + +	seq_printf(s, "L1.1 transition count: %u\n", +		   readl_relaxed(pcie_ep->mmio + PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L1)); + +	seq_printf(s, "L1.2 transition count: %u\n", +		   readl_relaxed(pcie_ep->mmio + PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L2)); + +	seq_printf(s, "L2 transition count: %u\n", +		   readl_relaxed(pcie_ep->mmio + PARF_DEBUG_CNT_PM_LINKST_IN_L2)); + +	return 0; +} + +static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep) +{ +	struct dw_pcie *pci = &pcie_ep->pci; + +	debugfs_create_devm_seqfile(pci->dev, "link_transition_count", pcie_ep->debugfs, +				    qcom_pcie_ep_link_transition_count); +} +  static const struct pci_epc_features qcom_pcie_epc_features = {  	.linkup_notifier = true,  	.core_init_notifier = true, @@ -649,6 +726,7 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev;  	struct qcom_pcie_ep *pcie_ep; +	char *name;  	int ret;  	pcie_ep = devm_kzalloc(dev, sizeof(*pcie_ep), GFP_KERNEL); @@ -680,8 +758,21 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)  	if (ret)  		goto err_disable_resources; +	name = devm_kasprintf(dev, GFP_KERNEL, "%pOFP", dev->of_node); +	if (!name) { +		ret = -ENOMEM; +		goto err_disable_irqs; +	} + +	pcie_ep->debugfs = debugfs_create_dir(name, NULL); +	qcom_pcie_ep_init_debugfs(pcie_ep); +  	return 0; +err_disable_irqs: +	disable_irq(pcie_ep->global_irq); +	disable_irq(pcie_ep->perst_irq); +  err_disable_resources:  	qcom_pcie_disable_resources(pcie_ep); @@ -692,6 +783,11 @@ static int qcom_pcie_ep_remove(struct platform_device *pdev)  {  	struct qcom_pcie_ep *pcie_ep = platform_get_drvdata(pdev); +	disable_irq(pcie_ep->global_irq); +	disable_irq(pcie_ep->perst_irq); + +	debugfs_remove_recursive(pcie_ep->debugfs); +  	if (pcie_ep->link_status == QCOM_PCIE_EP_LINK_DISABLED)  		return 0; @@ -702,8 +798,10 @@ static int qcom_pcie_ep_remove(struct platform_device *pdev)  static const struct of_device_id qcom_pcie_ep_match[] = {  	{ .compatible = "qcom,sdx55-pcie-ep", }, +	{ .compatible = "qcom,sm8450-pcie-ep", },  	{ }  }; +MODULE_DEVICE_TABLE(of, qcom_pcie_ep_match);  static struct platform_driver qcom_pcie_ep_driver = {  	.probe	= qcom_pcie_ep_probe,  |