From 684a3a91da401195dbe33b7cef9472bca41c61b9 Mon Sep 17 00:00:00 2001 From: Carlos Palminha Date: Mon, 17 Jul 2017 14:13:34 +0100 Subject: [PATCH 1/6] PCI: dwc: designware: Make dw_pcie_prog_*_atu_unroll() static Helper functions dw_pcie_prog_*_atu_unroll() don't need to be in global scope, so make them static. Cleans up sparse warnings: - symbol 'dw_pcie_prog_outbound_atu_unroll' was not declared. Should it be static? - symbol 'dw_pcie_prog_inbound_atu_unroll' was not declared. Should it be static? Signed-off-by: Carlos Palminha [bhelgaas: rewrap to fit in 80 columns] Signed-off-by: Bjorn Helgaas Acked-by: Joao Pinto Acked-by: Jingoo Han --- drivers/pci/dwc/pcie-designware.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c index 0e03af279259..ebcdce219acb 100644 --- a/drivers/pci/dwc/pcie-designware.c +++ b/drivers/pci/dwc/pcie-designware.c @@ -107,8 +107,9 @@ static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg, dw_pcie_writel_dbi(pci, offset + reg, val); } -void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int index, int type, - u64 cpu_addr, u64 pci_addr, u32 size) +static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, int index, + int type, u64 cpu_addr, + u64 pci_addr, u32 size) { u32 retries, val; @@ -200,8 +201,9 @@ static void dw_pcie_writel_ib_unroll(struct dw_pcie *pci, u32 index, u32 reg, dw_pcie_writel_dbi(pci, offset + reg, val); } -int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, int index, int bar, - u64 cpu_addr, enum dw_pcie_as_type as_type) +static int dw_pcie_prog_inbound_atu_unroll(struct dw_pcie *pci, int index, + int bar, u64 cpu_addr, + enum dw_pcie_as_type as_type) { int type; u32 retries, val; From e9be4d78618af2e0d5592d9556cf0bba210cfd1a Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 18 Jul 2017 14:48:21 +0800 Subject: [PATCH 2/6] PCI: dwc: designware: Test PCIE_ATU_ENABLE bit specifically The ATU CTRL2 register is 32 bits, and bits other than the enable bit may be set. To check whether the ATU is enabled or not, we should test the enable bit specifically. Signed-off-by: Jisheng Zhang Signed-off-by: Bjorn Helgaas Acked-by: Joao Pinto Acked-by: Jingoo Han --- drivers/pci/dwc/pcie-designware.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/dwc/pcie-designware.c b/drivers/pci/dwc/pcie-designware.c index ebcdce219acb..50cef47fc25d 100644 --- a/drivers/pci/dwc/pcie-designware.c +++ b/drivers/pci/dwc/pcie-designware.c @@ -178,7 +178,7 @@ void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, int type, */ for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { val = dw_pcie_readl_dbi(pci, PCIE_ATU_CR2); - if (val == PCIE_ATU_ENABLE) + if (val & PCIE_ATU_ENABLE) return; usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); From 4a301766f5263dd94c1b95d1b1bbdf338afb1a37 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sat, 15 Jul 2017 23:39:45 -0700 Subject: [PATCH 3/6] PCI: dwc: designware: Handle ->host_init() failures In several dwc-based drivers, ->host_init() can fail, so make sure to propagate and handle this to avoid continuing operation of a driver or hardware in an invalid state. Signed-off-by: Bjorn Andersson Signed-off-by: Bjorn Helgaas Acked-by: Joao Pinto Acked-by: Jingoo Han --- drivers/pci/dwc/pci-dra7xx.c | 4 +++- drivers/pci/dwc/pci-exynos.c | 4 +++- drivers/pci/dwc/pci-imx6.c | 4 +++- drivers/pci/dwc/pci-keystone.c | 4 +++- drivers/pci/dwc/pci-layerscape.c | 14 ++++++++++---- drivers/pci/dwc/pcie-armada8k.c | 4 +++- drivers/pci/dwc/pcie-artpec6.c | 4 +++- drivers/pci/dwc/pcie-designware-host.c | 7 +++++-- drivers/pci/dwc/pcie-designware-plat.c | 4 +++- drivers/pci/dwc/pcie-designware.h | 2 +- drivers/pci/dwc/pcie-kirin.c | 4 +++- drivers/pci/dwc/pcie-qcom.c | 6 ++++-- drivers/pci/dwc/pcie-spear13xx.c | 4 +++- 13 files changed, 47 insertions(+), 18 deletions(-) diff --git a/drivers/pci/dwc/pci-dra7xx.c b/drivers/pci/dwc/pci-dra7xx.c index f2fc5f47064e..e8c13bb76169 100644 --- a/drivers/pci/dwc/pci-dra7xx.c +++ b/drivers/pci/dwc/pci-dra7xx.c @@ -195,7 +195,7 @@ static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx) dra7xx_pcie_enable_msi_interrupts(dra7xx); } -static void dra7xx_pcie_host_init(struct pcie_port *pp) +static int dra7xx_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pci); @@ -206,6 +206,8 @@ static void dra7xx_pcie_host_init(struct pcie_port *pp) dw_pcie_wait_for_link(pci); dw_pcie_msi_init(pp); dra7xx_pcie_enable_interrupts(dra7xx); + + return 0; } static const struct dw_pcie_host_ops dra7xx_pcie_host_ops = { diff --git a/drivers/pci/dwc/pci-exynos.c b/drivers/pci/dwc/pci-exynos.c index c78c06552590..f77f872e8b78 100644 --- a/drivers/pci/dwc/pci-exynos.c +++ b/drivers/pci/dwc/pci-exynos.c @@ -581,13 +581,15 @@ static int exynos_pcie_link_up(struct dw_pcie *pci) return 0; } -static void exynos_pcie_host_init(struct pcie_port *pp) +static int exynos_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct exynos_pcie *ep = to_exynos_pcie(pci); exynos_pcie_establish_link(ep); exynos_pcie_enable_interrupts(ep); + + return 0; } static const struct dw_pcie_host_ops exynos_pcie_host_ops = { diff --git a/drivers/pci/dwc/pci-imx6.c b/drivers/pci/dwc/pci-imx6.c index bf5c3616e344..20aae4469ee4 100644 --- a/drivers/pci/dwc/pci-imx6.c +++ b/drivers/pci/dwc/pci-imx6.c @@ -636,7 +636,7 @@ err_reset_phy: return ret; } -static void imx6_pcie_host_init(struct pcie_port *pp) +static int imx6_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct imx6_pcie *imx6_pcie = to_imx6_pcie(pci); @@ -649,6 +649,8 @@ static void imx6_pcie_host_init(struct pcie_port *pp) if (IS_ENABLED(CONFIG_PCI_MSI)) dw_pcie_msi_init(pp); + + return 0; } static int imx6_pcie_link_up(struct dw_pcie *pci) diff --git a/drivers/pci/dwc/pci-keystone.c b/drivers/pci/dwc/pci-keystone.c index 4783cec1f78d..3ad3f8aa27b0 100644 --- a/drivers/pci/dwc/pci-keystone.c +++ b/drivers/pci/dwc/pci-keystone.c @@ -261,7 +261,7 @@ static int keystone_pcie_fault(unsigned long addr, unsigned int fsr, return 0; } -static void __init ks_pcie_host_init(struct pcie_port *pp) +static int __init ks_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct keystone_pcie *ks_pcie = to_keystone_pcie(pci); @@ -289,6 +289,8 @@ static void __init ks_pcie_host_init(struct pcie_port *pp) */ hook_fault_code(17, keystone_pcie_fault, SIGBUS, 0, "Asynchronous external abort"); + + return 0; } static const struct dw_pcie_host_ops keystone_pcie_host_ops = { diff --git a/drivers/pci/dwc/pci-layerscape.c b/drivers/pci/dwc/pci-layerscape.c index fd861289ad8b..7581490f007c 100644 --- a/drivers/pci/dwc/pci-layerscape.c +++ b/drivers/pci/dwc/pci-layerscape.c @@ -108,31 +108,35 @@ static int ls1021_pcie_link_up(struct dw_pcie *pci) return 1; } -static void ls1021_pcie_host_init(struct pcie_port *pp) +static int ls1021_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct ls_pcie *pcie = to_ls_pcie(pci); struct device *dev = pci->dev; u32 index[2]; + int ret; pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,pcie-scfg"); if (IS_ERR(pcie->scfg)) { + ret = PTR_ERR(pcie->scfg); dev_err(dev, "No syscfg phandle specified\n"); pcie->scfg = NULL; - return; + return ret; } if (of_property_read_u32_array(dev->of_node, "fsl,pcie-scfg", index, 2)) { pcie->scfg = NULL; - return; + return -EINVAL; } pcie->index = index[1]; dw_pcie_setup_rc(pp); ls_pcie_drop_msg_tlp(pcie); + + return 0; } static int ls_pcie_link_up(struct dw_pcie *pci) @@ -150,7 +154,7 @@ static int ls_pcie_link_up(struct dw_pcie *pci) return 1; } -static void ls_pcie_host_init(struct pcie_port *pp) +static int ls_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct ls_pcie *pcie = to_ls_pcie(pci); @@ -160,6 +164,8 @@ static void ls_pcie_host_init(struct pcie_port *pp) ls_pcie_clear_multifunction(pcie); ls_pcie_drop_msg_tlp(pcie); iowrite32(0, pci->dbi_base + PCIE_DBI_RO_WR_EN); + + return 0; } static int ls_pcie_msi_host_init(struct pcie_port *pp, diff --git a/drivers/pci/dwc/pcie-armada8k.c b/drivers/pci/dwc/pcie-armada8k.c index ea8f34af6a85..017a727a68db 100644 --- a/drivers/pci/dwc/pcie-armada8k.c +++ b/drivers/pci/dwc/pcie-armada8k.c @@ -134,13 +134,15 @@ static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie) dev_err(pci->dev, "Link not up after reconfiguration\n"); } -static void armada8k_pcie_host_init(struct pcie_port *pp) +static int armada8k_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct armada8k_pcie *pcie = to_armada8k_pcie(pci); dw_pcie_setup_rc(pp); armada8k_pcie_establish_link(pcie); + + return 0; } static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg) diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c index 01c6f7823672..5d81f1d884e3 100644 --- a/drivers/pci/dwc/pcie-artpec6.c +++ b/drivers/pci/dwc/pcie-artpec6.c @@ -175,13 +175,15 @@ static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie) dw_pcie_msi_init(pp); } -static void artpec6_pcie_host_init(struct pcie_port *pp) +static int artpec6_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci); artpec6_pcie_establish_link(artpec6_pcie); artpec6_pcie_enable_interrupts(artpec6_pcie); + + return 0; } static const struct dw_pcie_host_ops artpec6_pcie_host_ops = { diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c index d29c020da082..157621175147 100644 --- a/drivers/pci/dwc/pcie-designware-host.c +++ b/drivers/pci/dwc/pcie-designware-host.c @@ -401,8 +401,11 @@ int dw_pcie_host_init(struct pcie_port *pp) } } - if (pp->ops->host_init) - pp->ops->host_init(pp); + if (pp->ops->host_init) { + ret = pp->ops->host_init(pp); + if (ret) + goto error; + } pp->root_bus_nr = pp->busn->start; diff --git a/drivers/pci/dwc/pcie-designware-plat.c b/drivers/pci/dwc/pcie-designware-plat.c index 091b4e7ad059..168e2380f493 100644 --- a/drivers/pci/dwc/pcie-designware-plat.c +++ b/drivers/pci/dwc/pcie-designware-plat.c @@ -35,7 +35,7 @@ static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg) return dw_handle_msi_irq(pp); } -static void dw_plat_pcie_host_init(struct pcie_port *pp) +static int dw_plat_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -44,6 +44,8 @@ static void dw_plat_pcie_host_init(struct pcie_port *pp) if (IS_ENABLED(CONFIG_PCI_MSI)) dw_pcie_msi_init(pp); + + return 0; } static const struct dw_pcie_host_ops dw_plat_pcie_host_ops = { diff --git a/drivers/pci/dwc/pcie-designware.h b/drivers/pci/dwc/pcie-designware.h index b4d2a89f8e58..7366c8167404 100644 --- a/drivers/pci/dwc/pcie-designware.h +++ b/drivers/pci/dwc/pcie-designware.h @@ -134,7 +134,7 @@ struct dw_pcie_host_ops { unsigned int devfn, int where, int size, u32 *val); int (*wr_other_conf)(struct pcie_port *pp, struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val); - void (*host_init)(struct pcie_port *pp); + int (*host_init)(struct pcie_port *pp); void (*msi_set_irq)(struct pcie_port *pp, int irq); void (*msi_clear_irq)(struct pcie_port *pp, int irq); phys_addr_t (*get_msi_addr)(struct pcie_port *pp); diff --git a/drivers/pci/dwc/pcie-kirin.c b/drivers/pci/dwc/pcie-kirin.c index 33fddb9f6739..0b0eb67f2658 100644 --- a/drivers/pci/dwc/pcie-kirin.c +++ b/drivers/pci/dwc/pcie-kirin.c @@ -430,9 +430,11 @@ static int kirin_pcie_establish_link(struct pcie_port *pp) return 0; } -static void kirin_pcie_host_init(struct pcie_port *pp) +static int kirin_pcie_host_init(struct pcie_port *pp) { kirin_pcie_establish_link(pp); + + return 0; } static struct dw_pcie_ops kirin_dw_pcie_ops = { diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c index 68c5f2ab5bc8..d15657dc3990 100644 --- a/drivers/pci/dwc/pcie-qcom.c +++ b/drivers/pci/dwc/pcie-qcom.c @@ -891,7 +891,7 @@ static int qcom_pcie_link_up(struct dw_pcie *pci) return !!(val & PCI_EXP_LNKSTA_DLLLA); } -static void qcom_pcie_host_init(struct pcie_port *pp) +static int qcom_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct qcom_pcie *pcie = to_qcom_pcie(pci); @@ -921,12 +921,14 @@ static void qcom_pcie_host_init(struct pcie_port *pp) if (ret) goto err; - return; + return 0; err: qcom_ep_reset_assert(pcie); phy_power_off(pcie->phy); err_deinit: pcie->ops->deinit(pcie); + + return ret; } static int qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, diff --git a/drivers/pci/dwc/pcie-spear13xx.c b/drivers/pci/dwc/pcie-spear13xx.c index 80897291e0fb..52000bc34600 100644 --- a/drivers/pci/dwc/pcie-spear13xx.c +++ b/drivers/pci/dwc/pcie-spear13xx.c @@ -177,13 +177,15 @@ static int spear13xx_pcie_link_up(struct dw_pcie *pci) return 0; } -static void spear13xx_pcie_host_init(struct pcie_port *pp) +static int spear13xx_pcie_host_init(struct pcie_port *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pci); spear13xx_pcie_establish_link(spear13xx_pcie); spear13xx_pcie_enable_interrupts(spear13xx_pcie); + + return 0; } static const struct dw_pcie_host_ops spear13xx_pcie_host_ops = { From 89539f03061fc8aee120ea4a64d31da57d0045f2 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sat, 15 Jul 2017 23:41:53 -0700 Subject: [PATCH 4/6] PCI: qcom: Don't unroll init if ->init() fails When the init op fails it will restore the state of the resources, so we should not disable them one more time when this happens. Signed-off-by: Bjorn Andersson Signed-off-by: Bjorn Helgaas Acked-by: Stanimir Varbanov --- drivers/pci/dwc/pcie-qcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c index d15657dc3990..7b703741a3fd 100644 --- a/drivers/pci/dwc/pcie-qcom.c +++ b/drivers/pci/dwc/pcie-qcom.c @@ -901,7 +901,7 @@ static int qcom_pcie_host_init(struct pcie_port *pp) ret = pcie->ops->init(pcie); if (ret) - goto err_deinit; + return ret; ret = phy_power_on(pcie->phy); if (ret) From 71cee8e1902a3c1d00e52dc022e1aff3ac2680d3 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sat, 15 Jul 2017 23:42:03 -0700 Subject: [PATCH 5/6] PCI: qcom: Allow ->post_init() to fail host_init() should detect and propagate errors from post_init(). In addition, by acknowledging that post_init() can fail we must disable the post_init() resources in a step separate from the deinit, so that we don't try to disable the post_init() resources a second time. Signed-off-by: Bjorn Andersson Signed-off-by: Bjorn Helgaas Acked-by: Stanimir Varbanov --- drivers/pci/dwc/pcie-qcom.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/pci/dwc/pcie-qcom.c b/drivers/pci/dwc/pcie-qcom.c index 7b703741a3fd..26e84a957c35 100644 --- a/drivers/pci/dwc/pcie-qcom.c +++ b/drivers/pci/dwc/pcie-qcom.c @@ -124,6 +124,7 @@ struct qcom_pcie_ops { int (*init)(struct qcom_pcie *pcie); int (*post_init)(struct qcom_pcie *pcie); void (*deinit)(struct qcom_pcie *pcie); + void (*post_deinit)(struct qcom_pcie *pcie); void (*ltssm_enable)(struct qcom_pcie *pcie); }; @@ -517,13 +518,19 @@ static void qcom_pcie_deinit_v2(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v2 *res = &pcie->res.v2; - clk_disable_unprepare(res->pipe_clk); clk_disable_unprepare(res->slave_clk); clk_disable_unprepare(res->master_clk); clk_disable_unprepare(res->cfg_clk); clk_disable_unprepare(res->aux_clk); } +static void qcom_pcie_post_deinit_v2(struct qcom_pcie *pcie) +{ + struct qcom_pcie_resources_v2 *res = &pcie->res.v2; + + clk_disable_unprepare(res->pipe_clk); +} + static int qcom_pcie_init_v2(struct qcom_pcie *pcie) { struct qcom_pcie_resources_v2 *res = &pcie->res.v2; @@ -907,8 +914,11 @@ static int qcom_pcie_host_init(struct pcie_port *pp) if (ret) goto err_deinit; - if (pcie->ops->post_init) - pcie->ops->post_init(pcie); + if (pcie->ops->post_init) { + ret = pcie->ops->post_init(pcie); + if (ret) + goto err_disable_phy; + } dw_pcie_setup_rc(pp); @@ -924,6 +934,9 @@ static int qcom_pcie_host_init(struct pcie_port *pp) return 0; err: qcom_ep_reset_assert(pcie); + if (pcie->ops->post_deinit) + pcie->ops->post_deinit(pcie); +err_disable_phy: phy_power_off(pcie->phy); err_deinit: pcie->ops->deinit(pcie); @@ -971,6 +984,7 @@ static const struct qcom_pcie_ops ops_v2 = { .init = qcom_pcie_init_v2, .post_init = qcom_pcie_post_init_v2, .deinit = qcom_pcie_deinit_v2, + .post_deinit = qcom_pcie_post_deinit_v2, .ltssm_enable = qcom_pcie_v2_ltssm_enable, }; From 8c934095fa2f336d92b722f49f78ca7abf47e051 Mon Sep 17 00:00:00 2001 From: Faiz Abbas Date: Thu, 10 Aug 2017 16:54:55 +0530 Subject: [PATCH 6/6] PCI: dwc: Clear MSI interrupt status after it is handled, not before If the interrupt status is cleared before it is handled, it is possible that another interrupt will trigger while servicing the previous one. This is causing timeouts in some wireless lan cards which use PCIe. Clear MSI interrupt status after it gets serviced instead of before calling generic_handler. Signed-off-by: Faiz Abbas Signed-off-by: Bjorn Helgaas Acked-By: Joao Pinto --- drivers/pci/dwc/pcie-designware-host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/dwc/pcie-designware-host.c b/drivers/pci/dwc/pcie-designware-host.c index 157621175147..99cb70b5de79 100644 --- a/drivers/pci/dwc/pcie-designware-host.c +++ b/drivers/pci/dwc/pcie-designware-host.c @@ -71,9 +71,9 @@ irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) while ((pos = find_next_bit((unsigned long *) &val, 32, pos)) != 32) { irq = irq_find_mapping(pp->irq_domain, i * 32 + pos); + generic_handle_irq(irq); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, 1 << pos); - generic_handle_irq(irq); pos++; } }