From 5aa180974e4d53f397f7d02bbce2c1105e8e538b Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 28 May 2019 17:57:08 -0700 Subject: PCI: qcom: Use clk bulk API for 2.4.0 controllers Before introducing the QCS404 platform, which uses the same PCIe controller as IPQ4019, migrate this to use the bulk clock API, in order to make the error paths slighly cleaner. Signed-off-by: Bjorn Andersson Signed-off-by: Lorenzo Pieralisi Reviewed-by: Niklas Cassel Reviewed-by: Vinod Koul Acked-by: Stanimir Varbanov --- drivers/pci/controller/dwc/pcie-qcom.c | 53 ++++++++++------------------------ 1 file changed, 16 insertions(+), 37 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 0ed235d560e3..23dc01212508 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -112,10 +112,10 @@ struct qcom_pcie_resources_2_3_2 { struct regulator_bulk_data supplies[QCOM_PCIE_2_3_2_MAX_SUPPLY]; }; +#define QCOM_PCIE_2_4_0_MAX_CLOCKS 3 struct qcom_pcie_resources_2_4_0 { - struct clk *aux_clk; - struct clk *master_clk; - struct clk *slave_clk; + struct clk_bulk_data clks[QCOM_PCIE_2_4_0_MAX_CLOCKS]; + int num_clks; struct reset_control *axi_m_reset; struct reset_control *axi_s_reset; struct reset_control *pipe_reset; @@ -638,18 +638,17 @@ static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie) struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; struct dw_pcie *pci = pcie->pci; struct device *dev = pci->dev; + int ret; - res->aux_clk = devm_clk_get(dev, "aux"); - if (IS_ERR(res->aux_clk)) - return PTR_ERR(res->aux_clk); + res->clks[0].id = "aux"; + res->clks[1].id = "master_bus"; + res->clks[2].id = "slave_bus"; - res->master_clk = devm_clk_get(dev, "master_bus"); - if (IS_ERR(res->master_clk)) - return PTR_ERR(res->master_clk); + res->num_clks = 3; - res->slave_clk = devm_clk_get(dev, "slave_bus"); - if (IS_ERR(res->slave_clk)) - return PTR_ERR(res->slave_clk); + ret = devm_clk_bulk_get(dev, res->num_clks, res->clks); + if (ret < 0) + return ret; res->axi_m_reset = devm_reset_control_get_exclusive(dev, "axi_m"); if (IS_ERR(res->axi_m_reset)) @@ -719,9 +718,7 @@ static void qcom_pcie_deinit_2_4_0(struct qcom_pcie *pcie) reset_control_assert(res->axi_m_sticky_reset); reset_control_assert(res->pwr_reset); reset_control_assert(res->ahb_reset); - clk_disable_unprepare(res->aux_clk); - clk_disable_unprepare(res->master_clk); - clk_disable_unprepare(res->slave_clk); + clk_bulk_disable_unprepare(res->num_clks, res->clks); } static int qcom_pcie_init_2_4_0(struct qcom_pcie *pcie) @@ -850,23 +847,9 @@ static int qcom_pcie_init_2_4_0(struct qcom_pcie *pcie) usleep_range(10000, 12000); - ret = clk_prepare_enable(res->aux_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable iface clock\n"); - goto err_clk_aux; - } - - ret = clk_prepare_enable(res->master_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable core clock\n"); - goto err_clk_axi_m; - } - - ret = clk_prepare_enable(res->slave_clk); - if (ret) { - dev_err(dev, "cannot prepare/enable phy clock\n"); - goto err_clk_axi_s; - } + ret = clk_bulk_prepare_enable(res->num_clks, res->clks); + if (ret) + goto err_clks; /* enable PCIe clocks and resets */ val = readl(pcie->parf + PCIE20_PARF_PHY_CTRL); @@ -891,11 +874,7 @@ static int qcom_pcie_init_2_4_0(struct qcom_pcie *pcie) return 0; -err_clk_axi_s: - clk_disable_unprepare(res->master_clk); -err_clk_axi_m: - clk_disable_unprepare(res->aux_clk); -err_clk_aux: +err_clks: reset_control_assert(res->ahb_reset); err_rst_ahb: reset_control_assert(res->pwr_reset); -- cgit v1.2.3-55-g7522 From 67021ae0bbe941dca6605efab52089bcbf245fb5 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 28 May 2019 17:57:10 -0700 Subject: PCI: qcom: Add QCS404 PCIe controller support The QCS404 platform contains a PCIe version 2.4.0 controller and a Qualcomm PCIe2 PHY. The driver already supports version 2.4.0, for the IPQ4019, but this support touches clocks and resets related to the PHY as well and there's no upstream driver for the PHY. On QCS404 we must initialize the PHY, so a separate PHY driver is implemented to take care of this and the controller driver is updated to not require the PHY related resources. This is done by relying on the fact that operations in both the clock and reset framework are NOPs when passed NULL, so we can isolate this change to only the qcom_pcie_get_resources_2_4_0() function. For QCS404 we also need to enable the AHB (iface) clock, in order to access the register space of the controller, but as this is not part of the IPQ4019 DT binding this is only added for new users of the 2.4.0 controller. Signed-off-by: Bjorn Andersson Signed-off-by: Lorenzo Pieralisi Reviewed-by: Niklas Cassel Reviewed-by: Vinod Koul Acked-by: Stanimir Varbanov --- drivers/pci/controller/dwc/pcie-qcom.c | 64 ++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 26 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 23dc01212508..da5dd3639a49 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -112,7 +112,7 @@ struct qcom_pcie_resources_2_3_2 { struct regulator_bulk_data supplies[QCOM_PCIE_2_3_2_MAX_SUPPLY]; }; -#define QCOM_PCIE_2_4_0_MAX_CLOCKS 3 +#define QCOM_PCIE_2_4_0_MAX_CLOCKS 4 struct qcom_pcie_resources_2_4_0 { struct clk_bulk_data clks[QCOM_PCIE_2_4_0_MAX_CLOCKS]; int num_clks; @@ -638,13 +638,16 @@ static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie) struct qcom_pcie_resources_2_4_0 *res = &pcie->res.v2_4_0; struct dw_pcie *pci = pcie->pci; struct device *dev = pci->dev; + bool is_ipq = of_device_is_compatible(dev->of_node, "qcom,pcie-ipq4019"); int ret; res->clks[0].id = "aux"; res->clks[1].id = "master_bus"; res->clks[2].id = "slave_bus"; + res->clks[3].id = "iface"; - res->num_clks = 3; + /* qcom,pcie-ipq4019 is defined without "iface" */ + res->num_clks = is_ipq ? 3 : 4; ret = devm_clk_bulk_get(dev, res->num_clks, res->clks); if (ret < 0) @@ -658,27 +661,33 @@ static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie) if (IS_ERR(res->axi_s_reset)) return PTR_ERR(res->axi_s_reset); - res->pipe_reset = devm_reset_control_get_exclusive(dev, "pipe"); - if (IS_ERR(res->pipe_reset)) - return PTR_ERR(res->pipe_reset); - - res->axi_m_vmid_reset = devm_reset_control_get_exclusive(dev, - "axi_m_vmid"); - if (IS_ERR(res->axi_m_vmid_reset)) - return PTR_ERR(res->axi_m_vmid_reset); - - res->axi_s_xpu_reset = devm_reset_control_get_exclusive(dev, - "axi_s_xpu"); - if (IS_ERR(res->axi_s_xpu_reset)) - return PTR_ERR(res->axi_s_xpu_reset); - - res->parf_reset = devm_reset_control_get_exclusive(dev, "parf"); - if (IS_ERR(res->parf_reset)) - return PTR_ERR(res->parf_reset); - - res->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); - if (IS_ERR(res->phy_reset)) - return PTR_ERR(res->phy_reset); + if (is_ipq) { + /* + * These resources relates to the PHY or are secure clocks, but + * are controlled here for IPQ4019 + */ + res->pipe_reset = devm_reset_control_get_exclusive(dev, "pipe"); + if (IS_ERR(res->pipe_reset)) + return PTR_ERR(res->pipe_reset); + + res->axi_m_vmid_reset = devm_reset_control_get_exclusive(dev, + "axi_m_vmid"); + if (IS_ERR(res->axi_m_vmid_reset)) + return PTR_ERR(res->axi_m_vmid_reset); + + res->axi_s_xpu_reset = devm_reset_control_get_exclusive(dev, + "axi_s_xpu"); + if (IS_ERR(res->axi_s_xpu_reset)) + return PTR_ERR(res->axi_s_xpu_reset); + + res->parf_reset = devm_reset_control_get_exclusive(dev, "parf"); + if (IS_ERR(res->parf_reset)) + return PTR_ERR(res->parf_reset); + + res->phy_reset = devm_reset_control_get_exclusive(dev, "phy"); + if (IS_ERR(res->phy_reset)) + return PTR_ERR(res->phy_reset); + } res->axi_m_sticky_reset = devm_reset_control_get_exclusive(dev, "axi_m_sticky"); @@ -698,9 +707,11 @@ static int qcom_pcie_get_resources_2_4_0(struct qcom_pcie *pcie) if (IS_ERR(res->ahb_reset)) return PTR_ERR(res->ahb_reset); - res->phy_ahb_reset = devm_reset_control_get_exclusive(dev, "phy_ahb"); - if (IS_ERR(res->phy_ahb_reset)) - return PTR_ERR(res->phy_ahb_reset); + if (is_ipq) { + res->phy_ahb_reset = devm_reset_control_get_exclusive(dev, "phy_ahb"); + if (IS_ERR(res->phy_ahb_reset)) + return PTR_ERR(res->phy_ahb_reset); + } return 0; } @@ -1268,6 +1279,7 @@ static const struct of_device_id qcom_pcie_match[] = { { .compatible = "qcom,pcie-msm8996", .data = &ops_2_3_2 }, { .compatible = "qcom,pcie-ipq8074", .data = &ops_2_3_3 }, { .compatible = "qcom,pcie-ipq4019", .data = &ops_2_4_0 }, + { .compatible = "qcom,pcie-qcs404", .data = &ops_2_4_0 }, { } }; -- cgit v1.2.3-55-g7522 From 76002d8b48c4b08c9bd414517dd295e132ad910b Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 1 May 2019 11:00:16 -0600 Subject: PCI: Return error if cannot probe VF Commit 0e7df22401a3 ("PCI: Add sysfs sriov_drivers_autoprobe to control VF driver binding") allows the user to specify that drivers for VFs of a PF should not be probed, but it actually causes pci_device_probe() to return success back to the driver core in this case. Therefore by all sysfs appearances the device is bound to a driver, the driver link from the device exists as does the device link back from the driver, yet the driver's probe function is never called on the device. We also fail to do any sort of cleanup when we're prohibited from probing the device, the IRQ setup remains in place and we even hold a device reference. Instead, abort with errno before any setup or references are taken when pci_device_can_probe() prevents us from trying to probe the device. Link: https://lore.kernel.org/lkml/155672991496.20698.4279330795743262888.stgit@gimli.home Fixes: 0e7df22401a3 ("PCI: Add sysfs sriov_drivers_autoprobe to control VF driver binding") Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index cae630fe6387..53874aae3873 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -414,6 +414,9 @@ static int pci_device_probe(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *drv = to_pci_driver(dev->driver); + if (!pci_device_can_probe(pci_dev)) + return -ENODEV; + pci_assign_irq(pci_dev); error = pcibios_alloc_irq(pci_dev); @@ -421,12 +424,10 @@ static int pci_device_probe(struct device *dev) return error; pci_dev_get(pci_dev); - if (pci_device_can_probe(pci_dev)) { - error = __pci_device_probe(drv, pci_dev); - if (error) { - pcibios_free_irq(pci_dev); - pci_dev_put(pci_dev); - } + error = __pci_device_probe(drv, pci_dev); + if (error) { + pcibios_free_irq(pci_dev); + pci_dev_put(pci_dev); } return error; -- cgit v1.2.3-55-g7522 From ec15c4d0d5d2e431169fbdf6c6bcf0c8ff71ef22 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 24 Apr 2019 12:57:14 +0800 Subject: PCI: altera: Allow building as module Altera PCIe Rootport IP is a soft IP and is only available after an FPGA image (whose design contains it) is programmed. Make driver modulable to support use cases when FPGA image is programmed after the kernel has booted, so that the driver can be loaded upon request. Signed-off-by: Ley Foon Tan Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/Kconfig | 2 +- drivers/pci/controller/pcie-altera.c | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 011c57cae4b0..89906351bd3f 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -174,7 +174,7 @@ config PCIE_IPROC_MSI PCIe controller config PCIE_ALTERA - bool "Altera PCIe controller" + tristate "Altera PCIe controller" depends on ARM || NIOS2 || ARM64 || COMPILE_TEST help Say Y here if you want to enable PCIe controller support on Altera diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 27edcebd1726..27222071ace7 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -705,6 +706,13 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) return 0; } +static void altera_pcie_irq_teardown(struct altera_pcie *pcie) +{ + irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); + irq_domain_remove(pcie->irq_domain); + irq_dispose_mapping(pcie->irq); +} + static int altera_pcie_parse_dt(struct altera_pcie *pcie) { struct device *dev = &pcie->pdev->dev; @@ -798,6 +806,7 @@ static int altera_pcie_probe(struct platform_device *pdev) pcie = pci_host_bridge_priv(bridge); pcie->pdev = pdev; + platform_set_drvdata(pdev, pcie); match = of_match_device(altera_pcie_of_match, &pdev->dev); if (!match) @@ -855,13 +864,28 @@ static int altera_pcie_probe(struct platform_device *pdev) return ret; } +static int altera_pcie_remove(struct platform_device *pdev) +{ + struct altera_pcie *pcie = platform_get_drvdata(pdev); + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); + + pci_stop_root_bus(bridge->bus); + pci_remove_root_bus(bridge->bus); + pci_free_resource_list(&pcie->resources); + altera_pcie_irq_teardown(pcie); + + return 0; +} + static struct platform_driver altera_pcie_driver = { .probe = altera_pcie_probe, + .remove = altera_pcie_remove, .driver = { .name = "altera-pcie", .of_match_table = altera_pcie_of_match, - .suppress_bind_attrs = true, }, }; -builtin_platform_driver(altera_pcie_driver); +MODULE_DEVICE_TABLE(of, altera_pcie_of_match); +module_platform_driver(altera_pcie_driver); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-55-g7522 From c7ddfd3514f2702a70e9ff86504cafa856dd16b2 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 24 Apr 2019 12:57:15 +0800 Subject: PCI: altera-msi: Allow building as module Altera MSI IP is a soft IP and is only available after an FPGA image (with design containing it) is programmed. Make driver modulable to support use case FPGA image is programmed the after kernel has booted, so that the driver can be loaded upon request. Signed-off-by: Ley Foon Tan Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/Kconfig | 2 +- drivers/pci/controller/pcie-altera-msi.c | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 89906351bd3f..fe9f9f13ce11 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -181,7 +181,7 @@ config PCIE_ALTERA FPGA. config PCIE_ALTERA_MSI - bool "Altera PCIe MSI feature" + tristate "Altera PCIe MSI feature" depends on PCIE_ALTERA depends on PCI_MSI_IRQ_DOMAIN help diff --git a/drivers/pci/controller/pcie-altera-msi.c b/drivers/pci/controller/pcie-altera-msi.c index 025ef7d9a046..16d938920ca5 100644 --- a/drivers/pci/controller/pcie-altera-msi.c +++ b/drivers/pci/controller/pcie-altera-msi.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -288,4 +289,13 @@ static int __init altera_msi_init(void) { return platform_driver_register(&altera_msi_driver); } + +static void __exit altera_msi_exit(void) +{ + platform_driver_unregister(&altera_msi_driver); +} + subsys_initcall(altera_msi_init); +MODULE_DEVICE_TABLE(of, altera_msi_of_match); +module_exit(altera_msi_exit); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-55-g7522 From 64adde31c8e996a6db6f7a1a4131180e363aa9f2 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 29 May 2019 11:43:52 +0200 Subject: PCI: qcom: Ensure that PERST is asserted for at least 100 ms Currently, there is only a 1 ms sleep after asserting PERST. Reading the datasheets for different endpoints, some require PERST to be asserted for 10 ms in order for the endpoint to perform a reset, others require it to be asserted for 50 ms. Several SoCs using this driver uses PCIe Mini Card, where we don't know what endpoint will be plugged in. The PCI Express Card Electromechanical Specification r2.0, section 2.2, "PERST# Signal" specifies: "On power up, the deassertion of PERST# is delayed 100 ms (TPVPERL) from the power rails achieving specified operating limits." Add a sleep of 100 ms before deasserting PERST, in order to ensure that we are compliant with the spec. Fixes: 82a823833f4e ("PCI: qcom: Add Qualcomm PCIe controller driver") Signed-off-by: Niklas Cassel Signed-off-by: Lorenzo Pieralisi Acked-by: Stanimir Varbanov Cc: stable@vger.kernel.org # 4.5+ --- drivers/pci/controller/dwc/pcie-qcom.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index da5dd3639a49..7e581748ee9f 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -178,6 +178,8 @@ static void qcom_ep_reset_assert(struct qcom_pcie *pcie) static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) { + /* Ensure that PERST has been asserted for at least 100 ms */ + msleep(100); gpiod_set_value_cansleep(pcie->reset, 0); usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); } -- cgit v1.2.3-55-g7522 From db7a62482d2f6a63f36f30f62c4cbf1e53035719 Mon Sep 17 00:00:00 2001 From: Alan Mikhak Date: Thu, 23 May 2019 14:45:44 -0700 Subject: PCI: endpoint: Set endpoint controller pointer to NULL Set endpoint controller pointer to NULL in pci_epc_remove_epf() to avoid -EBUSY on subsequent call to pci_epc_add_epf(). Add a check for NULL endpoint function pointer. Signed-off-by: Alan Mikhak Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/pci-epc-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index e4712a0f249c..2091508c1620 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -519,11 +519,12 @@ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf) { unsigned long flags; - if (!epc || IS_ERR(epc)) + if (!epc || IS_ERR(epc) || !epf) return; spin_lock_irqsave(&epc->lock, flags); list_del(&epf->list); + epf->epc = NULL; spin_unlock_irqrestore(&epc->lock, flags); } EXPORT_SYMBOL_GPL(pci_epc_remove_epf); -- cgit v1.2.3-55-g7522 From f16fb16ed16c7f561e9c41c9ae4107c7f6aa553c Mon Sep 17 00:00:00 2001 From: Alan Mikhak Date: Thu, 23 May 2019 14:47:59 -0700 Subject: PCI: endpoint: Allocate enough space for fixed size BAR PCI endpoint test function code should honor the .bar_fixed_size parameter from underlying endpoint controller drivers or results may be unexpected. In pci_epf_test_alloc_space(), check if BAR being used for test register space is a fixed size BAR. If so, allocate the required fixed size. Signed-off-by: Alan Mikhak Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/functions/pci-epf-test.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 27806987e93b..7d41e6684b87 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -434,10 +434,16 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) int bar; enum pci_barno test_reg_bar = epf_test->test_reg_bar; const struct pci_epc_features *epc_features; + size_t test_reg_size; epc_features = epf_test->epc_features; - base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg), + if (epc_features->bar_fixed_size[test_reg_bar]) + test_reg_size = bar_size[test_reg_bar]; + else + test_reg_size = sizeof(struct pci_epf_test_reg); + + base = pci_epf_alloc_space(epf, test_reg_size, test_reg_bar, epc_features->align); if (!base) { dev_err(dev, "Failed to allocated register space\n"); -- cgit v1.2.3-55-g7522 From 3041a643613a2530ade35a9ae97709a9da4c0c72 Mon Sep 17 00:00:00 2001 From: Alan Mikhak Date: Thu, 23 May 2019 14:55:40 -0700 Subject: PCI: endpoint: Skip odd BAR when skipping 64bit BAR Always skip odd BAR when skipping 64bit BARs in pci_epf_test_set_bar() and pci_epf_test_alloc_space() otherwise pci_epf_test_set_bar() will call pci_epc_set_bar() on an odd loop index when skipping reserved 64bit BAR. Moreover, pci_epf_test_alloc_space() will call pci_epf_alloc_space() on bind for an odd loop index when BAR is 64bit but leaks on subsequent unbind by not calling pci_epf_free_space(). Signed-off-by: Alan Mikhak Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I Reviewed-by: Paul Walmsley --- drivers/pci/endpoint/functions/pci-epf-test.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 7d41e6684b87..e8bcc924dbf8 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -389,7 +389,7 @@ static void pci_epf_test_unbind(struct pci_epf *epf) static int pci_epf_test_set_bar(struct pci_epf *epf) { - int bar; + int bar, add; int ret; struct pci_epf_bar *epf_bar; struct pci_epc *epc = epf->epc; @@ -400,8 +400,14 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) epc_features = epf_test->epc_features; - for (bar = BAR_0; bar <= BAR_5; bar++) { + for (bar = BAR_0; bar <= BAR_5; bar += add) { epf_bar = &epf->bar[bar]; + /* + * pci_epc_set_bar() sets PCI_BASE_ADDRESS_MEM_TYPE_64 + * if the specific implementation required a 64-bit BAR, + * even if we only requested a 32-bit BAR. + */ + add = (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ? 2 : 1; if (!!(epc_features->reserved_bar & (1 << bar))) continue; @@ -413,13 +419,6 @@ static int pci_epf_test_set_bar(struct pci_epf *epf) if (bar == test_reg_bar) return ret; } - /* - * pci_epc_set_bar() sets PCI_BASE_ADDRESS_MEM_TYPE_64 - * if the specific implementation required a 64-bit BAR, - * even if we only requested a 32-bit BAR. - */ - if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) - bar++; } return 0; @@ -431,7 +430,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) struct device *dev = &epf->dev; struct pci_epf_bar *epf_bar; void *base; - int bar; + int bar, add; enum pci_barno test_reg_bar = epf_test->test_reg_bar; const struct pci_epc_features *epc_features; size_t test_reg_size; @@ -451,8 +450,10 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) } epf_test->reg[test_reg_bar] = base; - for (bar = BAR_0; bar <= BAR_5; bar++) { + for (bar = BAR_0; bar <= BAR_5; bar += add) { epf_bar = &epf->bar[bar]; + add = (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ? 2 : 1; + if (bar == test_reg_bar) continue; @@ -465,8 +466,6 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) dev_err(dev, "Failed to allocate space for BAR%d\n", bar); epf_test->reg[bar] = base; - if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64) - bar++; } return 0; -- cgit v1.2.3-55-g7522 From dbb7bbcc8ad248b1ab05bd27dfdb587ef4023dab Mon Sep 17 00:00:00 2001 From: Alan Mikhak Date: Thu, 23 May 2019 14:57:27 -0700 Subject: PCI: endpoint: Clear BAR before freeing its space Associated pci_epf_bar structure is needed in pci_epc_clear_bar() to clear a BAR correctly but it is reset in pci_epf_free_space() (that is called first) which results in pci_epc_clear_bar() failure. Reorder the pci_epc_clear_bar()/pci_epf_free_space() calls execution to fix the issue. Signed-off-by: Alan Mikhak [lorenzo.pieralisi@arm.com: reworded the commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/functions/pci-epf-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index e8bcc924dbf8..1cfe3687a211 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -381,8 +381,8 @@ static void pci_epf_test_unbind(struct pci_epf *epf) epf_bar = &epf->bar[bar]; if (epf_test->reg[bar]) { - pci_epf_free_space(epf, epf_test->reg[bar], bar); pci_epc_clear_bar(epc, epf->func_no, epf_bar); + pci_epf_free_space(epf, epf_test->reg[bar], bar); } } } -- cgit v1.2.3-55-g7522 From a17beb1a0882a544523dcb5d0da4801272dfd43a Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 6 Jun 2019 14:52:24 +0530 Subject: PCI: Generalize multi-function power dependency device links Although not allowed by the PCI specs, some multi-function devices have power dependencies between the functions. For example, function 1 may not work unless function 0 is in the D0 power state. The existing quirk_gpu_hda() adds a device link to express this dependency for GPU and HDA devices, but it really is not specific to those device types. Generalize it and rename it to pci_create_device_link() so we can create dependencies between any "consumer" and "producer" functions of a multi-function device, where the consumer is only functional if the producer is in D0. This reorganization should not affect any functionality. Link: https://lore.kernel.org/lkml/20190606092225.17960-2-abhsahu@nvidia.com Signed-off-by: Abhishek Sahu [bhelgaas: commit log, reword diagnostic] Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 54 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 20 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 0f16acc323c6..d9c162df9d52 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4934,35 +4934,49 @@ static void quirk_fsl_no_msi(struct pci_dev *pdev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi); /* - * GPUs with integrated HDA controller for streaming audio to attached displays - * need a device link from the HDA controller (consumer) to the GPU (supplier) - * so that the GPU is powered up whenever the HDA controller is accessed. - * The GPU and HDA controller are functions 0 and 1 of the same PCI device. - * The device link stays in place until shutdown (or removal of the PCI device - * if it's hotplugged). Runtime PM is allowed by default on the HDA controller - * to prevent it from permanently keeping the GPU awake. + * Although not allowed by the spec, some multi-function devices have + * dependencies of one function (consumer) on another (supplier). For the + * consumer to work in D0, the supplier must also be in D0. Create a + * device link from the consumer to the supplier to enforce this + * dependency. Runtime PM is allowed by default on the consumer to prevent + * it from permanently keeping the supplier awake. */ -static void quirk_gpu_hda(struct pci_dev *hda) +static void pci_create_device_link(struct pci_dev *pdev, unsigned int consumer, + unsigned int supplier, unsigned int class, + unsigned int class_shift) { - struct pci_dev *gpu; + struct pci_dev *supplier_pdev; - if (PCI_FUNC(hda->devfn) != 1) + if (PCI_FUNC(pdev->devfn) != consumer) return; - gpu = pci_get_domain_bus_and_slot(pci_domain_nr(hda->bus), - hda->bus->number, - PCI_DEVFN(PCI_SLOT(hda->devfn), 0)); - if (!gpu || (gpu->class >> 16) != PCI_BASE_CLASS_DISPLAY) { - pci_dev_put(gpu); + supplier_pdev = pci_get_domain_bus_and_slot(pci_domain_nr(pdev->bus), + pdev->bus->number, + PCI_DEVFN(PCI_SLOT(pdev->devfn), supplier)); + if (!supplier_pdev || (supplier_pdev->class >> class_shift) != class) { + pci_dev_put(supplier_pdev); return; } - if (!device_link_add(&hda->dev, &gpu->dev, - DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) - pci_err(hda, "cannot link HDA to GPU %s\n", pci_name(gpu)); + if (device_link_add(&pdev->dev, &supplier_pdev->dev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) + pci_info(pdev, "D0 power state depends on %s\n", + pci_name(supplier_pdev)); + else + pci_err(pdev, "Cannot enforce power dependency on %s\n", + pci_name(supplier_pdev)); + + pm_runtime_allow(&pdev->dev); + pci_dev_put(supplier_pdev); +} - pm_runtime_allow(&hda->dev); - pci_dev_put(gpu); +/* + * Create device link for GPUs with integrated HDA controller for streaming + * audio to attached displays. + */ +static void quirk_gpu_hda(struct pci_dev *hda) +{ + pci_create_device_link(hda, 1, 0, PCI_BASE_CLASS_DISPLAY, 16); } DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda); -- cgit v1.2.3-55-g7522 From 6d2e369f0d4c3e6125c886847c04106b03d2609e Mon Sep 17 00:00:00 2001 From: Abhishek Sahu Date: Thu, 6 Jun 2019 14:52:25 +0530 Subject: PCI: Add NVIDIA GPU multi-function power dependencies The NVIDIA Turing GPU is a multi-function PCI device with the following functions: - Function 0: VGA display controller - Function 1: Audio controller - Function 2: USB xHCI Host controller - Function 3: USB Type-C UCSI controller Function 0 is tightly coupled with other functions in the hardware. When function 0 is in D3, it gates power for hardware blocks used by other functions, which means those functions only work when function 0 is in D0. If any of these functions (1/2/3) are in D0, then function 0 should also be in D0. Commit 07f4f97d7b4b ("vga_switcheroo: Use device link for HDA controller") already creates a device link to show the dependency of function 1 on function 0 of this GPU. Create additional device links to express the dependencies of functions 2 and 3 on function 0. This means function 0 will be in D0 if any other function is in D0. [bhelgaas: I think the PCI spec expectation is that functions can be power-managed independently, so I don't think this device is technically compliant. For example, the PCIe r5.0 spec, sec 1.4, says "the PCI/PCIe hardware/software model includes architectural constructs necessary to discover, configure, and use a Function, without needing Function-specific knowledge" and sec 5.1 says "D states are associated with a particular Function" and "PM provides ... a mechanism to identify power management capabilities of a given Function [and] the ability to transition a Function into a certain power management state."] Link: https://lore.kernel.org/lkml/20190606092225.17960-3-abhsahu@nvidia.com Signed-off-by: Abhishek Sahu [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d9c162df9d52..c66c0ca446c4 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4985,6 +4985,32 @@ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMD, PCI_ANY_ID, DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda); +/* + * Create device link for NVIDIA GPU with integrated USB xHCI Host + * controller to VGA. + */ +static void quirk_gpu_usb(struct pci_dev *usb) +{ + pci_create_device_link(usb, 2, 0, PCI_BASE_CLASS_DISPLAY, 16); +} +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_CLASS_SERIAL_USB, 8, quirk_gpu_usb); + +/* + * Create device link for NVIDIA GPU with integrated Type-C UCSI controller + * to VGA. Currently there is no class code defined for UCSI device over PCI + * so using UNKNOWN class for now and it will be updated when UCSI + * over PCI gets a class code. + */ +#define PCI_CLASS_SERIAL_UNKNOWN 0x0c80 +static void quirk_gpu_usb_typec_ucsi(struct pci_dev *ucsi) +{ + pci_create_device_link(ucsi, 3, 0, PCI_BASE_CLASS_DISPLAY, 16); +} +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_CLASS_SERIAL_UNKNOWN, 8, + quirk_gpu_usb_typec_ucsi); + /* * Some IDT switches incorrectly flag an ACS Source Validation error on * completions for config read requests even though PCIe r4.0, sec -- cgit v1.2.3-55-g7522 From 2d2f4273cbe9058d1f5a518e5e880d27d7b3b30f Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 9 May 2019 13:27:22 -0600 Subject: PCI: Always allow probing with driver_override Commit 0e7df22401a3 ("PCI: Add sysfs sriov_drivers_autoprobe to control VF driver binding") introduced the sriov_drivers_autoprobe attribute which allows users to prevent the kernel from automatically probing a driver for new VFs as they are created. This allows VFs to be spawned without automatically binding the new device to a host driver, such as in cases where the user intends to use the device only with a meta driver like vfio-pci. However, the current implementation prevents any use of drivers_probe with the VF while sriov_drivers_autoprobe=0. This blocks the now current general practice of setting driver_override followed by using drivers_probe to bind a device to a specified driver. The kernel never automatically sets a driver_override therefore it seems we can assume a driver_override reflects the intent of the user. Also, probing a device using a driver_override match seems outside the scope of the 'auto' part of sriov_drivers_autoprobe. Therefore, let's allow driver_override matches regardless of sriov_drivers_autoprobe, which we can do by simply testing if a driver_override is set for a device as a 'can probe' condition. Fixes: 0e7df22401a3 ("PCI: Add sysfs sriov_drivers_autoprobe to control VF driver binding") Link: https://lore.kernel.org/lkml/155742996741.21878.569845487290798703.stgit@gimli.home Link: https://lore.kernel.org/linux-pci/155672991496.20698.4279330795743262888.stgit@gimli.home/T/#u Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 53874aae3873..b6a3a51801f0 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -399,7 +399,8 @@ void __weak pcibios_free_irq(struct pci_dev *dev) #ifdef CONFIG_PCI_IOV static inline bool pci_device_can_probe(struct pci_dev *pdev) { - return (!pdev->is_virtfn || pdev->physfn->sriov->drivers_autoprobe); + return (!pdev->is_virtfn || pdev->physfn->sriov->drivers_autoprobe || + pdev->driver_override); } #else static inline bool pci_device_can_probe(struct pci_dev *pdev) -- cgit v1.2.3-55-g7522 From de76cda215d56256ffcda7ffa538b70f9fb301a7 Mon Sep 17 00:00:00 2001 From: Gustavo Pimentel Date: Tue, 4 Jun 2019 18:24:43 +0200 Subject: PCI: Decode PCIe 32 GT/s link speed PCIe r5.0, sec 7.5.3.18, defines a new 32.0 GT/s bit in the Supported Link Speeds Vector of Link Capabilities 2. Decode this new speed. This does not affect the speed of the link, which should be negotiated automatically by the hardware; it only adds decoding when showing the speed to the user. Previously, reading the speed of a link operating at this speed showed "Unknown speed" instead of "32.0 GT/s". Link: https://lore.kernel.org/lkml/92365e3caf0fc559f9ab14bcd053bfc92d4f661c.1559664969.git.gustavo.pimentel@synopsys.com Signed-off-by: Gustavo Pimentel [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-sysfs.c | 3 +++ drivers/pci/pci.c | 4 +++- drivers/pci/probe.c | 2 +- drivers/pci/slot.c | 1 + include/linux/pci.h | 1 + include/uapi/linux/pci_regs.h | 4 ++++ 6 files changed, 13 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 6d27475e39b2..d52d30448e41 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -182,6 +182,9 @@ static ssize_t current_link_speed_show(struct device *dev, return -EINVAL; switch (linkstat & PCI_EXP_LNKSTA_CLS) { + case PCI_EXP_LNKSTA_CLS_32_0GB: + speed = "32 GT/s"; + break; case PCI_EXP_LNKSTA_CLS_16_0GB: speed = "16 GT/s"; break; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8abc843b1615..4729a7c7a9d9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5621,7 +5621,9 @@ enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev) */ pcie_capability_read_dword(dev, PCI_EXP_LNKCAP2, &lnkcap2); if (lnkcap2) { /* PCIe r3.0-compliant */ - if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_16_0GB) + if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_32_0GB) + return PCIE_SPEED_32_0GT; + else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_16_0GB) return PCIE_SPEED_16_0GT; else if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) return PCIE_SPEED_8_0GT; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0e8e2c186f50..c5f27c8cd140 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -668,7 +668,7 @@ const unsigned char pcie_link_speed[] = { PCIE_SPEED_5_0GT, /* 2 */ PCIE_SPEED_8_0GT, /* 3 */ PCIE_SPEED_16_0GT, /* 4 */ - PCI_SPEED_UNKNOWN, /* 5 */ + PCIE_SPEED_32_0GT, /* 5 */ PCI_SPEED_UNKNOWN, /* 6 */ PCI_SPEED_UNKNOWN, /* 7 */ PCI_SPEED_UNKNOWN, /* 8 */ diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index f4d92b1afe7b..ae4aa0e1f2f4 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -75,6 +75,7 @@ static const char *pci_bus_speed_strings[] = { "5.0 GT/s PCIe", /* 0x15 */ "8.0 GT/s PCIe", /* 0x16 */ "16.0 GT/s PCIe", /* 0x17 */ + "32.0 GT/s PCIe", /* 0x18 */ }; static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf) diff --git a/include/linux/pci.h b/include/linux/pci.h index 4a5a84d7bdd4..2173e6b75579 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -258,6 +258,7 @@ enum pci_bus_speed { PCIE_SPEED_5_0GT = 0x15, PCIE_SPEED_8_0GT = 0x16, PCIE_SPEED_16_0GT = 0x17, + PCIE_SPEED_32_0GT = 0x18, PCI_SPEED_UNKNOWN = 0xff, }; diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 27164769d184..f28e562d7ca8 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -528,6 +528,7 @@ #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ #define PCI_EXP_LNKCAP_SLS_16_0GB 0x00000004 /* LNKCAP2 SLS Vector bit 3 */ +#define PCI_EXP_LNKCAP_SLS_32_0GB 0x00000005 /* LNKCAP2 SLS Vector bit 4 */ #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ #define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ #define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ @@ -556,6 +557,7 @@ #define PCI_EXP_LNKSTA_CLS_5_0GB 0x0002 /* Current Link Speed 5.0GT/s */ #define PCI_EXP_LNKSTA_CLS_8_0GB 0x0003 /* Current Link Speed 8.0GT/s */ #define PCI_EXP_LNKSTA_CLS_16_0GB 0x0004 /* Current Link Speed 16.0GT/s */ +#define PCI_EXP_LNKSTA_CLS_32_0GB 0x0005 /* Current Link Speed 32.0GT/s */ #define PCI_EXP_LNKSTA_NLW 0x03f0 /* Negotiated Link Width */ #define PCI_EXP_LNKSTA_NLW_X1 0x0010 /* Current Link Width x1 */ #define PCI_EXP_LNKSTA_NLW_X2 0x0020 /* Current Link Width x2 */ @@ -661,6 +663,7 @@ #define PCI_EXP_LNKCAP2_SLS_5_0GB 0x00000004 /* Supported Speed 5GT/s */ #define PCI_EXP_LNKCAP2_SLS_8_0GB 0x00000008 /* Supported Speed 8GT/s */ #define PCI_EXP_LNKCAP2_SLS_16_0GB 0x00000010 /* Supported Speed 16GT/s */ +#define PCI_EXP_LNKCAP2_SLS_32_0GB 0x00000020 /* Supported Speed 32GT/s */ #define PCI_EXP_LNKCAP2_CROSSLINK 0x00000100 /* Crosslink supported */ #define PCI_EXP_LNKCTL2 48 /* Link Control 2 */ #define PCI_EXP_LNKCTL2_TLS 0x000f @@ -668,6 +671,7 @@ #define PCI_EXP_LNKCTL2_TLS_5_0GT 0x0002 /* Supported Speed 5GT/s */ #define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Supported Speed 8GT/s */ #define PCI_EXP_LNKCTL2_TLS_16_0GT 0x0004 /* Supported Speed 16GT/s */ +#define PCI_EXP_LNKCTL2_TLS_32_0GT 0x0005 /* Supported Speed 32GT/s */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */ #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ -- cgit v1.2.3-55-g7522 From c369b536f8e309691a8ac3f9264941867b4a7155 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Mon, 1 Apr 2019 15:12:39 +0200 Subject: PCI: armada8k: Add PHYs support Bring PHY support for the Armada8k driver. The Armada8k IP only supports x1, x2 or x4 link widths. Iterate over the DT 'phys' entries and configure them one by one. Use phy_set_mode_ext() to make use of the submode parameter (initially introduced for Ethernet modes). For PCI configuration, let the submode be the width (1, 2, 4, etc) so that the PHY driver knows how many lanes are bundled. Do not error out in case of error for compatibility reasons. Signed-off-by: Miquel Raynal Signed-off-by: Lorenzo Pieralisi Reviewed-by: Thomas Petazzoni --- drivers/pci/controller/dwc/pcie-armada8k.c | 82 +++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index 0c389a30ef5d..e567a7cfa3d7 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -25,10 +25,14 @@ #include "pcie-designware.h" +#define ARMADA8K_PCIE_MAX_LANES PCIE_LNK_X4 + struct armada8k_pcie { struct dw_pcie *pci; struct clk *clk; struct clk *clk_reg; + struct phy *phy[ARMADA8K_PCIE_MAX_LANES]; + unsigned int phy_count; }; #define PCIE_VENDOR_REGS_OFFSET 0x8000 @@ -67,6 +71,76 @@ struct armada8k_pcie { #define to_armada8k_pcie(x) dev_get_drvdata((x)->dev) +static void armada8k_pcie_disable_phys(struct armada8k_pcie *pcie) +{ + int i; + + for (i = 0; i < ARMADA8K_PCIE_MAX_LANES; i++) { + phy_power_off(pcie->phy[i]); + phy_exit(pcie->phy[i]); + } +} + +static int armada8k_pcie_enable_phys(struct armada8k_pcie *pcie) +{ + int ret; + int i; + + for (i = 0; i < ARMADA8K_PCIE_MAX_LANES; i++) { + ret = phy_init(pcie->phy[i]); + if (ret) + return ret; + + ret = phy_set_mode_ext(pcie->phy[i], PHY_MODE_PCIE, + pcie->phy_count); + if (ret) { + phy_exit(pcie->phy[i]); + return ret; + } + + ret = phy_power_on(pcie->phy[i]); + if (ret) { + phy_exit(pcie->phy[i]); + return ret; + } + } + + return 0; +} + +static int armada8k_pcie_setup_phys(struct armada8k_pcie *pcie) +{ + struct dw_pcie *pci = pcie->pci; + struct device *dev = pci->dev; + struct device_node *node = dev->of_node; + int ret = 0; + int i; + + for (i = 0; i < ARMADA8K_PCIE_MAX_LANES; i++) { + pcie->phy[i] = devm_of_phy_get_by_index(dev, node, i); + if (IS_ERR(pcie->phy[i]) && + (PTR_ERR(pcie->phy[i]) == -EPROBE_DEFER)) + return PTR_ERR(pcie->phy[i]); + + if (IS_ERR(pcie->phy[i])) { + pcie->phy[i] = NULL; + continue; + } + + pcie->phy_count++; + } + + /* Old bindings miss the PHY handle, so just warn if there is no PHY */ + if (!pcie->phy_count) + dev_warn(dev, "No available PHY\n"); + + ret = armada8k_pcie_enable_phys(pcie); + if (ret) + dev_err(dev, "Failed to initialize PHY(s) (%d)\n", ret); + + return ret; +} + static int armada8k_pcie_link_up(struct dw_pcie *pci) { u32 reg; @@ -249,14 +323,20 @@ static int armada8k_pcie_probe(struct platform_device *pdev) goto fail_clkreg; } + ret = armada8k_pcie_setup_phys(pcie); + if (ret) + goto fail_clkreg; + platform_set_drvdata(pdev, pcie); ret = armada8k_add_pcie_port(pcie, pdev); if (ret) - goto fail_clkreg; + goto disable_phy; return 0; +disable_phy: + armada8k_pcie_disable_phys(pcie); fail_clkreg: clk_disable_unprepare(pcie->clk_reg); fail: -- cgit v1.2.3-55-g7522 From 7a28db0a251ebc932a37f53a74c76e067aaac542 Mon Sep 17 00:00:00 2001 From: Ley Foon Tan Date: Wed, 12 Jun 2019 14:42:00 +0800 Subject: PCI: altera: Fix configuration type based on secondary number Stratix 10 PCIe controller does not support Type 1 to Type 0 conversion as previous version (V1) does so the PCIe controller configuration mechanism needs to send Type 0 config TLP if the target bus number matches with the secondary bus number. Implement a function to form a TLP header that depends on the PCIe controller version, so that the header can be formed according to specific host controller HW internals, fixing the type conversion issue. Signed-off-by: Ley Foon Tan [lorenzo.pieralisi@arm.com: commit log] Signed-off-by: Lorenzo Pieralisi --- drivers/pci/controller/pcie-altera.c | 41 ++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 14 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-altera.c b/drivers/pci/controller/pcie-altera.c index 27222071ace7..d2497ca43828 100644 --- a/drivers/pci/controller/pcie-altera.c +++ b/drivers/pci/controller/pcie-altera.c @@ -44,6 +44,8 @@ #define S10_RP_RXCPL_STATUS 0x200C #define S10_RP_CFG_ADDR(pcie, reg) \ (((pcie)->hip_base) + (reg) + (1 << 20)) +#define S10_RP_SECONDARY(pcie) \ + readb(S10_RP_CFG_ADDR(pcie, PCI_SECONDARY_BUS)) /* TLP configuration type 0 and 1 */ #define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ @@ -55,14 +57,9 @@ #define TLP_WRITE_TAG 0x10 #define RP_DEVFN 0 #define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) -#define TLP_CFGRD_DW0(pcie, bus) \ - ((((bus == pcie->root_bus_nr) ? pcie->pcie_data->cfgrd0 \ - : pcie->pcie_data->cfgrd1) << 24) | \ - TLP_PAYLOAD_SIZE) -#define TLP_CFGWR_DW0(pcie, bus) \ - ((((bus == pcie->root_bus_nr) ? pcie->pcie_data->cfgwr0 \ - : pcie->pcie_data->cfgwr1) << 24) | \ - TLP_PAYLOAD_SIZE) +#define TLP_CFG_DW0(pcie, cfg) \ + (((cfg) << 24) | \ + TLP_PAYLOAD_SIZE) #define TLP_CFG_DW1(pcie, tag, be) \ (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be)) #define TLP_CFG_DW2(bus, devfn, offset) \ @@ -322,14 +319,31 @@ static void s10_tlp_write_packet(struct altera_pcie *pcie, u32 *headers, s10_tlp_write_tx(pcie, data, RP_TX_EOP); } +static void get_tlp_header(struct altera_pcie *pcie, u8 bus, u32 devfn, + int where, u8 byte_en, bool read, u32 *headers) +{ + u8 cfg; + u8 cfg0 = read ? pcie->pcie_data->cfgrd0 : pcie->pcie_data->cfgwr0; + u8 cfg1 = read ? pcie->pcie_data->cfgrd1 : pcie->pcie_data->cfgwr1; + u8 tag = read ? TLP_READ_TAG : TLP_WRITE_TAG; + + if (pcie->pcie_data->version == ALTERA_PCIE_V1) + cfg = (bus == pcie->root_bus_nr) ? cfg0 : cfg1; + else + cfg = (bus > S10_RP_SECONDARY(pcie)) ? cfg0 : cfg1; + + headers[0] = TLP_CFG_DW0(pcie, cfg); + headers[1] = TLP_CFG_DW1(pcie, tag, byte_en); + headers[2] = TLP_CFG_DW2(bus, devfn, where); +} + static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn, int where, u8 byte_en, u32 *value) { u32 headers[TLP_HDR_SIZE]; - headers[0] = TLP_CFGRD_DW0(pcie, bus); - headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en); - headers[2] = TLP_CFG_DW2(bus, devfn, where); + get_tlp_header(pcie, bus, devfn, where, byte_en, true, + headers); pcie->pcie_data->ops->tlp_write_pkt(pcie, headers, 0, false); @@ -342,9 +356,8 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, u32 headers[TLP_HDR_SIZE]; int ret; - headers[0] = TLP_CFGWR_DW0(pcie, bus); - headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en); - headers[2] = TLP_CFG_DW2(bus, devfn, where); + get_tlp_header(pcie, bus, devfn, where, byte_en, false, + headers); /* check alignment to Qword */ if ((where & 0x7) == 0) -- cgit v1.2.3-55-g7522 From 1056dda8a8d662622fee123c4cc1845c8a8ea4bf Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:41 +0530 Subject: PCI: tegra: Handle failure cases in tegra_pcie_power_on() Unroll the PCIe power on sequence if any one of the steps fails in tegra_pcie_power_on(). Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 464ba2538d52..85c5e6a1e529 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -1052,7 +1052,7 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) err = clk_prepare_enable(pcie->pex_clk); if (err) { dev_err(dev, "failed to enable PEX clock: %d\n", err); - return err; + goto regulator_disable; } reset_control_deassert(pcie->pex_rst); } else { @@ -1061,7 +1061,7 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) pcie->pex_rst); if (err) { dev_err(dev, "powerup sequence failed: %d\n", err); - return err; + goto regulator_disable; } } @@ -1070,24 +1070,40 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) err = clk_prepare_enable(pcie->afi_clk); if (err < 0) { dev_err(dev, "failed to enable AFI clock: %d\n", err); - return err; + goto powergate; } if (soc->has_cml_clk) { err = clk_prepare_enable(pcie->cml_clk); if (err < 0) { dev_err(dev, "failed to enable CML clock: %d\n", err); - return err; + goto disable_afi_clk; } } err = clk_prepare_enable(pcie->pll_e); if (err < 0) { dev_err(dev, "failed to enable PLLE clock: %d\n", err); - return err; + goto disable_cml_clk; } return 0; + +disable_cml_clk: + if (soc->has_cml_clk) + clk_disable_unprepare(pcie->cml_clk); +disable_afi_clk: + clk_disable_unprepare(pcie->afi_clk); +powergate: + reset_control_assert(pcie->afi_rst); + reset_control_assert(pcie->pex_rst); + clk_disable_unprepare(pcie->pex_clk); + if (!dev->pm_domain) + tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); +regulator_disable: + regulator_bulk_disable(pcie->num_supplies, pcie->supplies); + + return err; } static int tegra_pcie_clocks_get(struct tegra_pcie *pcie) -- cgit v1.2.3-55-g7522 From 973d7499c51c105c739ed82673965e14ff793867 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:42 +0530 Subject: PCI: tegra: Rearrange Tegra PCIe driver functions Tegra PCIe has register specifications for: - AXI to FPCI(AFI) bridge - Multiple PCIe root ports - PCIe PHY - PCIe pad control Rearrange Tegra PCIe driver functions so that each function programs the required module only. - tegra_pcie_enable_controller(): Program AFI module and enable PCIe controller - tegra_pcie_phy_power_on(): Bring up PCIe PHY - tegra_pcie_apply_pad_settings(): Program PCIe REFCLK pad settings - tegra_pcie_enable_ports(): Program each root port and bring up PCIe link Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 70 ++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 40 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 85c5e6a1e529..5d93bb32ad44 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -852,7 +852,6 @@ static int tegra_pcie_port_phy_power_off(struct tegra_pcie_port *port) static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; - const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_pcie_port *port; int err; @@ -878,12 +877,6 @@ static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie) } } - /* Configure the reference clock driver */ - pads_writel(pcie, soc->pads_refclk_cfg0, PADS_REFCLK_CFG0); - - if (soc->num_ports > 2) - pads_writel(pcie, soc->pads_refclk_cfg1, PADS_REFCLK_CFG1); - return 0; } @@ -918,13 +911,11 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie) return 0; } -static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) +static void tegra_pcie_enable_controller(struct tegra_pcie *pcie) { - struct device *dev = pcie->dev; const struct tegra_pcie_soc *soc = pcie->soc; struct tegra_pcie_port *port; unsigned long value; - int err; /* enable PLL power down */ if (pcie->phy) { @@ -958,14 +949,6 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) afi_writel(pcie, value, AFI_FUSE); } - if (soc->program_uphy) { - err = tegra_pcie_phy_power_on(pcie); - if (err < 0) { - dev_err(dev, "failed to power on PHY(s): %d\n", err); - return err; - } - } - /* take the PCIe interface module out of reset */ reset_control_deassert(pcie->pcie_xrst); @@ -989,22 +972,6 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie) /* disable all exceptions */ afi_writel(pcie, 0, AFI_FPCI_ERROR_MASKS); - - return 0; -} - -static void tegra_pcie_disable_controller(struct tegra_pcie *pcie) -{ - int err; - - reset_control_assert(pcie->pcie_xrst); - - if (pcie->soc->program_uphy) { - err = tegra_pcie_phy_power_off(pcie); - if (err < 0) - dev_err(pcie->dev, "failed to power off PHY(s): %d\n", - err); - } } static void tegra_pcie_power_off(struct tegra_pcie *pcie) @@ -1106,6 +1073,17 @@ regulator_disable: return err; } +static void tegra_pcie_apply_pad_settings(struct tegra_pcie *pcie) +{ + const struct tegra_pcie_soc *soc = pcie->soc; + + /* Configure the reference clock driver */ + pads_writel(pcie, soc->pads_refclk_cfg0, PADS_REFCLK_CFG0); + + if (soc->num_ports > 2) + pads_writel(pcie, soc->pads_refclk_cfg1, PADS_REFCLK_CFG1); +} + static int tegra_pcie_clocks_get(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; @@ -2501,16 +2479,23 @@ static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) { struct tegra_pcie *pcie = dev_get_drvdata(dev); struct tegra_pcie_port *port; + int err; list_for_each_entry(port, &pcie->ports, list) tegra_pcie_pme_turnoff(port); tegra_pcie_disable_ports(pcie); + if (pcie->soc->program_uphy) { + err = tegra_pcie_phy_power_off(pcie); + if (err < 0) + dev_err(dev, "failed to power off PHY(s): %d\n", err); + } + if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_disable_msi(pcie); - tegra_pcie_disable_controller(pcie); + reset_control_assert(pcie->pcie_xrst); tegra_pcie_power_off(pcie); return 0; @@ -2526,16 +2511,21 @@ static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) dev_err(dev, "tegra pcie power on fail: %d\n", err); return err; } - err = tegra_pcie_enable_controller(pcie); - if (err) { - dev_err(dev, "tegra pcie controller enable fail: %d\n", err); - goto poweroff; - } + tegra_pcie_enable_controller(pcie); tegra_pcie_setup_translations(pcie); if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_enable_msi(pcie); + if (pcie->soc->program_uphy) { + err = tegra_pcie_phy_power_on(pcie); + if (err < 0) { + dev_err(dev, "failed to power on PHY(s): %d\n", err); + goto poweroff; + } + } + + tegra_pcie_apply_pad_settings(pcie); tegra_pcie_enable_ports(pcie); return 0; -- cgit v1.2.3-55-g7522 From 316b9ef1ee14c405f25923bf5831c0bd4bebc47a Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:43 +0530 Subject: PCI: tegra: Mask AFI_INTR in runtime suspend AFI_INTR is unmasked in tegra_pcie_enable_controller(), mask it to avoid unwanted interrupts raised by AFI after pex_rst is asserted. The following sequence triggers such scenario: - tegra_pcie_remove() triggers runtime suspend - pex_rst is asserted in runtime suspend - PRSNT_MAP bit field in RP_PRIV_MISC register changes from EP_PRSNT to EP_ABSNT - This is sensed by AFI and triggers "Slot present pin change" interrupt - tegra_pcie_isr() function accesses AFI register when runtime suspend is going through power off sequence Resulting faulty backtrace: rmmod pci-tegra pci_generic_config_write32: 108 callbacks suppressed pci_bus 0002:00: 2-byte config write to 0002:00:02.0 offset 0x4c may corrupt adjacent RW1C bits pci_bus 0002:00: 2-byte config write to 0002:00:02.0 offset 0x9c may corrupt adjacent RW1C bits pci_bus 0002:00: 2-byte config write to 0002:00:02.0 offset 0x88 may corrupt adjacent RW1C bits pci_bus 0002:00: 2-byte config write to 0002:00:02.0 offset 0x90 may corrupt adjacent RW1C bits pci_bus 0002:00: 2-byte config write to 0002:00:02.0 offset 0x4 may corrupt adjacent RW1C bits igb 0002:04:00.1: removed PHC on enP2p4s0f1 igb 0002:04:00.0: removed PHC on enP2p4s0f0 pci_bus 0002:00: 2-byte config write to 0002:00:01.0 offset 0x4c may corrupt adjacent RW1C bits pci_bus 0002:00: 2-byte config write to 0002:00:01.0 offset 0x9c may corrupt adjacent RW1C bits pci_bus 0002:00: 2-byte config write to 0002:00:01.0 offset 0x88 may corrupt adjacent RW1C bits pci_bus 0002:00: 2-byte config write to 0002:00:01.0 offset 0x90 may corrupt adjacent RW1C bits pci_bus 0002:00: 2-byte config write to 0002:00:01.0 offset 0x4 may corrupt adjacent RW1C bits rcu: INFO: rcu_preempt self-detected stall on CPU SError Interrupt on CPU0, code 0xbf000002 -- SError CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 5.1.0-rc3-next-20190405-00027-gcd8110499e6f-dirty #42 Hardware name: NVIDIA Jetson TX1 Developer Kit (DT) pstate: 20000085 (nzCv daIf -PAN -UAO) pc : tegra_pcie_isr+0x58/0x178 [pci_tegra] lr : tegra_pcie_isr+0x40/0x178 [pci_tegra] sp : ffff000010003da0 x29: ffff000010003da0 x28: 0000000000000000 x27: ffff8000f9e61000 x26: ffff000010fbf420 x25: ffff000011427f93 x24: ffff8000fa600410 x23: ffff00001129d000 x22: ffff00001129d000 x21: ffff8000f18bf3c0 x20: 0000000000000070 x19: 00000000ffffffff x18: 0000000000000000 x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 x14: ffff000008d40a48 x13: ffff000008d40a30 x12: ffff000008d40a20 x11: ffff000008d40a10 x10: ffff000008d40a00 x9 : ffff000008d409e8 x8 : ffff000008d40ae8 x7 : ffff000008d40ad0 x6 : ffff000010003e58 x5 : ffff8000fac00248 x4 : 0000000000000000 x3 : ffff000008d40b08 x2 : fffffffffffffff8 x1 : ffff000008d3f4e8 x0 : 00000000ffffffff Kernel panic - not syncing: Asynchronous SError Interrupt CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 5.1.0-rc3-next-20190405-00027-gcd8110499e6f-dirty #42 Hardware name: NVIDIA Jetson TX1 Developer Kit (DT) Call trace: dump_backtrace+0x0/0x158 show_stack+0x14/0x20 dump_stack+0xa8/0xcc panic+0x140/0x2f4 nmi_panic+0x6c/0x70 arm64_serror_panic+0x74/0x80 __pte_error+0x0/0x28 el1_error+0x84/0xf8 tegra_pcie_isr+0x58/0x178 [pci_tegra] __handle_irq_event_percpu+0x70/0x198 handle_irq_event_percpu+0x34/0x88 handle_irq_event+0x48/0x78 handle_fasteoi_irq+0xb4/0x190 generic_handle_irq+0x24/0x38 __handle_domain_irq+0x5c/0xb8 gic_handle_irq+0x58/0xa8 el1_irq+0xb8/0x180 cpuidle_enter_state+0x138/0x358 cpuidle_enter+0x18/0x20 call_cpuidle+0x1c/0x48 do_idle+0x230/0x2d0 cpu_startup_entry+0x20/0x28 rest_init+0xd4/0xe0 arch_call_rest_init+0xc/0x14 start_kernel+0x444/0x470 AFI_INTR is re-enabled on resume in tegra_pcie_pm_resume() through tegra_pcie_enable_controller(). Signed-off-by: Manikanta Maddireddy [lorenzo.pieralisi@arm.com: updated log] Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 5d93bb32ad44..b823a6391b8f 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -1641,6 +1641,15 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie) return 0; } +static void tegra_pcie_disable_interrupts(struct tegra_pcie *pcie) +{ + u32 value; + + value = afi_readl(pcie, AFI_INTR_MASK); + value &= ~AFI_INTR_MASK_INT_MASK; + afi_writel(pcie, value, AFI_INTR_MASK); +} + static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes, u32 *xbar) { @@ -2486,6 +2495,12 @@ static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) tegra_pcie_disable_ports(pcie); + /* + * AFI_INTR is unmasked in tegra_pcie_enable_controller(), mask it to + * avoid unwanted interrupts raised by AFI after pex_rst is asserted. + */ + tegra_pcie_disable_interrupts(pcie); + if (pcie->soc->program_uphy) { err = tegra_pcie_phy_power_off(pcie); if (err < 0) -- cgit v1.2.3-55-g7522 From d1f9113faf8ade46d48c6277cc3914e1422f3a4b Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:44 +0530 Subject: PCI: tegra: Fix PCIe host power up sequence The PCIe host power up sequence requires to program AFI(AXI to FPCI bridge) registers first and then PCIe registers, otherwise AFI register settings may not latch to PCIe IP. PCIe root port starts LTSSM as soon as PCIe xrst is deasserted. So deassert PCIe xrst after programming PCIe registers. Modify PCIe power up sequence as follows: - Power ungate PCIe partition - Enable AFI clock - Deassert AFI reset - Program AFI registers - Enable PCIe clock - Deassert PCIe reset - Program PCIe PHY - Program PCIe pad control registers - Program PCIe root port registers - Deassert PCIe xrst to start LTSSM Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 52 ++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 22 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index b823a6391b8f..c86e65708908 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -949,9 +949,6 @@ static void tegra_pcie_enable_controller(struct tegra_pcie *pcie) afi_writel(pcie, value, AFI_FUSE); } - /* take the PCIe interface module out of reset */ - reset_control_deassert(pcie->pcie_xrst); - /* finally enable PCIe */ value = afi_readl(pcie, AFI_CONFIGURATION); value |= AFI_CONFIGURATION_EN_FPCI; @@ -981,13 +978,11 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie) int err; reset_control_assert(pcie->afi_rst); - reset_control_assert(pcie->pex_rst); clk_disable_unprepare(pcie->pll_e); if (soc->has_cml_clk) clk_disable_unprepare(pcie->cml_clk); clk_disable_unprepare(pcie->afi_clk); - clk_disable_unprepare(pcie->pex_clk); if (!dev->pm_domain) tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); @@ -1015,25 +1010,19 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) if (err < 0) dev_err(dev, "failed to enable regulators: %d\n", err); - if (dev->pm_domain) { - err = clk_prepare_enable(pcie->pex_clk); + if (!dev->pm_domain) { + err = tegra_powergate_power_on(TEGRA_POWERGATE_PCIE); if (err) { - dev_err(dev, "failed to enable PEX clock: %d\n", err); + dev_err(dev, "failed to power ungate: %d\n", err); goto regulator_disable; } - reset_control_deassert(pcie->pex_rst); - } else { - err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE, - pcie->pex_clk, - pcie->pex_rst); + err = tegra_powergate_remove_clamping(TEGRA_POWERGATE_PCIE); if (err) { - dev_err(dev, "powerup sequence failed: %d\n", err); - goto regulator_disable; + dev_err(dev, "failed to remove clamp: %d\n", err); + goto powergate; } } - reset_control_deassert(pcie->afi_rst); - err = clk_prepare_enable(pcie->afi_clk); if (err < 0) { dev_err(dev, "failed to enable AFI clock: %d\n", err); @@ -1054,6 +1043,8 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie) goto disable_cml_clk; } + reset_control_deassert(pcie->afi_rst); + return 0; disable_cml_clk: @@ -1062,9 +1053,6 @@ disable_cml_clk: disable_afi_clk: clk_disable_unprepare(pcie->afi_clk); powergate: - reset_control_assert(pcie->afi_rst); - reset_control_assert(pcie->pex_rst); - clk_disable_unprepare(pcie->pex_clk); if (!dev->pm_domain) tegra_powergate_power_off(TEGRA_POWERGATE_PCIE); regulator_disable: @@ -2130,7 +2118,12 @@ static void tegra_pcie_enable_ports(struct tegra_pcie *pcie) port->index, port->lanes); tegra_pcie_port_enable(port); + } + /* Start LTSSM from Tegra side */ + reset_control_deassert(pcie->pcie_xrst); + + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { if (tegra_pcie_port_check_link(port)) continue; @@ -2145,6 +2138,8 @@ static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) { struct tegra_pcie_port *port, *tmp; + reset_control_assert(pcie->pcie_xrst); + list_for_each_entry_safe(port, tmp, &pcie->ports, list) tegra_pcie_port_disable(port); } @@ -2507,10 +2502,12 @@ static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) dev_err(dev, "failed to power off PHY(s): %d\n", err); } + reset_control_assert(pcie->pex_rst); + clk_disable_unprepare(pcie->pex_clk); + if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_disable_msi(pcie); - reset_control_assert(pcie->pcie_xrst); tegra_pcie_power_off(pcie); return 0; @@ -2532,11 +2529,19 @@ static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_enable_msi(pcie); + err = clk_prepare_enable(pcie->pex_clk); + if (err) { + dev_err(dev, "failed to enable PEX clock: %d\n", err); + goto poweroff; + } + + reset_control_deassert(pcie->pex_rst); + if (pcie->soc->program_uphy) { err = tegra_pcie_phy_power_on(pcie); if (err < 0) { dev_err(dev, "failed to power on PHY(s): %d\n", err); - goto poweroff; + goto disable_pex_clk; } } @@ -2545,6 +2550,9 @@ static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) return 0; +disable_pex_clk: + reset_control_assert(pcie->pex_rst); + clk_disable_unprepare(pcie->pex_clk); poweroff: tegra_pcie_power_off(pcie); -- cgit v1.2.3-55-g7522 From 538123a29aebca0ba7c35d69bd4d2b91d9d81026 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:45 +0530 Subject: PCI: tegra: Add PCIe Gen2 link speed support Tegra124, Tegra132, Tegra210 and Tegra186 support Gen2 link speed. After PCIe link is up in Gen1, set target link speed as Gen2 and retrain link. Link switches to Gen2 speed if Gen2 capable end point is connected, otherwise the link stays in Gen1. Per PCIe 4.0r0.9 sec 7.6.3.7 implementation note, driver needs to wait for PCIe LTSSM to come back from recovery before retraining the link. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index c86e65708908..dfb13aee6322 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -191,6 +191,8 @@ #define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 #define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 +#define RP_LINK_CONTROL_STATUS_2 0x000000b0 + #define PADS_CTL_SEL 0x0000009c #define PADS_CTL 0x000000a0 @@ -226,6 +228,7 @@ #define PADS_REFCLK_CFG_DRVI_SHIFT 12 /* 15:12 */ #define PME_ACK_TIMEOUT 10000 +#define LINK_RETRAIN_TIMEOUT 100000 /* in usec */ struct tegra_msi { struct msi_controller chip; @@ -2108,6 +2111,64 @@ retry: return false; } +static void tegra_pcie_change_link_speed(struct tegra_pcie *pcie) +{ + struct device *dev = pcie->dev; + struct tegra_pcie_port *port; + ktime_t deadline; + u32 value; + + list_for_each_entry(port, &pcie->ports, list) { + /* + * "Supported Link Speeds Vector" in "Link Capabilities 2" + * is not supported by Tegra. tegra_pcie_change_link_speed() + * is called only for Tegra chips which support Gen2. + * So there no harm if supported link speed is not verified. + */ + value = readl(port->base + RP_LINK_CONTROL_STATUS_2); + value &= ~PCI_EXP_LNKSTA_CLS; + value |= PCI_EXP_LNKSTA_CLS_5_0GB; + writel(value, port->base + RP_LINK_CONTROL_STATUS_2); + + /* + * Poll until link comes back from recovery to avoid race + * condition. + */ + deadline = ktime_add_us(ktime_get(), LINK_RETRAIN_TIMEOUT); + + while (ktime_before(ktime_get(), deadline)) { + value = readl(port->base + RP_LINK_CONTROL_STATUS); + if ((value & PCI_EXP_LNKSTA_LT) == 0) + break; + + usleep_range(2000, 3000); + } + + if (value & PCI_EXP_LNKSTA_LT) + dev_warn(dev, "PCIe port %u link is in recovery\n", + port->index); + + /* Retrain the link */ + value = readl(port->base + RP_LINK_CONTROL_STATUS); + value |= PCI_EXP_LNKCTL_RL; + writel(value, port->base + RP_LINK_CONTROL_STATUS); + + deadline = ktime_add_us(ktime_get(), LINK_RETRAIN_TIMEOUT); + + while (ktime_before(ktime_get(), deadline)) { + value = readl(port->base + RP_LINK_CONTROL_STATUS); + if ((value & PCI_EXP_LNKSTA_LT) == 0) + break; + + usleep_range(2000, 3000); + } + + if (value & PCI_EXP_LNKSTA_LT) + dev_err(dev, "failed to retrain link of port %u\n", + port->index); + } +} + static void tegra_pcie_enable_ports(struct tegra_pcie *pcie) { struct device *dev = pcie->dev; @@ -2132,6 +2193,9 @@ static void tegra_pcie_enable_ports(struct tegra_pcie *pcie) tegra_pcie_port_disable(port); tegra_pcie_port_free(port); } + + if (pcie->soc->has_gen2) + tegra_pcie_change_link_speed(pcie); } static void tegra_pcie_disable_ports(struct tegra_pcie *pcie) -- cgit v1.2.3-55-g7522 From c635a815c8c73a985b0e723efd4ffd70e99729fc Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:46 +0530 Subject: PCI: tegra: Advertise PCIe Advanced Error Reporting (AER) capability Default root port setting hides AER capability. This patch enables the advertisement of AER capability by root port. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index dfb13aee6322..c9687b5a6794 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -180,6 +180,9 @@ #define RP_VEND_XP 0x00000f00 #define RP_VEND_XP_DL_UP (1 << 30) +#define RP_VEND_CTL1 0x00000f48 +#define RP_VEND_CTL1_ERPT (1 << 13) + #define RP_VEND_CTL2 0x00000fa8 #define RP_VEND_CTL2_PCA_ENABLE (1 << 7) @@ -479,6 +482,16 @@ static void tegra_pcie_port_reset(struct tegra_pcie_port *port) afi_writel(port->pcie, value, ctrl); } +static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port) +{ + u32 value; + + /* Enable AER capability */ + value = readl(port->base + RP_VEND_CTL1); + value |= RP_VEND_CTL1_ERPT; + writel(value, port->base + RP_VEND_CTL1); +} + static void tegra_pcie_port_enable(struct tegra_pcie_port *port) { unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); @@ -503,6 +516,8 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port) value |= RP_VEND_CTL2_PCA_ENABLE; writel(value, port->base + RP_VEND_CTL2); } + + tegra_pcie_enable_rp_features(port); } static void tegra_pcie_port_disable(struct tegra_pcie_port *port) -- cgit v1.2.3-55-g7522 From 2513a4ee47354e1d8b6b9d084cb5625813fb96d4 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:47 +0530 Subject: PCI: tegra: Program UPHY electrical settings for Tegra210 UPHY electrical programming guidelines are documented in Tegra210 TRM. Program these electrical settings for proper eye diagram in Gen1 and Gen2 link speeds. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 107 +++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index c9687b5a6794..2d0a23b2c7bb 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -177,6 +177,32 @@ #define AFI_PEXBIAS_CTRL_0 0x168 +#define RP_ECTL_2_R1 0x00000e84 +#define RP_ECTL_2_R1_RX_CTLE_1C_MASK 0xffff + +#define RP_ECTL_4_R1 0x00000e8c +#define RP_ECTL_4_R1_RX_CDR_CTRL_1C_MASK (0xffff << 16) +#define RP_ECTL_4_R1_RX_CDR_CTRL_1C_SHIFT 16 + +#define RP_ECTL_5_R1 0x00000e90 +#define RP_ECTL_5_R1_RX_EQ_CTRL_L_1C_MASK 0xffffffff + +#define RP_ECTL_6_R1 0x00000e94 +#define RP_ECTL_6_R1_RX_EQ_CTRL_H_1C_MASK 0xffffffff + +#define RP_ECTL_2_R2 0x00000ea4 +#define RP_ECTL_2_R2_RX_CTLE_1C_MASK 0xffff + +#define RP_ECTL_4_R2 0x00000eac +#define RP_ECTL_4_R2_RX_CDR_CTRL_1C_MASK (0xffff << 16) +#define RP_ECTL_4_R2_RX_CDR_CTRL_1C_SHIFT 16 + +#define RP_ECTL_5_R2 0x00000eb0 +#define RP_ECTL_5_R2_RX_EQ_CTRL_L_1C_MASK 0xffffffff + +#define RP_ECTL_6_R2 0x00000eb4 +#define RP_ECTL_6_R2_RX_EQ_CTRL_H_1C_MASK 0xffffffff + #define RP_VEND_XP 0x00000f00 #define RP_VEND_XP_DL_UP (1 << 30) @@ -266,6 +292,19 @@ struct tegra_pcie_soc { bool has_gen2; bool force_pca_enable; bool program_uphy; + struct { + struct { + u32 rp_ectl_2_r1; + u32 rp_ectl_4_r1; + u32 rp_ectl_5_r1; + u32 rp_ectl_6_r1; + u32 rp_ectl_2_r2; + u32 rp_ectl_4_r2; + u32 rp_ectl_5_r2; + u32 rp_ectl_6_r2; + } regs; + bool enable; + } ectl; }; static inline struct tegra_msi *to_tegra_msi(struct msi_controller *chip) @@ -492,6 +531,54 @@ static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port) writel(value, port->base + RP_VEND_CTL1); } +static void tegra_pcie_program_ectl_settings(struct tegra_pcie_port *port) +{ + const struct tegra_pcie_soc *soc = port->pcie->soc; + u32 value; + + value = readl(port->base + RP_ECTL_2_R1); + value &= ~RP_ECTL_2_R1_RX_CTLE_1C_MASK; + value |= soc->ectl.regs.rp_ectl_2_r1; + writel(value, port->base + RP_ECTL_2_R1); + + value = readl(port->base + RP_ECTL_4_R1); + value &= ~RP_ECTL_4_R1_RX_CDR_CTRL_1C_MASK; + value |= soc->ectl.regs.rp_ectl_4_r1 << + RP_ECTL_4_R1_RX_CDR_CTRL_1C_SHIFT; + writel(value, port->base + RP_ECTL_4_R1); + + value = readl(port->base + RP_ECTL_5_R1); + value &= ~RP_ECTL_5_R1_RX_EQ_CTRL_L_1C_MASK; + value |= soc->ectl.regs.rp_ectl_5_r1; + writel(value, port->base + RP_ECTL_5_R1); + + value = readl(port->base + RP_ECTL_6_R1); + value &= ~RP_ECTL_6_R1_RX_EQ_CTRL_H_1C_MASK; + value |= soc->ectl.regs.rp_ectl_6_r1; + writel(value, port->base + RP_ECTL_6_R1); + + value = readl(port->base + RP_ECTL_2_R2); + value &= ~RP_ECTL_2_R2_RX_CTLE_1C_MASK; + value |= soc->ectl.regs.rp_ectl_2_r2; + writel(value, port->base + RP_ECTL_2_R2); + + value = readl(port->base + RP_ECTL_4_R2); + value &= ~RP_ECTL_4_R2_RX_CDR_CTRL_1C_MASK; + value |= soc->ectl.regs.rp_ectl_4_r2 << + RP_ECTL_4_R2_RX_CDR_CTRL_1C_SHIFT; + writel(value, port->base + RP_ECTL_4_R2); + + value = readl(port->base + RP_ECTL_5_R2); + value &= ~RP_ECTL_5_R2_RX_EQ_CTRL_L_1C_MASK; + value |= soc->ectl.regs.rp_ectl_5_r2; + writel(value, port->base + RP_ECTL_5_R2); + + value = readl(port->base + RP_ECTL_6_R2); + value &= ~RP_ECTL_6_R2_RX_EQ_CTRL_H_1C_MASK; + value |= soc->ectl.regs.rp_ectl_6_r2; + writel(value, port->base + RP_ECTL_6_R2); +} + static void tegra_pcie_port_enable(struct tegra_pcie_port *port) { unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); @@ -518,6 +605,9 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port) } tegra_pcie_enable_rp_features(port); + + if (soc->ectl.enable) + tegra_pcie_program_ectl_settings(port); } static void tegra_pcie_port_disable(struct tegra_pcie_port *port) @@ -2242,6 +2332,7 @@ static const struct tegra_pcie_soc tegra20_pcie = { .has_gen2 = false, .force_pca_enable = false, .program_uphy = true, + .ectl.enable = false, }; static const struct tegra_pcie_port_soc tegra30_pcie_ports[] = { @@ -2265,6 +2356,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { .has_gen2 = false, .force_pca_enable = false, .program_uphy = true, + .ectl.enable = false, }; static const struct tegra_pcie_soc tegra124_pcie = { @@ -2281,6 +2373,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { .has_gen2 = true, .force_pca_enable = false, .program_uphy = true, + .ectl.enable = false, }; static const struct tegra_pcie_soc tegra210_pcie = { @@ -2297,6 +2390,19 @@ static const struct tegra_pcie_soc tegra210_pcie = { .has_gen2 = true, .force_pca_enable = true, .program_uphy = true, + .ectl = { + .regs = { + .rp_ectl_2_r1 = 0x0000000f, + .rp_ectl_4_r1 = 0x00000067, + .rp_ectl_5_r1 = 0x55010000, + .rp_ectl_6_r1 = 0x00000001, + .rp_ectl_2_r2 = 0x0000008f, + .rp_ectl_4_r2 = 0x000000c7, + .rp_ectl_5_r2 = 0x55010000, + .rp_ectl_6_r2 = 0x00000001, + }, + .enable = true, + }, }; static const struct tegra_pcie_port_soc tegra186_pcie_ports[] = { @@ -2320,6 +2426,7 @@ static const struct tegra_pcie_soc tegra186_pcie = { .has_gen2 = true, .force_pca_enable = false, .program_uphy = false, + .ectl.enable = false, }; static const struct of_device_id tegra_pcie_of_match[] = { -- cgit v1.2.3-55-g7522 From 7763cc24e2103a54eb43bbe46fca59ddab77c2f2 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:48 +0530 Subject: PCI: tegra: Enable opportunistic UpdateFC and ACK Enable opportunistic UpdateFC and ACK to allow data link layer send pending ACKs and UpdateFC packets when link is idle instead of waiting for timers to expire. This improves the PCIe performance due to better utilization of PCIe bandwidth. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 2d0a23b2c7bb..cd69c43c9a0c 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -204,7 +204,9 @@ #define RP_ECTL_6_R2_RX_EQ_CTRL_H_1C_MASK 0xffffffff #define RP_VEND_XP 0x00000f00 -#define RP_VEND_XP_DL_UP (1 << 30) +#define RP_VEND_XP_DL_UP (1 << 30) +#define RP_VEND_XP_OPPORTUNISTIC_ACK (1 << 27) +#define RP_VEND_XP_OPPORTUNISTIC_UPDATEFC (1 << 28) #define RP_VEND_CTL1 0x00000f48 #define RP_VEND_CTL1_ERPT (1 << 13) @@ -529,6 +531,12 @@ static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port) value = readl(port->base + RP_VEND_CTL1); value |= RP_VEND_CTL1_ERPT; writel(value, port->base + RP_VEND_CTL1); + + /* Optimal settings to enhance bandwidth */ + value = readl(port->base + RP_VEND_XP); + value |= RP_VEND_XP_OPPORTUNISTIC_ACK; + value |= RP_VEND_XP_OPPORTUNISTIC_UPDATEFC; + writel(value, port->base + RP_VEND_XP); } static void tegra_pcie_program_ectl_settings(struct tegra_pcie_port *port) -- cgit v1.2.3-55-g7522 From 92bd94f1fdde630e593a81930b45c9e3dc024d48 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:49 +0530 Subject: PCI: tegra: Disable AFI dynamic clock gating Outstanding write counter in AFI is used to generate idle signal to dynamically gate the AFI clock. When there are 32 outstanding writes from AFI to memory, the outstanding write counter overflows and indicates that there are "0" outstanding write transactions. When memory controller is under heavy load, write completions to AFI gets delayed and AFI write counter overflows. This causes AFI clock gating even when there are outstanding transactions towards memory controller resulting in a system hang. Disable dynamic clock gating of AFI clock to avoid system hang. CLKEN_OVERRIDE bit is not defined in Tegra20 and Tegra30, however programming this bit doesn't cause any side effects. Program this bit for all Tegra SoCs to avoid conditional check. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index cd69c43c9a0c..3238db2ac7f1 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -95,7 +95,8 @@ #define AFI_MSI_EN_VEC7 0xa8 #define AFI_CONFIGURATION 0xac -#define AFI_CONFIGURATION_EN_FPCI (1 << 0) +#define AFI_CONFIGURATION_EN_FPCI (1 << 0) +#define AFI_CONFIGURATION_CLKEN_OVERRIDE (1 << 31) #define AFI_FPCI_ERROR_MASKS 0xb0 @@ -1065,9 +1066,10 @@ static void tegra_pcie_enable_controller(struct tegra_pcie *pcie) afi_writel(pcie, value, AFI_FUSE); } - /* finally enable PCIe */ + /* Disable AFI dynamic clock gating and enable PCIe */ value = afi_readl(pcie, AFI_CONFIGURATION); value |= AFI_CONFIGURATION_EN_FPCI; + value |= AFI_CONFIGURATION_CLKEN_OVERRIDE; afi_writel(pcie, value, AFI_CONFIGURATION); value = AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR | -- cgit v1.2.3-55-g7522 From 52db2fd89e1a811c3bf63586a31fe4e454e59b56 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:50 +0530 Subject: PCI: tegra: Process pending DLL transactions before entering L1 or L2 PM message are truncated while entering L1 or L2, which is resulting in receiver errors. Set the required bit to finish processing DLLP before link enter L1 or L2. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 3238db2ac7f1..d079cde8d4bb 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -212,6 +212,9 @@ #define RP_VEND_CTL1 0x00000f48 #define RP_VEND_CTL1_ERPT (1 << 13) +#define RP_VEND_XP_BIST 0x00000f4c +#define RP_VEND_XP_BIST_GOTO_L1_L2_AFTER_DLLP_DONE (1 << 28) + #define RP_VEND_CTL2 0x00000fa8 #define RP_VEND_CTL2_PCA_ENABLE (1 << 7) @@ -538,6 +541,14 @@ static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port) value |= RP_VEND_XP_OPPORTUNISTIC_ACK; value |= RP_VEND_XP_OPPORTUNISTIC_UPDATEFC; writel(value, port->base + RP_VEND_XP); + + /* + * LTSSM will wait for DLLP to finish before entering L1 or L2, + * to avoid truncation of PM messages which results in receiver errors + */ + value = readl(port->base + RP_VEND_XP_BIST); + value |= RP_VEND_XP_BIST_GOTO_L1_L2_AFTER_DLLP_DONE; + writel(value, port->base + RP_VEND_XP_BIST); } static void tegra_pcie_program_ectl_settings(struct tegra_pcie_port *port) -- cgit v1.2.3-55-g7522 From f1178099a6e434d3ae676db4c13dbcc5211c2817 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:51 +0530 Subject: PCI: tegra: Enable PCIe xclk clock clamping Enable xclk clock clamping when entering L1. Clamp threshold will determine the time spent waiting for clock module to turn on xclk after signaling it. Default threshold value in Tegra124 and Tegra210 is not enough to turn on xclk clock. Increase the clamp threshold to meet the clock module timing in Tegra124 and Tegra210. Default threshold value is enough in Tegra20, Tegra30 and Tegra186. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index d079cde8d4bb..56648b2fa0ee 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -219,8 +219,14 @@ #define RP_VEND_CTL2_PCA_ENABLE (1 << 7) #define RP_PRIV_MISC 0x00000fe0 -#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xe << 0) -#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xf << 0) +#define RP_PRIV_MISC_PRSNT_MAP_EP_PRSNT (0xe << 0) +#define RP_PRIV_MISC_PRSNT_MAP_EP_ABSNT (0xf << 0) +#define RP_PRIV_MISC_CTLR_CLK_CLAMP_THRESHOLD_MASK (0x7f << 16) +#define RP_PRIV_MISC_CTLR_CLK_CLAMP_THRESHOLD (0xf << 16) +#define RP_PRIV_MISC_CTLR_CLK_CLAMP_ENABLE (1 << 23) +#define RP_PRIV_MISC_TMS_CLK_CLAMP_THRESHOLD_MASK (0x7f << 24) +#define RP_PRIV_MISC_TMS_CLK_CLAMP_THRESHOLD (0xf << 24) +#define RP_PRIV_MISC_TMS_CLK_CLAMP_ENABLE (1 << 31) #define RP_LINK_CONTROL_STATUS 0x00000090 #define RP_LINK_CONTROL_STATUS_DL_LINK_ACTIVE 0x20000000 @@ -298,6 +304,7 @@ struct tegra_pcie_soc { bool has_gen2; bool force_pca_enable; bool program_uphy; + bool update_clamp_threshold; struct { struct { u32 rp_ectl_2_r1; @@ -529,6 +536,7 @@ static void tegra_pcie_port_reset(struct tegra_pcie_port *port) static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port) { + const struct tegra_pcie_soc *soc = port->pcie->soc; u32 value; /* Enable AER capability */ @@ -549,6 +557,19 @@ static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port) value = readl(port->base + RP_VEND_XP_BIST); value |= RP_VEND_XP_BIST_GOTO_L1_L2_AFTER_DLLP_DONE; writel(value, port->base + RP_VEND_XP_BIST); + + value = readl(port->base + RP_PRIV_MISC); + value |= RP_PRIV_MISC_CTLR_CLK_CLAMP_ENABLE; + value |= RP_PRIV_MISC_TMS_CLK_CLAMP_ENABLE; + + if (soc->update_clamp_threshold) { + value &= ~(RP_PRIV_MISC_CTLR_CLK_CLAMP_THRESHOLD_MASK | + RP_PRIV_MISC_TMS_CLK_CLAMP_THRESHOLD_MASK); + value |= RP_PRIV_MISC_CTLR_CLK_CLAMP_THRESHOLD | + RP_PRIV_MISC_TMS_CLK_CLAMP_THRESHOLD; + } + + writel(value, port->base + RP_PRIV_MISC); } static void tegra_pcie_program_ectl_settings(struct tegra_pcie_port *port) @@ -2353,6 +2374,7 @@ static const struct tegra_pcie_soc tegra20_pcie = { .has_gen2 = false, .force_pca_enable = false, .program_uphy = true, + .update_clamp_threshold = false, .ectl.enable = false, }; @@ -2377,6 +2399,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { .has_gen2 = false, .force_pca_enable = false, .program_uphy = true, + .update_clamp_threshold = false, .ectl.enable = false, }; @@ -2394,6 +2417,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { .has_gen2 = true, .force_pca_enable = false, .program_uphy = true, + .update_clamp_threshold = true, .ectl.enable = false, }; @@ -2411,6 +2435,7 @@ static const struct tegra_pcie_soc tegra210_pcie = { .has_gen2 = true, .force_pca_enable = true, .program_uphy = true, + .update_clamp_threshold = true, .ectl = { .regs = { .rp_ectl_2_r1 = 0x0000000f, @@ -2447,6 +2472,7 @@ static const struct tegra_pcie_soc tegra186_pcie = { .has_gen2 = true, .force_pca_enable = false, .program_uphy = false, + .update_clamp_threshold = false, .ectl.enable = false, }; -- cgit v1.2.3-55-g7522 From b2634cd0d26d2431f065e06e30069ce11a081755 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:52 +0530 Subject: PCI: tegra: Increase the deskew retry time Sometimes link speed change from Gen2 to Gen1 fails due to instability in deskew logic on lane-0 in Tegra210. Increase the deskew retry time to resolve this issue. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 56648b2fa0ee..609edcb45f47 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -209,6 +209,10 @@ #define RP_VEND_XP_OPPORTUNISTIC_ACK (1 << 27) #define RP_VEND_XP_OPPORTUNISTIC_UPDATEFC (1 << 28) +#define RP_VEND_CTL0 0x00000f44 +#define RP_VEND_CTL0_DSK_RST_PULSE_WIDTH_MASK (0xf << 12) +#define RP_VEND_CTL0_DSK_RST_PULSE_WIDTH (0x9 << 12) + #define RP_VEND_CTL1 0x00000f48 #define RP_VEND_CTL1_ERPT (1 << 13) @@ -305,6 +309,7 @@ struct tegra_pcie_soc { bool force_pca_enable; bool program_uphy; bool update_clamp_threshold; + bool program_deskew_time; struct { struct { u32 rp_ectl_2_r1; @@ -620,6 +625,24 @@ static void tegra_pcie_program_ectl_settings(struct tegra_pcie_port *port) writel(value, port->base + RP_ECTL_6_R2); } +static void tegra_pcie_apply_sw_fixup(struct tegra_pcie_port *port) +{ + const struct tegra_pcie_soc *soc = port->pcie->soc; + u32 value; + + /* + * Sometimes link speed change from Gen2 to Gen1 fails due to + * instability in deskew logic on lane-0. Increase the deskew + * retry time to resolve this issue. + */ + if (soc->program_deskew_time) { + value = readl(port->base + RP_VEND_CTL0); + value &= ~RP_VEND_CTL0_DSK_RST_PULSE_WIDTH_MASK; + value |= RP_VEND_CTL0_DSK_RST_PULSE_WIDTH; + writel(value, port->base + RP_VEND_CTL0); + } +} + static void tegra_pcie_port_enable(struct tegra_pcie_port *port) { unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port); @@ -649,6 +672,8 @@ static void tegra_pcie_port_enable(struct tegra_pcie_port *port) if (soc->ectl.enable) tegra_pcie_program_ectl_settings(port); + + tegra_pcie_apply_sw_fixup(port); } static void tegra_pcie_port_disable(struct tegra_pcie_port *port) @@ -2375,6 +2400,7 @@ static const struct tegra_pcie_soc tegra20_pcie = { .force_pca_enable = false, .program_uphy = true, .update_clamp_threshold = false, + .program_deskew_time = false, .ectl.enable = false, }; @@ -2400,6 +2426,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { .force_pca_enable = false, .program_uphy = true, .update_clamp_threshold = false, + .program_deskew_time = false, .ectl.enable = false, }; @@ -2418,6 +2445,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { .force_pca_enable = false, .program_uphy = true, .update_clamp_threshold = true, + .program_deskew_time = false, .ectl.enable = false, }; @@ -2436,6 +2464,7 @@ static const struct tegra_pcie_soc tegra210_pcie = { .force_pca_enable = true, .program_uphy = true, .update_clamp_threshold = true, + .program_deskew_time = true, .ectl = { .regs = { .rp_ectl_2_r1 = 0x0000000f, @@ -2473,6 +2502,7 @@ static const struct tegra_pcie_soc tegra186_pcie = { .force_pca_enable = false, .program_uphy = false, .update_clamp_threshold = false, + .program_deskew_time = false, .ectl.enable = false, }; -- cgit v1.2.3-55-g7522 From 191cd6fb5d2cf184a3010c55ca290b2fe5a3d727 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:53 +0530 Subject: PCI: tegra: Add SW fixup for RAW violations The logic which blocks read requests till AFI gets ACK for all outstanding writes from memory controller does not behave correctly when number of outstanding writes become more than 32 in Tegra124 and Tegra132. SW fixup is to prevent writes from accumulating more than 32 by: - limiting outstanding posted writes to 14 - modifying Gen1 and Gen2 UpdateFC timer frequency UpdateFC timer frequency is equal to twice the value of register content in nsec. These settings are recommended after stress testing with different values. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 609edcb45f47..95e27c08e8bd 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -178,6 +178,13 @@ #define AFI_PEXBIAS_CTRL_0 0x168 +#define RP_PRIV_XP_DL 0x00000494 +#define RP_PRIV_XP_DL_GEN2_UPD_FC_TSHOLD (0x1ff << 1) + +#define RP_RX_HDR_LIMIT 0x00000e00 +#define RP_RX_HDR_LIMIT_PW_MASK (0xff << 8) +#define RP_RX_HDR_LIMIT_PW (0x0e << 8) + #define RP_ECTL_2_R1 0x00000e84 #define RP_ECTL_2_R1_RX_CTLE_1C_MASK 0xffff @@ -208,6 +215,7 @@ #define RP_VEND_XP_DL_UP (1 << 30) #define RP_VEND_XP_OPPORTUNISTIC_ACK (1 << 27) #define RP_VEND_XP_OPPORTUNISTIC_UPDATEFC (1 << 28) +#define RP_VEND_XP_UPDATE_FC_THRESHOLD_MASK (0xff << 18) #define RP_VEND_CTL0 0x00000f44 #define RP_VEND_CTL0_DSK_RST_PULSE_WIDTH_MASK (0xf << 12) @@ -301,6 +309,7 @@ struct tegra_pcie_soc { u32 tx_ref_sel; u32 pads_refclk_cfg0; u32 pads_refclk_cfg1; + u32 update_fc_threshold; bool has_pex_clkreq_en; bool has_pex_bias_ctrl; bool has_intr_prsnt_sense; @@ -310,6 +319,7 @@ struct tegra_pcie_soc { bool program_uphy; bool update_clamp_threshold; bool program_deskew_time; + bool raw_violation_fixup; struct { struct { u32 rp_ectl_2_r1; @@ -641,6 +651,23 @@ static void tegra_pcie_apply_sw_fixup(struct tegra_pcie_port *port) value |= RP_VEND_CTL0_DSK_RST_PULSE_WIDTH; writel(value, port->base + RP_VEND_CTL0); } + + /* Fixup for read after write violation. */ + if (soc->raw_violation_fixup) { + value = readl(port->base + RP_RX_HDR_LIMIT); + value &= ~RP_RX_HDR_LIMIT_PW_MASK; + value |= RP_RX_HDR_LIMIT_PW; + writel(value, port->base + RP_RX_HDR_LIMIT); + + value = readl(port->base + RP_PRIV_XP_DL); + value |= RP_PRIV_XP_DL_GEN2_UPD_FC_TSHOLD; + writel(value, port->base + RP_PRIV_XP_DL); + + value = readl(port->base + RP_VEND_XP); + value &= ~RP_VEND_XP_UPDATE_FC_THRESHOLD_MASK; + value |= soc->update_fc_threshold; + writel(value, port->base + RP_VEND_XP); + } } static void tegra_pcie_port_enable(struct tegra_pcie_port *port) @@ -2401,6 +2428,7 @@ static const struct tegra_pcie_soc tegra20_pcie = { .program_uphy = true, .update_clamp_threshold = false, .program_deskew_time = false, + .raw_violation_fixup = false, .ectl.enable = false, }; @@ -2427,6 +2455,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { .program_uphy = true, .update_clamp_threshold = false, .program_deskew_time = false, + .raw_violation_fixup = false, .ectl.enable = false, }; @@ -2437,6 +2466,8 @@ static const struct tegra_pcie_soc tegra124_pcie = { .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, .pads_refclk_cfg0 = 0x44ac44ac, + /* FC threshold is bit[25:18] */ + .update_fc_threshold = 0x03fc0000, .has_pex_clkreq_en = true, .has_pex_bias_ctrl = true, .has_intr_prsnt_sense = true, @@ -2446,6 +2477,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { .program_uphy = true, .update_clamp_threshold = true, .program_deskew_time = false, + .raw_violation_fixup = true, .ectl.enable = false, }; @@ -2465,6 +2497,7 @@ static const struct tegra_pcie_soc tegra210_pcie = { .program_uphy = true, .update_clamp_threshold = true, .program_deskew_time = true, + .raw_violation_fixup = false, .ectl = { .regs = { .rp_ectl_2_r1 = 0x0000000f, @@ -2503,6 +2536,7 @@ static const struct tegra_pcie_soc tegra186_pcie = { .program_uphy = false, .update_clamp_threshold = false, .program_deskew_time = false, + .raw_violation_fixup = false, .ectl.enable = false, }; -- cgit v1.2.3-55-g7522 From 9f570b6c240e4f64791805b37fb4c60c65ea95c6 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:54 +0530 Subject: PCI: tegra: Update flow control timer frequency in Tegra210 Recommended UpdateFC threshold in Tegra210 is 0x60 for best performance of x1 link. Setting this to 0x60 provides the best balance between number of UpdateFC packets and read data sent over the link. UpdateFC timer frequency is equal to twice the value of register content in nsec, i.e (2 * 0x60) = 192 nsec. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 95e27c08e8bd..cf2398a10d8c 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -320,6 +320,7 @@ struct tegra_pcie_soc { bool update_clamp_threshold; bool program_deskew_time; bool raw_violation_fixup; + bool update_fc_timer; struct { struct { u32 rp_ectl_2_r1; @@ -668,6 +669,13 @@ static void tegra_pcie_apply_sw_fixup(struct tegra_pcie_port *port) value |= soc->update_fc_threshold; writel(value, port->base + RP_VEND_XP); } + + if (soc->update_fc_timer) { + value = readl(port->base + RP_VEND_XP); + value &= ~RP_VEND_XP_UPDATE_FC_THRESHOLD_MASK; + value |= soc->update_fc_threshold; + writel(value, port->base + RP_VEND_XP); + } } static void tegra_pcie_port_enable(struct tegra_pcie_port *port) @@ -2429,6 +2437,7 @@ static const struct tegra_pcie_soc tegra20_pcie = { .update_clamp_threshold = false, .program_deskew_time = false, .raw_violation_fixup = false, + .update_fc_timer = false, .ectl.enable = false, }; @@ -2456,6 +2465,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { .update_clamp_threshold = false, .program_deskew_time = false, .raw_violation_fixup = false, + .update_fc_timer = false, .ectl.enable = false, }; @@ -2478,6 +2488,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { .update_clamp_threshold = true, .program_deskew_time = false, .raw_violation_fixup = true, + .update_fc_timer = false, .ectl.enable = false, }; @@ -2488,6 +2499,8 @@ static const struct tegra_pcie_soc tegra210_pcie = { .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, .pads_refclk_cfg0 = 0x90b890b8, + /* FC threshold is bit[25:18] */ + .update_fc_threshold = 0x01800000, .has_pex_clkreq_en = true, .has_pex_bias_ctrl = true, .has_intr_prsnt_sense = true, @@ -2498,6 +2511,7 @@ static const struct tegra_pcie_soc tegra210_pcie = { .update_clamp_threshold = true, .program_deskew_time = true, .raw_violation_fixup = false, + .update_fc_timer = true, .ectl = { .regs = { .rp_ectl_2_r1 = 0x0000000f, @@ -2537,6 +2551,7 @@ static const struct tegra_pcie_soc tegra186_pcie = { .update_clamp_threshold = false, .program_deskew_time = false, .raw_violation_fixup = false, + .update_fc_timer = false, .ectl.enable = false, }; -- cgit v1.2.3-55-g7522 From c23ae2aec5bc40cfc8e9ad0058aff2edacb0fbcb Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:55 +0530 Subject: PCI: tegra: Set target speed as Gen1 before starting LTSSM PCIe link up fails with few legacy endpoints if root port advertises both Gen-1 and Gen-2 speeds in Tegra. This is because link number negotiation fails if both Gen1 & Gen2 are advertised. Tegra doesn't retry link up by advertising only Gen1. Hence, the strategy followed here is to initially advertise only Gen-1 and after link is up, retrain link to Gen-2 speed. Tegra doesn't support HW autonomous speed change. Link comes up in Gen1 even if Gen2 is advertised, so there is no downside of this change. This behavior is observed with following two PCIe devices on Tegra: - Fusion HDTV 5 Express card - IOGear SIL - PCIE - SATA card Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index cf2398a10d8c..ffe3ad5d0509 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -676,6 +676,17 @@ static void tegra_pcie_apply_sw_fixup(struct tegra_pcie_port *port) value |= soc->update_fc_threshold; writel(value, port->base + RP_VEND_XP); } + + /* + * PCIe link doesn't come up with few legacy PCIe endpoints if + * root port advertises both Gen-1 and Gen-2 speeds in Tegra. + * Hence, the strategy followed here is to initially advertise + * only Gen-1 and after link is up, retrain link to Gen-2 speed + */ + value = readl(port->base + RP_LINK_CONTROL_STATUS_2); + value &= ~PCI_EXP_LNKSTA_CLS; + value |= PCI_EXP_LNKSTA_CLS_2_5GB; + writel(value, port->base + RP_LINK_CONTROL_STATUS_2); } static void tegra_pcie_port_enable(struct tegra_pcie_port *port) -- cgit v1.2.3-55-g7522 From eef4a35026613ff1576ca93a9b158e774330576b Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:56 +0530 Subject: PCI: tegra: Fix PLLE power down issue due to CLKREQ# signal Disable controllers which failed to bring the link up and configure CLKREQ# signals of these controllers as GPIO. This is required to avoid CLKREQ# signal of inactive controllers interfering with PLLE power down sequence. PCIE_CLKREQ_GPIO bits are defined only in Tegra186, however programming these bits in other SoCs doesn't cause any side effects. Program these bits for all Tegra SoCs to avoid a conditional check. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index ffe3ad5d0509..60051a43c34f 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -160,6 +160,8 @@ #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_211 (0x1 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411 (0x2 << 20) #define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_111 (0x2 << 20) +#define AFI_PCIE_CONFIG_PCIE_CLKREQ_GPIO(x) (1 << ((x) + 29)) +#define AFI_PCIE_CONFIG_PCIE_CLKREQ_GPIO_ALL (0x7 << 29) #define AFI_FUSE 0x104 #define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) @@ -741,6 +743,12 @@ static void tegra_pcie_port_disable(struct tegra_pcie_port *port) value &= ~AFI_PEX_CTRL_REFCLK_EN; afi_writel(port->pcie, value, ctrl); + + /* disable PCIe port and set CLKREQ# as GPIO to allow PLLE power down */ + value = afi_readl(port->pcie, AFI_PCIE_CONFIG); + value |= AFI_PCIE_CONFIG_PCIE_DISABLE(port->index); + value |= AFI_PCIE_CONFIG_PCIE_CLKREQ_GPIO(port->index); + afi_writel(port->pcie, value, AFI_PCIE_CONFIG); } static void tegra_pcie_port_free(struct tegra_pcie_port *port) @@ -1153,9 +1161,12 @@ static void tegra_pcie_enable_controller(struct tegra_pcie *pcie) value = afi_readl(pcie, AFI_PCIE_CONFIG); value &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK; value |= AFI_PCIE_CONFIG_PCIE_DISABLE_ALL | pcie->xbar_config; + value |= AFI_PCIE_CONFIG_PCIE_CLKREQ_GPIO_ALL; - list_for_each_entry(port, &pcie->ports, list) + list_for_each_entry(port, &pcie->ports, list) { value &= ~AFI_PCIE_CONFIG_PCIE_DISABLE(port->index); + value &= ~AFI_PCIE_CONFIG_PCIE_CLKREQ_GPIO(port->index); + } afi_writel(pcie, value, AFI_PCIE_CONFIG); -- cgit v1.2.3-55-g7522 From b5b4717ea0ddf64357c68a865f652487b9b1cdf8 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:57 +0530 Subject: PCI: tegra: Program AFI_CACHE_BAR_{0,1}_{ST,SZ} registers only for Tegra20 Cacheable upstream transactions are supported in Tegra20 and Tegra186 only. AFI_CACHE_BAR_{0,1}_{ST,SZ} registers are available in Tegra20 to support cacheable upstream transactions. In Tegra186, AFI_AXCACHE register is defined instead of AFI_CACHE_BAR_{0,1}_{ST,SZ} to be in line with its memory subsystem design. Therefore, program AFI_CACHE_BAR_{0,1}_{ST,SZ} registers only for Tegra20. Signed-off-by: Manikanta Maddireddy [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 60051a43c34f..69cf0a014de9 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -323,6 +323,7 @@ struct tegra_pcie_soc { bool program_deskew_time; bool raw_violation_fixup; bool update_fc_timer; + bool has_cache_bars; struct { struct { u32 rp_ectl_2_r1; @@ -932,11 +933,13 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) afi_writel(pcie, 0, AFI_AXI_BAR5_SZ); afi_writel(pcie, 0, AFI_FPCI_BAR5); - /* map all upstream transactions as uncached */ - afi_writel(pcie, 0, AFI_CACHE_BAR0_ST); - afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ); - afi_writel(pcie, 0, AFI_CACHE_BAR1_ST); - afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ); + if (pcie->soc->has_cache_bars) { + /* map all upstream transactions as uncached */ + afi_writel(pcie, 0, AFI_CACHE_BAR0_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR0_SZ); + afi_writel(pcie, 0, AFI_CACHE_BAR1_ST); + afi_writel(pcie, 0, AFI_CACHE_BAR1_SZ); + } /* MSI translations are setup only when needed */ afi_writel(pcie, 0, AFI_MSI_FPCI_BAR_ST); @@ -2460,6 +2463,7 @@ static const struct tegra_pcie_soc tegra20_pcie = { .program_deskew_time = false, .raw_violation_fixup = false, .update_fc_timer = false, + .has_cache_bars = true, .ectl.enable = false, }; @@ -2488,6 +2492,7 @@ static const struct tegra_pcie_soc tegra30_pcie = { .program_deskew_time = false, .raw_violation_fixup = false, .update_fc_timer = false, + .has_cache_bars = false, .ectl.enable = false, }; @@ -2511,6 +2516,7 @@ static const struct tegra_pcie_soc tegra124_pcie = { .program_deskew_time = false, .raw_violation_fixup = true, .update_fc_timer = false, + .has_cache_bars = false, .ectl.enable = false, }; @@ -2534,6 +2540,7 @@ static const struct tegra_pcie_soc tegra210_pcie = { .program_deskew_time = true, .raw_violation_fixup = false, .update_fc_timer = true, + .has_cache_bars = false, .ectl = { .regs = { .rp_ectl_2_r1 = 0x0000000f, @@ -2574,6 +2581,7 @@ static const struct tegra_pcie_soc tegra186_pcie = { .program_deskew_time = false, .raw_violation_fixup = false, .update_fc_timer = false, + .has_cache_bars = false, .ectl.enable = false, }; -- cgit v1.2.3-55-g7522 From c894121d014260af3fd199e1d2a56d4af5ff8ba9 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:31:58 +0530 Subject: PCI: tegra: Change PRSNT_SENSE IRQ log to debug PRSNT_MAP bit field is programmed to update the slot present status. PRSNT_SENSE IRQ is triggered when this bit field is programmed, which is not an error. Add a new if condition to trap PRSNT_SENSE code and print it with debug log level. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 69cf0a014de9..adc50daca9fb 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -864,7 +864,7 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg) * do not pollute kernel log with master abort reports since they * happen a lot during enumeration */ - if (code == AFI_INTR_MASTER_ABORT) + if (code == AFI_INTR_MASTER_ABORT || code == AFI_INTR_PE_PRSNT_SENSE) dev_dbg(dev, "%s, signature: %08x\n", err_msg[code], signature); else dev_err(dev, "%s, signature: %08x\n", err_msg[code], signature); -- cgit v1.2.3-55-g7522 From adb2653b3d2e91e3053c807710615f532781b61f Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:32:00 +0530 Subject: PCI: tegra: Add AFI_PEX2_CTRL reg offset as part of SoC struct Tegra186 and Tegra30 have three PCIe root ports. AFI_PEX2_CTRL register is defined for third root port. Offset of this register in Tegra186 is different from Tegra30, so add the offset as part of SoC data structure. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index adc50daca9fb..6be4ea72da92 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -168,7 +168,6 @@ #define AFI_PEX0_CTRL 0x110 #define AFI_PEX1_CTRL 0x118 -#define AFI_PEX2_CTRL 0x128 #define AFI_PEX_CTRL_RST (1 << 0) #define AFI_PEX_CTRL_CLKREQ_EN (1 << 1) #define AFI_PEX_CTRL_REFCLK_EN (1 << 3) @@ -307,6 +306,7 @@ struct tegra_pcie_soc { unsigned int num_ports; const struct tegra_pcie_port_soc *ports; unsigned int msi_base_shift; + unsigned long afi_pex2_ctrl; u32 pads_pll_ctl; u32 tx_ref_sel; u32 pads_refclk_cfg0; @@ -517,6 +517,7 @@ static struct pci_ops tegra_pcie_ops = { static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) { + const struct tegra_pcie_soc *soc = port->pcie->soc; unsigned long ret = 0; switch (port->index) { @@ -529,7 +530,7 @@ static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) break; case 2: - ret = AFI_PEX2_CTRL; + ret = soc->afi_pex2_ctrl; break; } @@ -2449,6 +2450,7 @@ static const struct tegra_pcie_soc tegra20_pcie = { .num_ports = 2, .ports = tegra20_pcie_ports, .msi_base_shift = 0, + .afi_pex2_ctrl = 0x128, .pads_pll_ctl = PADS_PLL_CTL_TEGRA20, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_DIV10, .pads_refclk_cfg0 = 0xfa5cfa5c, @@ -2566,6 +2568,7 @@ static const struct tegra_pcie_soc tegra186_pcie = { .num_ports = 3, .ports = tegra186_pcie_ports, .msi_base_shift = 8, + .afi_pex2_ctrl = 0x19c, .pads_pll_ctl = PADS_PLL_CTL_TEGRA30, .tx_ref_sel = PADS_PLL_CTL_TXCLKREF_BUF_EN, .pads_refclk_cfg0 = 0x80b880b8, -- cgit v1.2.3-55-g7522 From 2d8c7361585fd53903943cc6a632585f32c14a20 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:32:03 +0530 Subject: PCI: tegra: Put PEX CLK & BIAS pads in DPD mode In Tegra210 AFI design has clamp value for the BIAS pad as 0, which keeps the bias pad in non power down mode. This is leading to power consumption of 2 mW in BIAS pad, even if the PCIe partition is powergated. To avoid unnecessary power consumption, put PEX CLK & BIAS pads in deep power down mode when PCIe partition is power gated. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 6be4ea72da92..0fd1c1a8c1b9 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -2853,6 +2854,7 @@ static int __maybe_unused tegra_pcie_pm_suspend(struct device *dev) if (IS_ENABLED(CONFIG_PCI_MSI)) tegra_pcie_disable_msi(pcie); + pinctrl_pm_select_idle_state(dev); tegra_pcie_power_off(pcie); return 0; @@ -2868,6 +2870,13 @@ static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) dev_err(dev, "tegra pcie power on fail: %d\n", err); return err; } + + err = pinctrl_pm_select_default_state(dev); + if (err < 0) { + dev_err(dev, "failed to disable PCIe IO DPD: %d\n", err); + goto poweroff; + } + tegra_pcie_enable_controller(pcie); tegra_pcie_setup_translations(pcie); @@ -2877,7 +2886,7 @@ static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) err = clk_prepare_enable(pcie->pex_clk); if (err) { dev_err(dev, "failed to enable PEX clock: %d\n", err); - goto poweroff; + goto pex_dpd_enable; } reset_control_deassert(pcie->pex_rst); @@ -2898,6 +2907,8 @@ static int __maybe_unused tegra_pcie_pm_resume(struct device *dev) disable_pex_clk: reset_control_assert(pcie->pex_rst); clk_disable_unprepare(pcie->pex_clk); +pex_dpd_enable: + pinctrl_pm_select_idle_state(dev); poweroff: tegra_pcie_power_off(pcie); -- cgit v1.2.3-55-g7522 From dc6b698a86fe40a50525433eb8e92a267847f6f9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 27 May 2019 00:51:51 +0200 Subject: PCI: sysfs: Ignore lockdep for remove attribute With CONFIG_PROVE_LOCKING=y, using sysfs to remove a bridge with a device below it causes a lockdep warning, e.g., # echo 1 > /sys/class/pci_bus/0000:00/device/0000:00:00.0/remove ============================================ WARNING: possible recursive locking detected ... pci_bus 0000:01: busn_res: [bus 01] is released The remove recursively removes the subtree below the bridge. Each call uses a different lock so there's no deadlock, but the locks were all created with the same lockdep key so the lockdep checker can't tell them apart. Mark the "remove" sysfs attribute with __ATTR_IGNORE_LOCKDEP() as it is safe to ignore the lockdep check between different "remove" kernfs instances. There's discussion about a similar issue in USB at [1], which resulted in 356c05d58af0 ("sysfs: get rid of some lockdep false positives") and e9b526fe7048 ("i2c: suppress lockdep warning on delete_device"), which do basically the same thing for USB "remove" and i2c "delete_device" files. [1] https://lore.kernel.org/r/Pine.LNX.4.44L0.1204251436140.1206-100000@iolanthe.rowland.org Link: https://lore.kernel.org/r/20190526225151.3865-1-marek.vasut@gmail.com Signed-off-by: Marek Vasut [bhelgaas: trim commit log, details at above links] Signed-off-by: Bjorn Helgaas Cc: Geert Uytterhoeven Cc: Phil Edworthy Cc: Simon Horman Cc: Tejun Heo Cc: Wolfram Sang --- drivers/pci/pci-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d52d30448e41..965c72104150 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -480,7 +480,7 @@ static ssize_t remove_store(struct device *dev, struct device_attribute *attr, pci_stop_and_remove_bus_device_locked(to_pci_dev(dev)); return count; } -static struct device_attribute dev_remove_attr = __ATTR(remove, +static struct device_attribute dev_remove_attr = __ATTR_IGNORE_LOCKDEP(remove, (S_IWUSR|S_IWGRP), NULL, remove_store); -- cgit v1.2.3-55-g7522 From 7ac0d094fbe95bf7cc96b3066a97e1090ddc734a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Sat, 15 Jun 2019 10:23:58 +1000 Subject: PCI: Don't auto-realloc if we're preserving firmware config Prevent auto-enabling of bridges reallocation when the FW tells us that the initial configuration must be preserved for a given host bridge. Link: https://lore.kernel.org/r/20190615002359.29577-3-benh@kernel.crashing.org Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 0cdd5ff389de..d533102b2788 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1684,10 +1684,15 @@ static enum enable_type pci_realloc_detect(struct pci_bus *bus, enum enable_type enable_local) { bool unassigned = false; + struct pci_host_bridge *host; if (enable_local != undefined) return enable_local; + host = pci_find_host_bridge(bus); + if (host->preserve_config) + return auto_disabled; + pci_walk_bus(bus, iov_resources_unassigned, &unassigned); if (unassigned) return auto_enabled; -- cgit v1.2.3-55-g7522 From 181fa434d0514e40ebf6e9721f2b72700287b6e2 Mon Sep 17 00:00:00 2001 From: Bharat Kumar Gogada Date: Wed, 12 Jun 2019 15:47:59 +0530 Subject: PCI: xilinx-nwl: Fix Multi MSI data programming According to the PCI Local Bus specification Revision 3.0, section 6.8.1.3 (Message Control for MSI), endpoints that are Multiple Message Capable as defined by bits [3:1] in the Message Control for MSI can request a number of vectors that is power of two aligned. As specified in section 6.8.1.6 "Message data for MSI", the Multiple Message Enable field (bits [6:4] of the Message Control register) defines the number of low order message data bits the function is permitted to modify to generate its system software allocated vectors. The MSI controller in the Xilinx NWL PCIe controller supports a number of MSI vectors specified through a bitmap and the hwirq number for an MSI, that is the value written in the MSI data TLP is determined by the bitmap allocation. For instance, in a situation where two endpoints sitting on the PCI bus request the following MSI configuration, with the current PCI Xilinx bitmap allocation code (that does not align MSI vector allocation on a power of two boundary): Endpoint #1: Requesting 1 MSI vector - allocated bitmap bits 0 Endpoint #2: Requesting 2 MSI vectors - allocated bitmap bits [1,2] The bitmap value(s) corresponds to the hwirq number that is programmed into the Message Data for MSI field in the endpoint MSI capability and is detected by the root complex to fire the corresponding MSI irqs. The value written in Message Data for MSI field corresponds to the first bit allocated in the bitmap for Multi MSI vectors. The current Xilinx NWL MSI allocation code allows a bitmap allocation that is not a power of two boundaries, so endpoint #2, is allowed to toggle Message Data bit[0] to differentiate between its two vectors (meaning that the MSI data will be respectively 0x0 and 0x1 for the two vectors allocated to endpoint #2). This clearly aliases with the Endpoint #1 vector allocation, resulting in a broken Multi MSI implementation. Update the code to allocate MSI bitmap ranges with a power of two alignment, fixing the bug. Fixes: ab597d35ef11 ("PCI: xilinx-nwl: Add support for Xilinx NWL PCIe Host Controller") Suggested-by: Marc Zyngier Signed-off-by: Bharat Kumar Gogada [lorenzo.pieralisi@arm.com: updated commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Marc Zyngier --- drivers/pci/controller/pcie-xilinx-nwl.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 3b031f00a94a..45c0f344ccd1 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -482,15 +482,13 @@ static int nwl_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, int i; mutex_lock(&msi->lock); - bit = bitmap_find_next_zero_area(msi->bitmap, INT_PCI_MSI_NR, 0, - nr_irqs, 0); - if (bit >= INT_PCI_MSI_NR) { + bit = bitmap_find_free_region(msi->bitmap, INT_PCI_MSI_NR, + get_count_order(nr_irqs)); + if (bit < 0) { mutex_unlock(&msi->lock); return -ENOSPC; } - bitmap_set(msi->bitmap, bit, nr_irqs); - for (i = 0; i < nr_irqs; i++) { irq_domain_set_info(domain, virq + i, bit + i, &nwl_irq_chip, domain->host_data, handle_simple_irq, @@ -508,7 +506,8 @@ static void nwl_irq_domain_free(struct irq_domain *domain, unsigned int virq, struct nwl_msi *msi = &pcie->msi; mutex_lock(&msi->lock); - bitmap_clear(msi->bitmap, data->hwirq, nr_irqs); + bitmap_release_region(msi->bitmap, data->hwirq, + get_count_order(nr_irqs)); mutex_unlock(&msi->lock); } -- cgit v1.2.3-55-g7522 From 9d071cade30a05b46e894885beeba9922dc4fd23 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Tue, 25 Jun 2019 14:52:36 +0530 Subject: PCI: dwc: Add API support to de-initialize host Add an API to group all the tasks to be done to de-initialize host which can then be called by any dwc core based driver implementations while adding .remove() support in their respective drivers. Signed-off-by: Vidya Sagar Signed-off-by: Lorenzo Pieralisi Acked-by: Gustavo Pimentel --- drivers/pci/controller/dwc/pcie-designware-host.c | 8 ++++++++ drivers/pci/controller/dwc/pcie-designware.h | 5 +++++ 2 files changed, 13 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 77db32529319..d069e4290180 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -496,6 +496,14 @@ err_free_msi: return ret; } +void dw_pcie_host_deinit(struct pcie_port *pp) +{ + pci_stop_root_bus(pp->root_bus); + pci_remove_root_bus(pp->root_bus); + if (pci_msi_enabled() && !pp->ops->msi_host_init) + dw_pcie_free_msi(pp); +} + static int dw_pcie_access_other_conf(struct pcie_port *pp, struct pci_bus *bus, u32 devfn, int where, int size, u32 *val, bool write) diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index b8993f2b78df..14762e262758 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -351,6 +351,7 @@ void dw_pcie_msi_init(struct pcie_port *pp); void dw_pcie_free_msi(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); +void dw_pcie_host_deinit(struct pcie_port *pp); int dw_pcie_allocate_domains(struct pcie_port *pp); #else static inline irqreturn_t dw_handle_msi_irq(struct pcie_port *pp) @@ -375,6 +376,10 @@ static inline int dw_pcie_host_init(struct pcie_port *pp) return 0; } +static inline void dw_pcie_host_deinit(struct pcie_port *pp) +{ +} + static inline int dw_pcie_allocate_domains(struct pcie_port *pp) { return 0; -- cgit v1.2.3-55-g7522 From 7bc082d7e97009f252bd432de5d476b0bcf3b266 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Tue, 25 Jun 2019 14:52:37 +0530 Subject: PCI: dwc: Cleanup DBI,ATU read and write APIs Cleanup DBI read and write APIs by removing leading "__" (underscore) from their names as there is no reason to have leading underscores in the first place in the function definition. Remove dbi/dbi2 base address parameters as the same behaviour can be obtained through read and write APIs. Since dw_pcie_{readl/writel}_dbi() APIs can't be used for ATU read/write as ATU base address could be different from DBI base address, implement ATU read/write APIs using ATU base address without using dw_pcie_{readl/writel}_dbi() APIs. Signed-off-by: Vidya Sagar Signed-off-by: Lorenzo Pieralisi Acked-by: Jingoo Han --- drivers/pci/controller/dwc/pcie-designware.c | 57 ++++++++++++++++++++-------- drivers/pci/controller/dwc/pcie-designware.h | 34 ++++++++--------- 2 files changed, 57 insertions(+), 34 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 9d7c51c32b3b..c2843ea1d1e8 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -52,68 +52,93 @@ int dw_pcie_write(void __iomem *addr, int size, u32 val) return PCIBIOS_SUCCESSFUL; } -u32 __dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size) +u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size) { int ret; u32 val; if (pci->ops->read_dbi) - return pci->ops->read_dbi(pci, base, reg, size); + return pci->ops->read_dbi(pci, pci->dbi_base, reg, size); - ret = dw_pcie_read(base + reg, size, &val); + ret = dw_pcie_read(pci->dbi_base + reg, size, &val); if (ret) dev_err(pci->dev, "Read DBI address failed\n"); return val; } -void __dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size, u32 val) +void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 val) { int ret; if (pci->ops->write_dbi) { - pci->ops->write_dbi(pci, base, reg, size, val); + pci->ops->write_dbi(pci, pci->dbi_base, reg, size, val); return; } - ret = dw_pcie_write(base + reg, size, val); + ret = dw_pcie_write(pci->dbi_base + reg, size, val); if (ret) dev_err(pci->dev, "Write DBI address failed\n"); } -u32 __dw_pcie_read_dbi2(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size) +u32 dw_pcie_read_dbi2(struct dw_pcie *pci, u32 reg, size_t size) { int ret; u32 val; if (pci->ops->read_dbi2) - return pci->ops->read_dbi2(pci, base, reg, size); + return pci->ops->read_dbi2(pci, pci->dbi_base2, reg, size); - ret = dw_pcie_read(base + reg, size, &val); + ret = dw_pcie_read(pci->dbi_base2 + reg, size, &val); if (ret) dev_err(pci->dev, "read DBI address failed\n"); return val; } -void __dw_pcie_write_dbi2(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size, u32 val) +void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val) { int ret; if (pci->ops->write_dbi2) { - pci->ops->write_dbi2(pci, base, reg, size, val); + pci->ops->write_dbi2(pci, pci->dbi_base2, reg, size, val); return; } - ret = dw_pcie_write(base + reg, size, val); + ret = dw_pcie_write(pci->dbi_base2 + reg, size, val); if (ret) dev_err(pci->dev, "write DBI address failed\n"); } +u32 dw_pcie_read_atu(struct dw_pcie *pci, u32 reg, size_t size) +{ + int ret; + u32 val; + + if (pci->ops->read_dbi) + return pci->ops->read_dbi(pci, pci->atu_base, reg, size); + + ret = dw_pcie_read(pci->atu_base + reg, size, &val); + if (ret) + dev_err(pci->dev, "Read ATU address failed\n"); + + return val; +} + +void dw_pcie_write_atu(struct dw_pcie *pci, u32 reg, size_t size, u32 val) +{ + int ret; + + if (pci->ops->write_dbi) { + pci->ops->write_dbi(pci, pci->atu_base, reg, size, val); + return; + } + + ret = dw_pcie_write(pci->atu_base + reg, size, val); + if (ret) + dev_err(pci->dev, "Write ATU address failed\n"); +} + static u32 dw_pcie_readl_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg) { u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 14762e262758..ffed084a0b4f 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -254,14 +254,12 @@ struct dw_pcie { int dw_pcie_read(void __iomem *addr, int size, u32 *val); int dw_pcie_write(void __iomem *addr, int size, u32 val); -u32 __dw_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size); -void __dw_pcie_write_dbi(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size, u32 val); -u32 __dw_pcie_read_dbi2(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size); -void __dw_pcie_write_dbi2(struct dw_pcie *pci, void __iomem *base, u32 reg, - size_t size, u32 val); +u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size); +void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 val); +u32 dw_pcie_read_dbi2(struct dw_pcie *pci, u32 reg, size_t size); +void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val); +u32 dw_pcie_read_atu(struct dw_pcie *pci, u32 reg, size_t size); +void dw_pcie_write_atu(struct dw_pcie *pci, u32 reg, size_t size, u32 val); int dw_pcie_link_up(struct dw_pcie *pci); int dw_pcie_wait_for_link(struct dw_pcie *pci); void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, @@ -275,52 +273,52 @@ void dw_pcie_setup(struct dw_pcie *pci); static inline void dw_pcie_writel_dbi(struct dw_pcie *pci, u32 reg, u32 val) { - __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x4, val); + dw_pcie_write_dbi(pci, reg, 0x4, val); } static inline u32 dw_pcie_readl_dbi(struct dw_pcie *pci, u32 reg) { - return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x4); + return dw_pcie_read_dbi(pci, reg, 0x4); } static inline void dw_pcie_writew_dbi(struct dw_pcie *pci, u32 reg, u16 val) { - __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x2, val); + dw_pcie_write_dbi(pci, reg, 0x2, val); } static inline u16 dw_pcie_readw_dbi(struct dw_pcie *pci, u32 reg) { - return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x2); + return dw_pcie_read_dbi(pci, reg, 0x2); } static inline void dw_pcie_writeb_dbi(struct dw_pcie *pci, u32 reg, u8 val) { - __dw_pcie_write_dbi(pci, pci->dbi_base, reg, 0x1, val); + dw_pcie_write_dbi(pci, reg, 0x1, val); } static inline u8 dw_pcie_readb_dbi(struct dw_pcie *pci, u32 reg) { - return __dw_pcie_read_dbi(pci, pci->dbi_base, reg, 0x1); + return dw_pcie_read_dbi(pci, reg, 0x1); } static inline void dw_pcie_writel_dbi2(struct dw_pcie *pci, u32 reg, u32 val) { - __dw_pcie_write_dbi2(pci, pci->dbi_base2, reg, 0x4, val); + dw_pcie_write_dbi2(pci, reg, 0x4, val); } static inline u32 dw_pcie_readl_dbi2(struct dw_pcie *pci, u32 reg) { - return __dw_pcie_read_dbi2(pci, pci->dbi_base2, reg, 0x4); + return dw_pcie_read_dbi2(pci, reg, 0x4); } static inline void dw_pcie_writel_atu(struct dw_pcie *pci, u32 reg, u32 val) { - __dw_pcie_write_dbi(pci, pci->atu_base, reg, 0x4, val); + dw_pcie_write_atu(pci, reg, 0x4, val); } static inline u32 dw_pcie_readl_atu(struct dw_pcie *pci, u32 reg) { - return __dw_pcie_read_dbi(pci, pci->atu_base, reg, 0x4); + return dw_pcie_read_atu(pci, reg, 0x4); } static inline void dw_pcie_dbi_ro_wr_en(struct dw_pcie *pci) -- cgit v1.2.3-55-g7522 From ca98329d3b58ef636b8f455b0b1b6ac9c89738b2 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Tue, 25 Jun 2019 14:52:38 +0530 Subject: PCI: dwc: Export APIs to support .remove() implementation Export all configuration space access APIs and also other APIs to support host controller drivers of dwc core based implementations while adding support for .remove() hook to build their respective drivers as modules. Signed-off-by: Vidya Sagar Signed-off-by: Lorenzo Pieralisi Acked-by: Gustavo Pimentel --- drivers/pci/controller/dwc/pcie-designware-host.c | 4 ++++ drivers/pci/controller/dwc/pcie-designware.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index d069e4290180..f93252d0da5b 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -311,6 +311,7 @@ void dw_pcie_msi_init(struct pcie_port *pp) dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, upper_32_bits(msi_target)); } +EXPORT_SYMBOL_GPL(dw_pcie_msi_init); int dw_pcie_host_init(struct pcie_port *pp) { @@ -495,6 +496,7 @@ err_free_msi: dw_pcie_free_msi(pp); return ret; } +EXPORT_SYMBOL_GPL(dw_pcie_host_init); void dw_pcie_host_deinit(struct pcie_port *pp) { @@ -503,6 +505,7 @@ void dw_pcie_host_deinit(struct pcie_port *pp) if (pci_msi_enabled() && !pp->ops->msi_host_init) dw_pcie_free_msi(pp); } +EXPORT_SYMBOL_GPL(dw_pcie_host_deinit); static int dw_pcie_access_other_conf(struct pcie_port *pp, struct pci_bus *bus, u32 devfn, int where, int size, u32 *val, @@ -695,3 +698,4 @@ void dw_pcie_setup_rc(struct pcie_port *pp) val |= PORT_LOGIC_SPEED_CHANGE; dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); } +EXPORT_SYMBOL_GPL(dw_pcie_setup_rc); diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index c2843ea1d1e8..7d25102c304c 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -34,6 +34,7 @@ int dw_pcie_read(void __iomem *addr, int size, u32 *val) return PCIBIOS_SUCCESSFUL; } +EXPORT_SYMBOL_GPL(dw_pcie_read); int dw_pcie_write(void __iomem *addr, int size, u32 val) { @@ -51,6 +52,7 @@ int dw_pcie_write(void __iomem *addr, int size, u32 val) return PCIBIOS_SUCCESSFUL; } +EXPORT_SYMBOL_GPL(dw_pcie_write); u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size) { @@ -66,6 +68,7 @@ u32 dw_pcie_read_dbi(struct dw_pcie *pci, u32 reg, size_t size) return val; } +EXPORT_SYMBOL_GPL(dw_pcie_read_dbi); void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 val) { @@ -80,6 +83,7 @@ void dw_pcie_write_dbi(struct dw_pcie *pci, u32 reg, size_t size, u32 val) if (ret) dev_err(pci->dev, "Write DBI address failed\n"); } +EXPORT_SYMBOL_GPL(dw_pcie_write_dbi); u32 dw_pcie_read_dbi2(struct dw_pcie *pci, u32 reg, size_t size) { -- cgit v1.2.3-55-g7522 From 5c6bcc344b18dfb3b0ddcca6c26f6858879f73bf Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Sat, 22 Jun 2019 11:43:46 -0500 Subject: PCI: Simplify pci_bus_distribute_available_resources() Reorder pci_bus_distribute_available_resources() to group related code together. No functional change intended. Link: https://lore.kernel.org/r/PS2P216MB0642C7A485649D2D787A1C6F80000@PS2P216MB0642.KORP216.PROD.OUTLOOK.COM Link: https://lore.kernel.org/r/20190622210310.180905-2-helgaas@kernel.org Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Mika Westerberg Reviewed-by: Logan Gunthorpe --- drivers/pci/setup-bus.c | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index d533102b2788..ee5be219dd41 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1865,16 +1865,6 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, extend_bridge_window(bridge, mmio_pref_res, add_list, available_mmio_pref); - /* - * Calculate the total amount of extra resource space we can - * pass to bridges below this one. This is basically the - * extra space reduced by the minimal required space for the - * non-hotplug bridges. - */ - remaining_io = available_io; - remaining_mmio = available_mmio; - remaining_mmio_pref = available_mmio_pref; - /* * Calculate how many hotplug bridges and normal bridges there * are on this bus. We will distribute the additional available @@ -1887,6 +1877,31 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, normal_bridges++; } + /* + * There is only one bridge on the bus so it gets all available + * resources which it can then distribute to the possible hotplug + * bridges below. + */ + if (hotplug_bridges + normal_bridges == 1) { + dev = list_first_entry(&bus->devices, struct pci_dev, bus_list); + if (dev->subordinate) { + pci_bus_distribute_available_resources(dev->subordinate, + add_list, available_io, available_mmio, + available_mmio_pref); + } + return; + } + + /* + * Calculate the total amount of extra resource space we can + * pass to bridges below this one. This is basically the + * extra space reduced by the minimal required space for the + * non-hotplug bridges. + */ + remaining_io = available_io; + remaining_mmio = available_mmio; + remaining_mmio_pref = available_mmio_pref; + for_each_pci_bridge(dev, bus) { const struct resource *res; @@ -1910,21 +1925,6 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, remaining_mmio_pref -= resource_size(res); } - /* - * There is only one bridge on the bus so it gets all available - * resources which it can then distribute to the possible hotplug - * bridges below. - */ - if (hotplug_bridges + normal_bridges == 1) { - dev = list_first_entry(&bus->devices, struct pci_dev, bus_list); - if (dev->subordinate) { - pci_bus_distribute_available_resources(dev->subordinate, - add_list, available_io, available_mmio, - available_mmio_pref); - } - return; - } - /* * Go over devices on this bus and distribute the remaining * resource space between hotplug bridges. -- cgit v1.2.3-55-g7522 From 6a381ea694c9da31ba8741c42a7f1b206c156841 Mon Sep 17 00:00:00 2001 From: Nicholas Johnson Date: Sat, 22 Jun 2019 12:13:50 -0500 Subject: PCI: Skip resource distribution when no hotplug bridges If "hotplug_bridges == 0", "!dev->is_hotplug_bridge" is always true, so the loop that divides the remaining resources among hotplug-capable bridges does nothing. Check for "hotplug_bridges == 0" earlier, so we don't even have to compute the amount of remaining resources. No functional change intended. Link: https://lore.kernel.org/r/PS2P216MB0642C7A485649D2D787A1C6F80000@PS2P216MB0642.KORP216.PROD.OUTLOOK.COM Link: https://lore.kernel.org/r/20190622210310.180905-3-helgaas@kernel.org Signed-off-by: Nicholas Johnson Signed-off-by: Bjorn Helgaas Reviewed-by: Logan Gunthorpe Reviewed-by: Mika Westerberg --- drivers/pci/setup-bus.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index ee5be219dd41..79b1fa6519be 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1892,6 +1892,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, return; } + if (hotplug_bridges == 0) + return; + /* * Calculate the total amount of extra resource space we can * pass to bridges below this one. This is basically the @@ -1941,8 +1944,6 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * Distribute available extra resources equally between * hotplug-capable downstream ports taking alignment into * account. - * - * Here hotplug_bridges is always != 0. */ align = pci_resource_alignment(bridge, io_res); io = div64_ul(available_io, hotplug_bridges); -- cgit v1.2.3-55-g7522 From 9c002bb66f5bd8b8f470926489a115b7f486f501 Mon Sep 17 00:00:00 2001 From: Logan Gunthorpe Date: Tue, 2 Jul 2019 11:35:44 -0600 Subject: PCI/P2PDMA: Fix missing check for dma_virt_ops Drivers that use dma_virt_ops were meant to be rejected when testing compatibility for P2PDMA. This check got inadvertently dropped in one of the later versions of the original patchset, so add it back. Fixes: 52916982af48 ("PCI/P2PDMA: Support peer-to-peer memory") Link: https://lore.kernel.org/r/20190702173544.21950-1-logang@deltatee.com Signed-off-by: Logan Gunthorpe Signed-off-by: Bjorn Helgaas --- drivers/pci/p2pdma.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 742928d0053e..1940a7a0a684 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -468,6 +468,14 @@ int pci_p2pdma_distance_many(struct pci_dev *provider, struct device **clients, return -1; for (i = 0; i < num_clients; i++) { + if (IS_ENABLED(CONFIG_DMA_VIRT_OPS) && + clients[i]->dma_ops == &dma_virt_ops) { + if (verbose) + dev_warn(clients[i], + "cannot be used for peer-to-peer DMA because the driver makes use of dma_virt_ops\n"); + return -1; + } + pci_client = find_parent_pci_dev(clients[i]); if (!pci_client) { if (verbose) @@ -732,7 +740,7 @@ int pci_p2pdma_map_sg(struct device *dev, struct scatterlist *sg, int nents, * p2pdma mappings are not compatible with devices that use * dma_virt_ops. If the upper layers do the right thing * this should never happen because it will be prevented - * by the check in pci_p2pdma_add_client() + * by the check in pci_p2pdma_distance_many() */ if (WARN_ON_ONCE(IS_ENABLED(CONFIG_DMA_VIRT_OPS) && dev->dma_ops == &dma_virt_ops)) -- cgit v1.2.3-55-g7522 From 590a18e1711318a7e1756be48853223a1eb84316 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Tue, 2 Jul 2019 13:21:33 +0200 Subject: PCI: Use seq_puts() instead of seq_printf() in show_device() The driver name in /proc/bus/pci/devices can be printed without a printf format specification, so use seq_puts() instead of seq_printf(). This issue was detected by using the Coccinelle software. Link: https://lore.kernel.org/r/a6b110cb-0d0e-5dc3-9ca1-9041609cf74c@web.de Signed-off-by: Markus Elfring [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas --- drivers/pci/proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 445b51db75b0..fe7fe678965b 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -377,7 +377,7 @@ static int show_device(struct seq_file *m, void *v) } seq_putc(m, '\t'); if (drv) - seq_printf(m, "%s", drv->name); + seq_puts(m, drv->name); seq_putc(m, '\n'); return 0; } -- cgit v1.2.3-55-g7522 From 76bf6a8634a1ac04d3075993c31a6d94a7f75580 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 13 Jun 2019 16:57:12 -0600 Subject: Revert "PCI/IOV: Use VF0 cached config space size for other VFs" Revert 975bb8b4dc93 ("PCI/IOV: Use VF0 cached config space size for other VFs"), which attempted to cache the config space size from the first VF to re-use for subsequent VFs. The cached value was determined prior to discovering the PCIe capability on the VF, which resulted in the first VF reporting the correct config space size (4K), as it has a special case through pci_cfg_space_size(), while all the other VFs only reported 256 bytes. As this was only a performance optimization, we're better off without it. Fixes: 975bb8b4dc93 ("PCI/IOV: Use VF0 cached config space size for other VFs") Link: https://lore.kernel.org/r/156046663197.29869.3633634445109057665.stgit@gimli.home Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas Cc: KarimAllah Ahmed Cc: Kuppuswamy Sathyanarayanan Cc: Hao Zheng --- drivers/pci/iov.c | 2 -- drivers/pci/pci.h | 1 - drivers/pci/probe.c | 17 ----------------- 3 files changed, 20 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 3aa115ed3a65..525fd3f272b3 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -132,8 +132,6 @@ static void pci_read_vf_config_common(struct pci_dev *virtfn) &physfn->sriov->subsystem_vendor); pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID, &physfn->sriov->subsystem_device); - - physfn->sriov->cfg_size = pci_cfg_space_size(virtfn); } int pci_iov_add_virtfn(struct pci_dev *dev, int id) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9cb99380c61e..3fc227ef0815 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -293,7 +293,6 @@ struct pci_sriov { u16 driver_max_VFs; /* Max num VFs driver supports */ struct pci_dev *dev; /* Lowest numbered PF */ struct pci_dev *self; /* This PF */ - u32 cfg_size; /* VF config space size */ u32 class; /* VF device */ u8 hdr_type; /* VF header type */ u16 subsystem_vendor; /* VF subsystem vendor */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0e8e2c186f50..a3a3c6b28343 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1555,29 +1555,12 @@ static int pci_cfg_space_size_ext(struct pci_dev *dev) return PCI_CFG_SPACE_EXP_SIZE; } -#ifdef CONFIG_PCI_IOV -static bool is_vf0(struct pci_dev *dev) -{ - if (pci_iov_virtfn_devfn(dev->physfn, 0) == dev->devfn && - pci_iov_virtfn_bus(dev->physfn, 0) == dev->bus->number) - return true; - - return false; -} -#endif - int pci_cfg_space_size(struct pci_dev *dev) { int pos; u32 status; u16 class; -#ifdef CONFIG_PCI_IOV - /* Read cached value for all VFs except for VF0 */ - if (dev->is_virtfn && !is_vf0(dev)) - return dev->physfn->sriov->cfg_size; -#endif - if (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_EXTCFG) return PCI_CFG_SPACE_SIZE; -- cgit v1.2.3-55-g7522 From 06013b647c708e4f3179b7b0cf3adf28cb8cbfcf Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 13 Jun 2019 16:57:20 -0600 Subject: PCI/IOV: Assume SR-IOV VFs support extended config space. The SR-IOV specification requires both PFs and VFs to implement a PCIe capability. Generally this is sufficient to assume extended config space is present, but we generally also perform additional tests to make sure the extended config space is reachable and not simply an alias of standard config space. For a VF to exist extended config space must be accessible on the PF, therefore we can also assume it to be accessible on the VF. This enables a micro performance optimization previously implemented in commit 975bb8b4dc93 ("PCI/IOV: Use VF0 cached config space size for other VFs") to speed up probing of VFs. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas Reviewed-by: Kuppuswamy Sathyanarayanan Cc: KarimAllah Ahmed Cc: Hao Zheng --- drivers/pci/probe.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index a3a3c6b28343..439244ff8f09 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1561,6 +1561,21 @@ int pci_cfg_space_size(struct pci_dev *dev) u32 status; u16 class; +#ifdef CONFIG_PCI_IOV + /* + * Per the SR-IOV specification (rev 1.1, sec 3.5), VFs are required to + * implement a PCIe capability and therefore must implement extended + * config space. We can skip the NO_EXTCFG test below and the + * reachability/aliasing test in pci_cfg_space_size_ext() by virtue of + * the fact that the SR-IOV capability on the PF resides in extended + * config space and must be accessible and non-aliased to have enabled + * support for this VF. This is a micro performance optimization for + * systems supporting many VFs. + */ + if (dev->is_virtfn) + return PCI_CFG_SPACE_EXP_SIZE; +#endif + if (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_EXTCFG) return PCI_CFG_SPACE_SIZE; -- cgit v1.2.3-55-g7522 From dbdcc22c845be72d0b930a82ca20df8c92c677b9 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:32:05 +0530 Subject: PCI: tegra: Add support for GPIO based PERST# Tegra PCIe has fixed per port SFIO line to signal PERST#, which can be controlled by AFI port register. However, if a platform routes a different GPIO to the PCIe slot, then port register cannot control it. Add support for GPIO based PERST# signal for such platforms. GPIO number comes from per port PCIe device tree node. PCIe driver probe doesn't fail if per port "reset-gpios" property is not populated, so platforms that require this workaround must make sure that the DT property is not missed in the corresponding device tree. Link: https://lore.kernel.org/linux-pci/20190705084850.30777-1-jonathanh@nvidia.com/ Signed-off-by: Manikanta Maddireddy [lorenzo.pieralisi@arm.com: squashed in fix in Link] Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 49 +++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 0fd1c1a8c1b9..6086c741cef7 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -399,6 +400,8 @@ struct tegra_pcie_port { unsigned int lanes; struct phy **phys; + + struct gpio_desc *reset_gpio; }; struct tegra_pcie_bus { @@ -544,15 +547,23 @@ static void tegra_pcie_port_reset(struct tegra_pcie_port *port) unsigned long value; /* pulse reset signal */ - value = afi_readl(port->pcie, ctrl); - value &= ~AFI_PEX_CTRL_RST; - afi_writel(port->pcie, value, ctrl); + if (port->reset_gpio) { + gpiod_set_value(port->reset_gpio, 1); + } else { + value = afi_readl(port->pcie, ctrl); + value &= ~AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + } usleep_range(1000, 2000); - value = afi_readl(port->pcie, ctrl); - value |= AFI_PEX_CTRL_RST; - afi_writel(port->pcie, value, ctrl); + if (port->reset_gpio) { + gpiod_set_value(port->reset_gpio, 0); + } else { + value = afi_readl(port->pcie, ctrl); + value |= AFI_PEX_CTRL_RST; + afi_writel(port->pcie, value, ctrl); + } } static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port) @@ -2218,6 +2229,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) struct tegra_pcie_port *rp; unsigned int index; u32 value; + char *label; err = of_pci_get_devfn(port); if (err < 0) { @@ -2276,6 +2288,31 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie) if (IS_ERR(rp->base)) return PTR_ERR(rp->base); + label = devm_kasprintf(dev, GFP_KERNEL, "pex-reset-%u", index); + if (!label) { + dev_err(dev, "failed to create reset GPIO label\n"); + return -ENOMEM; + } + + /* + * Returns -ENOENT if reset-gpios property is not populated + * and in this case fall back to using AFI per port register + * to toggle PERST# SFIO line. + */ + rp->reset_gpio = devm_gpiod_get_from_of_node(dev, port, + "reset-gpios", 0, + GPIOD_OUT_LOW, + label); + if (IS_ERR(rp->reset_gpio)) { + if (PTR_ERR(rp->reset_gpio) == -ENOENT) { + rp->reset_gpio = NULL; + } else { + dev_err(dev, "failed to get reset GPIO: %d\n", + err); + return PTR_ERR(rp->reset_gpio); + } + } + list_add_tail(&rp->list, &pcie->ports); } -- cgit v1.2.3-55-g7522 From 4b16a8227907118e011fb396022da671a52b2272 Mon Sep 17 00:00:00 2001 From: Manikanta Maddireddy Date: Tue, 18 Jun 2019 23:32:06 +0530 Subject: PCI: tegra: Change link retry log level to debug Driver checks for link up three times before giving up, each retry attempt is printed as an error. Letting users know that PCIe link is down and in the process of being brought up again is for debug, not an error condition. Signed-off-by: Manikanta Maddireddy Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 6086c741cef7..37c694dcbc9e 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -2360,7 +2360,7 @@ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port) } while (--timeout); if (!timeout) { - dev_err(dev, "link %u down, retrying\n", port->index); + dev_dbg(dev, "link %u down, retrying\n", port->index); goto retry; } -- cgit v1.2.3-55-g7522 From 7be142caabc4780b13a522c485abc806de5c4114 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Thu, 4 Jul 2019 20:34:28 +0530 Subject: PCI: tegra: Enable Relaxed Ordering only for Tegra20 & Tegra30 The PCI Tegra controller conversion to a device tree configurable driver in commit d1523b52bff3 ("PCI: tegra: Move PCIe driver to drivers/pci/host") implied that code for the driver can be compiled in for a kernel supporting multiple platforms. Unfortunately, a blind move of the code did not check that some of the quirks that were applied in arch/arm (eg enabling Relaxed Ordering on all PCI devices - since the quirk hook erroneously matches PCI_ANY_ID for both Vendor-ID and Device-ID) are now applied in all kernels that compile the PCI Tegra controlled driver, DT and ACPI alike. This is completely wrong, in that enablement of Relaxed Ordering is only required by default in Tegra20 platforms as described in the Tegra20 Technical Reference Manual (available at https://developer.nvidia.com/embedded/downloads#?search=tegra%202 in Section 34.1, where it is mentioned that Relaxed Ordering bit needs to be enabled in its root ports to avoid deadlock in hardware) and in the Tegra30 platforms for the same reasons (unfortunately not documented in the TRM). There is no other strict requirement on PCI devices Relaxed Ordering enablement on any other Tegra platforms or PCI host bridge driver. Fix this quite upsetting situation by limiting the vendor and device IDs to which the Relaxed Ordering quirk applies to the root ports in question, reported above. Signed-off-by: Vidya Sagar [lorenzo.pieralisi@arm.com: completely rewrote the commit log/fixes tag] Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- drivers/pci/controller/pci-tegra.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 37c694dcbc9e..9a917b2456f6 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -787,12 +787,15 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1c, tegra_pcie_fixup_class); DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0e1d, tegra_pcie_fixup_class); -/* Tegra PCIE requires relaxed ordering */ +/* Tegra20 and Tegra30 PCIE requires relaxed ordering */ static void tegra_pcie_relax_enable(struct pci_dev *dev) { pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); } -DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_relax_enable); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_relax_enable); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, 0x0e1c, tegra_pcie_relax_enable); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, 0x0e1d, tegra_pcie_relax_enable); static int tegra_pcie_request_resources(struct tegra_pcie *pcie) { -- cgit v1.2.3-55-g7522 From 4df591b20b80cb77920953812d894db259d85bd7 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Fri, 21 Jun 2019 23:45:23 +0000 Subject: PCI: hv: Fix a use-after-free bug in hv_eject_device_work() Fix a use-after-free in hv_eject_device_work(). Fixes: 05f151a73ec2 ("PCI: hv: Fix a memory leak in hv_eject_device_work()") Signed-off-by: Dexuan Cui Signed-off-by: Lorenzo Pieralisi Reviewed-by: Michael Kelley Cc: stable@vger.kernel.org --- drivers/pci/controller/pci-hyperv.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index 82acd6155adf..40b625458afa 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -1875,6 +1875,7 @@ static void hv_pci_devices_present(struct hv_pcibus_device *hbus, static void hv_eject_device_work(struct work_struct *work) { struct pci_eject_response *ejct_pkt; + struct hv_pcibus_device *hbus; struct hv_pci_dev *hpdev; struct pci_dev *pdev; unsigned long flags; @@ -1885,6 +1886,7 @@ static void hv_eject_device_work(struct work_struct *work) } ctxt; hpdev = container_of(work, struct hv_pci_dev, wrk); + hbus = hpdev->hbus; WARN_ON(hpdev->state != hv_pcichild_ejecting); @@ -1895,8 +1897,7 @@ static void hv_eject_device_work(struct work_struct *work) * because hbus->pci_bus may not exist yet. */ wslot = wslot_to_devfn(hpdev->desc.win_slot.slot); - pdev = pci_get_domain_bus_and_slot(hpdev->hbus->sysdata.domain, 0, - wslot); + pdev = pci_get_domain_bus_and_slot(hbus->sysdata.domain, 0, wslot); if (pdev) { pci_lock_rescan_remove(); pci_stop_and_remove_bus_device(pdev); @@ -1904,9 +1905,9 @@ static void hv_eject_device_work(struct work_struct *work) pci_unlock_rescan_remove(); } - spin_lock_irqsave(&hpdev->hbus->device_list_lock, flags); + spin_lock_irqsave(&hbus->device_list_lock, flags); list_del(&hpdev->list_entry); - spin_unlock_irqrestore(&hpdev->hbus->device_list_lock, flags); + spin_unlock_irqrestore(&hbus->device_list_lock, flags); if (hpdev->pci_slot) pci_destroy_slot(hpdev->pci_slot); @@ -1915,7 +1916,7 @@ static void hv_eject_device_work(struct work_struct *work) ejct_pkt = (struct pci_eject_response *)&ctxt.pkt.message; ejct_pkt->message_type.type = PCI_EJECTION_COMPLETE; ejct_pkt->wslot.slot = hpdev->desc.win_slot.slot; - vmbus_sendpacket(hpdev->hbus->hdev->channel, ejct_pkt, + vmbus_sendpacket(hbus->hdev->channel, ejct_pkt, sizeof(*ejct_pkt), (unsigned long)&ctxt.pkt, VM_PKT_DATA_INBAND, 0); @@ -1924,7 +1925,9 @@ static void hv_eject_device_work(struct work_struct *work) /* For the two refs got in new_pcichild_device() */ put_pcichild(hpdev); put_pcichild(hpdev); - put_hvpcibus(hpdev->hbus); + /* hpdev has been freed. Do not use it any more. */ + + put_hvpcibus(hbus); } /** -- cgit v1.2.3-55-g7522 From 7e8ab1b268b1ab2d2956823f93c7f3c62b9c2b26 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 11 Mar 2019 18:59:28 +0000 Subject: PCI: imx6: Simplify Kconfig depends on The imx6 driver can be used on imx6sx without enabling support for imx6q or imx7d but the "depends on" condition doesn't allow that. Instead of making the condition even longer just make it depend on "ARCH_MXC || COMPILE_TEST" instead. Signed-off-by: Leonard Crestez Signed-off-by: Lorenzo Pieralisi Acked-by: Lucas Stach --- drivers/pci/controller/dwc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index a6ce1ee51b4c..6ea778ae4877 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -90,7 +90,7 @@ config PCI_EXYNOS config PCI_IMX6 bool "Freescale i.MX6/7/8 PCIe controller" - depends on SOC_IMX6Q || SOC_IMX7D || (ARM64 && ARCH_MXC) || COMPILE_TEST + depends on ARCH_MXC || COMPILE_TEST depends on PCI_MSI_IRQ_DOMAIN select PCIE_DW_HOST -- cgit v1.2.3-55-g7522 From bcbe0d9a8d931597202859d13506611dd9736da9 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:29 +0800 Subject: PCI: mobiveil: Unify register accessors It is confusing to have two sets of functions to read/write registers, some with csr_readl()/csr_writel(), while others with read_paged_register()/write_paged_register(). In the register space the lower 3KB of 4KB PCIe configure space can be accessed directly and higher 1KB through a simple paging mechanism. Unify the register accessors in csr_readl() and csr_writel() by comparing the register offset with page access boundary 3KB in the accessor internal so that the paging mechanism is hidden behind the csr_read()/write() common function calls. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 179 +++++++++++++++++++++++---------- 1 file changed, 124 insertions(+), 55 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 77052a0712d0..d55c7e780c6e 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -47,7 +47,6 @@ #define PAGE_SEL_SHIFT 13 #define PAGE_SEL_MASK 0x3f #define PAGE_LO_MASK 0x3ff -#define PAGE_SEL_EN 0xc00 #define PAGE_SEL_OFFSET_SHIFT 10 #define PAB_AXI_PIO_CTRL 0x0840 @@ -117,6 +116,12 @@ #define LINK_WAIT_MIN 90000 #define LINK_WAIT_MAX 100000 +#define PAGED_ADDR_BNDRY 0xc00 +#define OFFSET_TO_PAGE_ADDR(off) \ + ((off & PAGE_LO_MASK) | PAGED_ADDR_BNDRY) +#define OFFSET_TO_PAGE_IDX(off) \ + ((off >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK) + struct mobiveil_msi { /* MSI information */ struct mutex lock; /* protect bitmap variable */ struct irq_domain *msi_domain; @@ -145,15 +150,119 @@ struct mobiveil_pcie { struct mobiveil_msi msi; }; -static inline void csr_writel(struct mobiveil_pcie *pcie, const u32 value, - const u32 reg) +/* + * mobiveil_pcie_sel_page - routine to access paged register + * + * Registers whose address greater than PAGED_ADDR_BNDRY (0xc00) are paged, + * for this scheme to work extracted higher 6 bits of the offset will be + * written to pg_sel field of PAB_CTRL register and rest of the lower 10 + * bits enabled with PAGED_ADDR_BNDRY are used as offset of the register. + */ +static void mobiveil_pcie_sel_page(struct mobiveil_pcie *pcie, u8 pg_idx) { - writel_relaxed(value, pcie->csr_axi_slave_base + reg); + u32 val; + + val = readl(pcie->csr_axi_slave_base + PAB_CTRL); + val &= ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT); + val |= (pg_idx & PAGE_SEL_MASK) << PAGE_SEL_SHIFT; + + writel(val, pcie->csr_axi_slave_base + PAB_CTRL); } -static inline u32 csr_readl(struct mobiveil_pcie *pcie, const u32 reg) +static void *mobiveil_pcie_comp_addr(struct mobiveil_pcie *pcie, u32 off) { - return readl_relaxed(pcie->csr_axi_slave_base + reg); + if (off < PAGED_ADDR_BNDRY) { + /* For directly accessed registers, clear the pg_sel field */ + mobiveil_pcie_sel_page(pcie, 0); + return pcie->csr_axi_slave_base + off; + } + + mobiveil_pcie_sel_page(pcie, OFFSET_TO_PAGE_IDX(off)); + return pcie->csr_axi_slave_base + OFFSET_TO_PAGE_ADDR(off); +} + +static int mobiveil_pcie_read(void __iomem *addr, int size, u32 *val) +{ + if ((uintptr_t)addr & (size - 1)) { + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + switch (size) { + case 4: + *val = readl(addr); + break; + case 2: + *val = readw(addr); + break; + case 1: + *val = readb(addr); + break; + default: + *val = 0; + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int mobiveil_pcie_write(void __iomem *addr, int size, u32 val) +{ + if ((uintptr_t)addr & (size - 1)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + switch (size) { + case 4: + writel(val, addr); + break; + case 2: + writew(val, addr); + break; + case 1: + writeb(val, addr); + break; + default: + return PCIBIOS_BAD_REGISTER_NUMBER; + } + + return PCIBIOS_SUCCESSFUL; +} + +static u32 csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size) +{ + void *addr; + u32 val; + int ret; + + addr = mobiveil_pcie_comp_addr(pcie, off); + + ret = mobiveil_pcie_read(addr, size, &val); + if (ret) + dev_err(&pcie->pdev->dev, "read CSR address failed\n"); + + return val; +} + +static void csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off, size_t size) +{ + void *addr; + int ret; + + addr = mobiveil_pcie_comp_addr(pcie, off); + + ret = mobiveil_pcie_write(addr, size, val); + if (ret) + dev_err(&pcie->pdev->dev, "write CSR address failed\n"); +} + +static u32 csr_readl(struct mobiveil_pcie *pcie, u32 off) +{ + return csr_read(pcie, off, 0x4); +} + +static void csr_writel(struct mobiveil_pcie *pcie, u32 val, u32 off) +{ + csr_write(pcie, val, off, 0x4); } static bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie) @@ -342,45 +451,6 @@ static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie) return 0; } -/* - * select_paged_register - routine to access paged register of root complex - * - * registers of RC are paged, for this scheme to work - * extracted higher 6 bits of the offset will be written to pg_sel - * field of PAB_CTRL register and rest of the lower 10 bits enabled with - * PAGE_SEL_EN are used as offset of the register. - */ -static void select_paged_register(struct mobiveil_pcie *pcie, u32 offset) -{ - int pab_ctrl_dw, pg_sel; - - /* clear pg_sel field */ - pab_ctrl_dw = csr_readl(pcie, PAB_CTRL); - pab_ctrl_dw = (pab_ctrl_dw & ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT)); - - /* set pg_sel field */ - pg_sel = (offset >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK; - pab_ctrl_dw |= ((pg_sel << PAGE_SEL_SHIFT)); - csr_writel(pcie, pab_ctrl_dw, PAB_CTRL); -} - -static void write_paged_register(struct mobiveil_pcie *pcie, - u32 val, u32 offset) -{ - u32 off = (offset & PAGE_LO_MASK) | PAGE_SEL_EN; - - select_paged_register(pcie, offset); - csr_writel(pcie, val, off); -} - -static u32 read_paged_register(struct mobiveil_pcie *pcie, u32 offset) -{ - u32 off = (offset & PAGE_LO_MASK) | PAGE_SEL_EN; - - select_paged_register(pcie, offset); - return csr_readl(pcie, off); -} - static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, int pci_addr, u32 type, u64 size) { @@ -397,19 +467,19 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, pio_ctrl_val = csr_readl(pcie, PAB_PEX_PIO_CTRL); csr_writel(pcie, pio_ctrl_val | (1 << PIO_ENABLE_SHIFT), PAB_PEX_PIO_CTRL); - amap_ctrl_dw = read_paged_register(pcie, PAB_PEX_AMAP_CTRL(win_num)); + amap_ctrl_dw = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); amap_ctrl_dw = (amap_ctrl_dw | (type << AMAP_CTRL_TYPE_SHIFT)); amap_ctrl_dw = (amap_ctrl_dw | (1 << AMAP_CTRL_EN_SHIFT)); - write_paged_register(pcie, amap_ctrl_dw | lower_32_bits(size64), - PAB_PEX_AMAP_CTRL(win_num)); + csr_writel(pcie, amap_ctrl_dw | lower_32_bits(size64), + PAB_PEX_AMAP_CTRL(win_num)); - write_paged_register(pcie, upper_32_bits(size64), - PAB_EXT_PEX_AMAP_SIZEN(win_num)); + csr_writel(pcie, upper_32_bits(size64), + PAB_EXT_PEX_AMAP_SIZEN(win_num)); - write_paged_register(pcie, pci_addr, PAB_PEX_AMAP_AXI_WIN(win_num)); - write_paged_register(pcie, pci_addr, PAB_PEX_AMAP_PEX_WIN_L(win_num)); - write_paged_register(pcie, 0, PAB_PEX_AMAP_PEX_WIN_H(win_num)); + csr_writel(pcie, pci_addr, PAB_PEX_AMAP_AXI_WIN(win_num)); + csr_writel(pcie, pci_addr, PAB_PEX_AMAP_PEX_WIN_L(win_num)); + csr_writel(pcie, 0, PAB_PEX_AMAP_PEX_WIN_H(win_num)); } /* @@ -437,8 +507,7 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, csr_writel(pcie, 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | lower_32_bits(size64), PAB_AXI_AMAP_CTRL(win_num)); - write_paged_register(pcie, upper_32_bits(size64), - PAB_EXT_AXI_AMAP_SIZE(win_num)); + csr_writel(pcie, upper_32_bits(size64), PAB_EXT_AXI_AMAP_SIZE(win_num)); /* * program AXI window base with appropriate value in -- cgit v1.2.3-55-g7522 From a131fb6364c1be0924dcb969ecf6b988c556a5d5 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:30 +0800 Subject: PCI: mobiveil: Remove the flag MSI_FLAG_MULTI_PCI_MSI The Mobiveil internal MSI controller requires separate target addresses, one per MSI vector; this is clearly incompatible with the Multiple MSI feature, which requires the same target address for all vectors requested by an endpoint (ie the Message Address field in the MSI Capability structure), so the multi MSI feature is clearly not supported by the host controller driver. Remove the flag MSI_FLAG_MULTI_PCI_MSI and with it multi MSI support, fixing the misconfiguration. Fixes: 1e913e58335f ("PCI: mobiveil: Add MSI support") Signed-off-by: Hou Zhiqiang [lorenzo.pieralisi@arm.com: commit log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian --- drivers/pci/controller/pcie-mobiveil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index d55c7e780c6e..e5819029ad8e 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -692,7 +692,7 @@ static struct irq_chip mobiveil_msi_irq_chip = { static struct msi_domain_info mobiveil_msi_domain_info = { .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_MULTI_PCI_MSI | MSI_FLAG_PCI_MSIX), + MSI_FLAG_PCI_MSIX), .chip = &mobiveil_msi_irq_chip, }; -- cgit v1.2.3-55-g7522 From f99536e9d2f55996038158a6559d4254a7cc1693 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:31 +0800 Subject: PCI: mobiveil: Fix PCI base address in MEM/IO outbound windows The outbound memory windows PCI base addresses should be taken from the 'ranges' property of DT node to setup MEM/IO outbound windows decoding correctly instead of being hardcoded to zero. Update the code to retrieve the PCI base address for each range and use it to program the outbound windows address decoders Fixes: 9af6bcb11e12 ("PCI: mobiveil: Add Mobiveil PCIe Host Bridge IP driver") Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index e5819029ad8e..53df31700551 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -621,8 +621,9 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) if (type) { /* configure outbound translation window */ program_ob_windows(pcie, pcie->ob_wins_configured, - win->res->start, 0, type, - resource_size(win->res)); + win->res->start, + win->res->start - win->offset, + type, resource_size(win->res)); } } -- cgit v1.2.3-55-g7522 From 9815d3288ed0540efc5f261ea1736f4327c4c9b5 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:32 +0800 Subject: PCI: mobiveil: Update the resource list traversal function Code that executes the resource list traversal does not need to delete any node therefore using the *_safe() API version is useless. Replace function resource_list_for_each_entry_safe() with the resource_list_for_each_entry() counterpart. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 53df31700551..c359654abc30 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -562,7 +562,7 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) { u32 value, pab_ctrl, type = 0; int err; - struct resource_entry *win, *tmp; + struct resource_entry *win; err = mobiveil_bringup_link(pcie); if (err) { @@ -612,7 +612,7 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) program_ib_windows(pcie, WIN_NUM_1, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE); /* Get the I/O and memory ranges from DT */ - resource_list_for_each_entry_safe(win, tmp, &pcie->resources) { + resource_list_for_each_entry(win, &pcie->resources) { type = 0; if (resource_type(win->res) == IORESOURCE_MEM) type = MEM_WINDOW_TYPE; -- cgit v1.2.3-55-g7522 From f39ed3d09b34f733bb12178712019050bd8bd38d Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:33 +0800 Subject: PCI: mobiveil: Use WIN_NUM_0 explicitly for CFG outbound window As the .map_bus() use the WIN_NUM_0 for CFG transactions, it is appropriate to pass WIN_NUM_0 explicitly when initializing the CFG outbound window rather than implicitly relying on the ob_wins_configure counter. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index c359654abc30..0f41c238fa18 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -604,9 +604,8 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) */ /* config outbound translation window */ - program_ob_windows(pcie, pcie->ob_wins_configured, - pcie->ob_io_res->start, 0, CFG_WINDOW_TYPE, - resource_size(pcie->ob_io_res)); + program_ob_windows(pcie, WIN_NUM_0, pcie->ob_io_res->start, 0, + CFG_WINDOW_TYPE, resource_size(pcie->ob_io_res)); /* memory inbound translation window */ program_ib_windows(pcie, WIN_NUM_1, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE); -- cgit v1.2.3-55-g7522 From f7fee1b42fe4f8171a4b1cad05c61907c33c53f6 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:34 +0800 Subject: PCI: mobiveil: Use the 1st inbound window for MEM inbound transactions The inbound and outbound windows have completely separate control registers sets in the host controller MMIO space. Windows control register are accessed through an MMIO base address and an offset that depends on the window index. Since inbound and outbound windows control registers are completely separate there is no real need to use different window indexes in the inbound/outbound windows initialization routines to prevent clashing. To fix this inconsistency, change the MEM inbound window index to 0, mirroring the outbound window set-up. Signed-off-by: Hou Zhiqiang [lorenzo.pieralisi@arm.com: update commit log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 0f41c238fa18..827218314ef3 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -608,7 +608,7 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) CFG_WINDOW_TYPE, resource_size(pcie->ob_io_res)); /* memory inbound translation window */ - program_ib_windows(pcie, WIN_NUM_1, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE); + program_ib_windows(pcie, WIN_NUM_0, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE); /* Get the I/O and memory ranges from DT */ resource_list_for_each_entry(win, &pcie->resources) { -- cgit v1.2.3-55-g7522 From 0122af0a08243f344a438f924e5c2486486555b3 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:35 +0800 Subject: PCI: mobiveil: Fix the Class Code field Fix up the Class Code field in PCI configuration space and set it to PCI_CLASS_BRIDGE_PCI. Move the Class Code fixup to function mobiveil_host_init() where it belongs. Fixes: 9af6bcb11e12 ("PCI: mobiveil: Add Mobiveil PCIe Host Bridge IP driver") Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 827218314ef3..9e080612a97b 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -626,6 +626,12 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) } } + /* fixup for PCIe class register */ + value = csr_readl(pcie, PAB_INTP_AXI_PIO_CLASS); + value &= 0xff; + value |= (PCI_CLASS_BRIDGE_PCI << 16); + csr_writel(pcie, value, PAB_INTP_AXI_PIO_CLASS); + /* setup MSI hardware registers */ mobiveil_pcie_enable_msi(pcie); @@ -866,9 +872,6 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) goto error; } - /* fixup for PCIe class register */ - csr_writel(pcie, 0x060402ab, PAB_INTP_AXI_PIO_CLASS); - /* initialize the IRQ domains */ ret = mobiveil_pcie_init_irq_domain(pcie); if (ret) { -- cgit v1.2.3-55-g7522 From 40af900c5f12ea3e4eda2688050a22dc1fee42d1 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:36 +0800 Subject: PCI: mobiveil: Move the link up waiting out of mobiveil_host_init() The host initializing sequence does not depend on the PCIe link being up, so move the link up check right before the code scanning the root bus for enumeration. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 9e080612a97b..8f92f05c9211 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -561,15 +561,8 @@ static void mobiveil_pcie_enable_msi(struct mobiveil_pcie *pcie) static int mobiveil_host_init(struct mobiveil_pcie *pcie) { u32 value, pab_ctrl, type = 0; - int err; struct resource_entry *win; - err = mobiveil_bringup_link(pcie); - if (err) { - dev_info(&pcie->pdev->dev, "link bring-up failed\n"); - return err; - } - /* * program Bus Master Enable Bit in Command Register in PAB Config * Space @@ -635,7 +628,7 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) /* setup MSI hardware registers */ mobiveil_pcie_enable_msi(pcie); - return err; + return 0; } static void mobiveil_mask_intx_irq(struct irq_data *data) @@ -892,6 +885,12 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) bridge->map_irq = of_irq_parse_and_map_pci; bridge->swizzle_irq = pci_common_swizzle; + ret = mobiveil_bringup_link(pcie); + if (ret) { + dev_info(dev, "link bring-up failed\n"); + goto error; + } + /* setup the kernel resources for the newly added PCIe root bus */ ret = pci_scan_root_bus_bridge(bridge); if (ret) -- cgit v1.2.3-55-g7522 From 284441a9662cd3352396cf7d74edd9270f641916 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:37 +0800 Subject: PCI: mobiveil: Move IRQ chained handler setup out of DT parse The irq_set_chained_handler_and_data() call is not dependent on device tree firmware so it should be moved out of the DT parsing function for clarity. Signed-off-by: Hou Zhiqiang [lorenzo.pieralisi@arm.com: rewritten commit log] Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 8f92f05c9211..cdf15cca526e 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -446,8 +446,6 @@ static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie) return -ENODEV; } - irq_set_chained_handler_and_data(pcie->irq, mobiveil_pcie_isr, pcie); - return 0; } @@ -872,6 +870,8 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) goto error; } + irq_set_chained_handler_and_data(pcie->irq, mobiveil_pcie_isr, pcie); + ret = devm_request_pci_bus_resources(dev, &pcie->resources); if (ret) goto error; -- cgit v1.2.3-55-g7522 From 6f3ab451aa5c2cbff33197d82fe8489cbd55ad91 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:38 +0800 Subject: PCI: mobiveil: Initialize Primary/Secondary/Subordinate bus numbers The reset value of Primary, Secondary and Subordinate bus numbers is zero which is a broken setup. Program a sensible default value for Primary/Secondary/Subordinate bus numbers. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index cdf15cca526e..8f561300a88e 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -561,6 +561,12 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) u32 value, pab_ctrl, type = 0; struct resource_entry *win; + /* setup bus numbers */ + value = csr_readl(pcie, PCI_PRIMARY_BUS); + value &= 0xff000000; + value |= 0x00ff0100; + csr_writel(pcie, value, PCI_PRIMARY_BUS); + /* * program Bus Master Enable Bit in Command Register in PAB Config * Space -- cgit v1.2.3-55-g7522 From cbd50b3ca3964c79dac65fda277637577e029e8c Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:39 +0800 Subject: PCI: mobiveil: Fix devfn check in mobiveil_pcie_valid_device() Current check for devfn number in mobiveil_pci_valid_device() is wrong in that it flags as invalid functions present in PCI device 0 in the root bus while it is perfectly valid to access all functions in PCI device 0 in the root bus. Update the check in mobiveil_pci_valid_device() to fix the issue. Fixes: 9af6bcb11e12 ("PCI: mobiveil: Add Mobiveil PCIe Host Bridge IP driver") Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian --- drivers/pci/controller/pcie-mobiveil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 8f561300a88e..c9bf5658c4e7 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -283,7 +283,7 @@ static bool mobiveil_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) * Do not read more than one device on the bus directly * attached to RC */ - if ((bus->primary == pcie->root_bus_nr) && (devfn > 0)) + if ((bus->primary == pcie->root_bus_nr) && (PCI_SLOT(devfn) > 0)) return false; return true; -- cgit v1.2.3-55-g7522 From e369faf6255df555644b33a22931dddff5e24755 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:41 +0800 Subject: PCI: mobiveil: Reformat the code for readability Reformat the code to make it more readable. No functional change intended. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian --- drivers/pci/controller/pcie-mobiveil.c | 208 +++++++++++++++++---------------- 1 file changed, 107 insertions(+), 101 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index c9bf5658c4e7..0767f1906fe6 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -31,38 +31,40 @@ * translation tables are grouped into windows, each window registers are * grouped into blocks of 4 or 16 registers each */ -#define PAB_REG_BLOCK_SIZE 16 -#define PAB_EXT_REG_BLOCK_SIZE 4 +#define PAB_REG_BLOCK_SIZE 16 +#define PAB_EXT_REG_BLOCK_SIZE 4 -#define PAB_REG_ADDR(offset, win) (offset + (win * PAB_REG_BLOCK_SIZE)) -#define PAB_EXT_REG_ADDR(offset, win) (offset + (win * PAB_EXT_REG_BLOCK_SIZE)) +#define PAB_REG_ADDR(offset, win) \ + (offset + (win * PAB_REG_BLOCK_SIZE)) +#define PAB_EXT_REG_ADDR(offset, win) \ + (offset + (win * PAB_EXT_REG_BLOCK_SIZE)) -#define LTSSM_STATUS 0x0404 -#define LTSSM_STATUS_L0_MASK 0x3f -#define LTSSM_STATUS_L0 0x2d +#define LTSSM_STATUS 0x0404 +#define LTSSM_STATUS_L0_MASK 0x3f +#define LTSSM_STATUS_L0 0x2d -#define PAB_CTRL 0x0808 -#define AMBA_PIO_ENABLE_SHIFT 0 -#define PEX_PIO_ENABLE_SHIFT 1 -#define PAGE_SEL_SHIFT 13 -#define PAGE_SEL_MASK 0x3f -#define PAGE_LO_MASK 0x3ff -#define PAGE_SEL_OFFSET_SHIFT 10 +#define PAB_CTRL 0x0808 +#define AMBA_PIO_ENABLE_SHIFT 0 +#define PEX_PIO_ENABLE_SHIFT 1 +#define PAGE_SEL_SHIFT 13 +#define PAGE_SEL_MASK 0x3f +#define PAGE_LO_MASK 0x3ff +#define PAGE_SEL_OFFSET_SHIFT 10 -#define PAB_AXI_PIO_CTRL 0x0840 -#define APIO_EN_MASK 0xf +#define PAB_AXI_PIO_CTRL 0x0840 +#define APIO_EN_MASK 0xf -#define PAB_PEX_PIO_CTRL 0x08c0 -#define PIO_ENABLE_SHIFT 0 +#define PAB_PEX_PIO_CTRL 0x08c0 +#define PIO_ENABLE_SHIFT 0 #define PAB_INTP_AMBA_MISC_ENB 0x0b0c -#define PAB_INTP_AMBA_MISC_STAT 0x0b1c +#define PAB_INTP_AMBA_MISC_STAT 0x0b1c #define PAB_INTP_INTX_MASK 0x01e0 #define PAB_INTP_MSI_MASK 0x8 -#define PAB_AXI_AMAP_CTRL(win) PAB_REG_ADDR(0x0ba0, win) -#define WIN_ENABLE_SHIFT 0 -#define WIN_TYPE_SHIFT 1 +#define PAB_AXI_AMAP_CTRL(win) PAB_REG_ADDR(0x0ba0, win) +#define WIN_ENABLE_SHIFT 0 +#define WIN_TYPE_SHIFT 1 #define PAB_EXT_AXI_AMAP_SIZE(win) PAB_EXT_REG_ADDR(0xbaf0, win) @@ -70,16 +72,16 @@ #define AXI_WINDOW_ALIGN_MASK 3 #define PAB_AXI_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x0ba8, win) -#define PAB_BUS_SHIFT 24 -#define PAB_DEVICE_SHIFT 19 -#define PAB_FUNCTION_SHIFT 16 +#define PAB_BUS_SHIFT 24 +#define PAB_DEVICE_SHIFT 19 +#define PAB_FUNCTION_SHIFT 16 #define PAB_AXI_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x0bac, win) #define PAB_INTP_AXI_PIO_CLASS 0x474 -#define PAB_PEX_AMAP_CTRL(win) PAB_REG_ADDR(0x4ba0, win) -#define AMAP_CTRL_EN_SHIFT 0 -#define AMAP_CTRL_TYPE_SHIFT 1 +#define PAB_PEX_AMAP_CTRL(win) PAB_REG_ADDR(0x4ba0, win) +#define AMAP_CTRL_EN_SHIFT 0 +#define AMAP_CTRL_TYPE_SHIFT 1 #define PAB_EXT_PEX_AMAP_SIZEN(win) PAB_EXT_REG_ADDR(0xbef0, win) #define PAB_PEX_AMAP_AXI_WIN(win) PAB_REG_ADDR(0x4ba4, win) @@ -87,39 +89,39 @@ #define PAB_PEX_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x4bac, win) /* starting offset of INTX bits in status register */ -#define PAB_INTX_START 5 +#define PAB_INTX_START 5 /* supported number of MSI interrupts */ -#define PCI_NUM_MSI 16 +#define PCI_NUM_MSI 16 /* MSI registers */ -#define MSI_BASE_LO_OFFSET 0x04 -#define MSI_BASE_HI_OFFSET 0x08 -#define MSI_SIZE_OFFSET 0x0c -#define MSI_ENABLE_OFFSET 0x14 -#define MSI_STATUS_OFFSET 0x18 -#define MSI_DATA_OFFSET 0x20 -#define MSI_ADDR_L_OFFSET 0x24 -#define MSI_ADDR_H_OFFSET 0x28 +#define MSI_BASE_LO_OFFSET 0x04 +#define MSI_BASE_HI_OFFSET 0x08 +#define MSI_SIZE_OFFSET 0x0c +#define MSI_ENABLE_OFFSET 0x14 +#define MSI_STATUS_OFFSET 0x18 +#define MSI_DATA_OFFSET 0x20 +#define MSI_ADDR_L_OFFSET 0x24 +#define MSI_ADDR_H_OFFSET 0x28 /* outbound and inbound window definitions */ -#define WIN_NUM_0 0 -#define WIN_NUM_1 1 -#define CFG_WINDOW_TYPE 0 -#define IO_WINDOW_TYPE 1 -#define MEM_WINDOW_TYPE 2 -#define IB_WIN_SIZE ((u64)256 * 1024 * 1024 * 1024) -#define MAX_PIO_WINDOWS 8 +#define WIN_NUM_0 0 +#define WIN_NUM_1 1 +#define CFG_WINDOW_TYPE 0 +#define IO_WINDOW_TYPE 1 +#define MEM_WINDOW_TYPE 2 +#define IB_WIN_SIZE ((u64)256 * 1024 * 1024 * 1024) +#define MAX_PIO_WINDOWS 8 /* Parameters for the waiting for link up routine */ -#define LINK_WAIT_MAX_RETRIES 10 -#define LINK_WAIT_MIN 90000 -#define LINK_WAIT_MAX 100000 +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_MIN 90000 +#define LINK_WAIT_MAX 100000 -#define PAGED_ADDR_BNDRY 0xc00 -#define OFFSET_TO_PAGE_ADDR(off) \ +#define PAGED_ADDR_BNDRY 0xc00 +#define OFFSET_TO_PAGE_ADDR(off) \ ((off & PAGE_LO_MASK) | PAGED_ADDR_BNDRY) -#define OFFSET_TO_PAGE_IDX(off) \ +#define OFFSET_TO_PAGE_IDX(off) \ ((off >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK) struct mobiveil_msi { /* MSI information */ @@ -294,17 +296,16 @@ static bool mobiveil_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) * root port or endpoint */ static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus, - unsigned int devfn, int where) + unsigned int devfn, int where) { struct mobiveil_pcie *pcie = bus->sysdata; if (!mobiveil_pcie_valid_device(bus, devfn)) return NULL; - if (bus->number == pcie->root_bus_nr) { - /* RC config access */ + /* RC config access */ + if (bus->number == pcie->root_bus_nr) return pcie->csr_axi_slave_base + where; - } /* * EP config access (in Config/APIO space) @@ -313,9 +314,9 @@ static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus, * Relies on pci_lock serialization */ csr_writel(pcie, bus->number << PAB_BUS_SHIFT | - PCI_SLOT(devfn) << PAB_DEVICE_SHIFT | - PCI_FUNC(devfn) << PAB_FUNCTION_SHIFT, - PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0)); + PCI_SLOT(devfn) << PAB_DEVICE_SHIFT | + PCI_FUNC(devfn) << PAB_FUNCTION_SHIFT, + PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0)); return pcie->config_axi_slave_base + where; } @@ -351,21 +352,21 @@ static void mobiveil_pcie_isr(struct irq_desc *desc) /* Handle INTx */ if (intr_status & PAB_INTP_INTX_MASK) { shifted_status = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT) >> - PAB_INTX_START; + PAB_INTX_START; do { for_each_set_bit(bit, &shifted_status, PCI_NUM_INTX) { virq = irq_find_mapping(pcie->intx_domain, - bit + 1); + bit + 1); if (virq) generic_handle_irq(virq); else - dev_err_ratelimited(dev, - "unexpected IRQ, INT%d\n", bit); + dev_err_ratelimited(dev, "unexpected IRQ, INT%d\n", + bit); /* clear interrupt */ csr_writel(pcie, - shifted_status << PAB_INTX_START, - PAB_INTP_AMBA_MISC_STAT); + shifted_status << PAB_INTX_START, + PAB_INTP_AMBA_MISC_STAT); } } while ((shifted_status >> PAB_INTX_START) != 0); } @@ -375,8 +376,7 @@ static void mobiveil_pcie_isr(struct irq_desc *desc) /* handle MSI interrupts */ while (msi_status & 1) { - msi_data = readl_relaxed(pcie->apb_csr_base - + MSI_DATA_OFFSET); + msi_data = readl_relaxed(pcie->apb_csr_base + MSI_DATA_OFFSET); /* * MSI_STATUS_OFFSET register gets updated to zero @@ -385,18 +385,18 @@ static void mobiveil_pcie_isr(struct irq_desc *desc) * two dummy reads. */ msi_addr_lo = readl_relaxed(pcie->apb_csr_base + - MSI_ADDR_L_OFFSET); + MSI_ADDR_L_OFFSET); msi_addr_hi = readl_relaxed(pcie->apb_csr_base + - MSI_ADDR_H_OFFSET); + MSI_ADDR_H_OFFSET); dev_dbg(dev, "MSI registers, data: %08x, addr: %08x:%08x\n", - msi_data, msi_addr_hi, msi_addr_lo); + msi_data, msi_addr_hi, msi_addr_lo); virq = irq_find_mapping(msi->dev_domain, msi_data); if (virq) generic_handle_irq(virq); msi_status = readl_relaxed(pcie->apb_csr_base + - MSI_STATUS_OFFSET); + MSI_STATUS_OFFSET); } /* Clear the interrupt status */ @@ -413,7 +413,7 @@ static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie) /* map config resource */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "config_axi_slave"); + "config_axi_slave"); pcie->config_axi_slave_base = devm_pci_remap_cfg_resource(dev, res); if (IS_ERR(pcie->config_axi_slave_base)) return PTR_ERR(pcie->config_axi_slave_base); @@ -421,7 +421,7 @@ static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie) /* map csr resource */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "csr_axi_slave"); + "csr_axi_slave"); pcie->csr_axi_slave_base = devm_pci_remap_cfg_resource(dev, res); if (IS_ERR(pcie->csr_axi_slave_base)) return PTR_ERR(pcie->csr_axi_slave_base); @@ -450,7 +450,7 @@ static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie) } static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, - int pci_addr, u32 type, u64 size) + int pci_addr, u32 type, u64 size) { int pio_ctrl_val; int amap_ctrl_dw; @@ -463,8 +463,8 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, } pio_ctrl_val = csr_readl(pcie, PAB_PEX_PIO_CTRL); - csr_writel(pcie, - pio_ctrl_val | (1 << PIO_ENABLE_SHIFT), PAB_PEX_PIO_CTRL); + csr_writel(pcie, pio_ctrl_val | (1 << PIO_ENABLE_SHIFT), + PAB_PEX_PIO_CTRL); amap_ctrl_dw = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); amap_ctrl_dw = (amap_ctrl_dw | (type << AMAP_CTRL_TYPE_SHIFT)); amap_ctrl_dw = (amap_ctrl_dw | (1 << AMAP_CTRL_EN_SHIFT)); @@ -484,7 +484,8 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, * routine to program the outbound windows */ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, - u64 cpu_addr, u64 pci_addr, u32 config_io_bit, u64 size) + u64 cpu_addr, u64 pci_addr, + u32 config_io_bit, u64 size) { u32 value, type; @@ -503,7 +504,7 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, type = config_io_bit; value = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); csr_writel(pcie, 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | - lower_32_bits(size64), PAB_AXI_AMAP_CTRL(win_num)); + lower_32_bits(size64), PAB_AXI_AMAP_CTRL(win_num)); csr_writel(pcie, upper_32_bits(size64), PAB_EXT_AXI_AMAP_SIZE(win_num)); @@ -513,14 +514,14 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, */ value = csr_readl(pcie, PAB_AXI_AMAP_AXI_WIN(win_num)); csr_writel(pcie, cpu_addr & (~AXI_WINDOW_ALIGN_MASK), - PAB_AXI_AMAP_AXI_WIN(win_num)); + PAB_AXI_AMAP_AXI_WIN(win_num)); value = csr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_H(win_num)); csr_writel(pcie, lower_32_bits(pci_addr), - PAB_AXI_AMAP_PEX_WIN_L(win_num)); + PAB_AXI_AMAP_PEX_WIN_L(win_num)); csr_writel(pcie, upper_32_bits(pci_addr), - PAB_AXI_AMAP_PEX_WIN_H(win_num)); + PAB_AXI_AMAP_PEX_WIN_H(win_num)); pcie->ob_wins_configured++; } @@ -536,7 +537,9 @@ static int mobiveil_bringup_link(struct mobiveil_pcie *pcie) usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); } + dev_err(&pcie->pdev->dev, "link never came up\n"); + return -ETIMEDOUT; } @@ -549,9 +552,9 @@ static void mobiveil_pcie_enable_msi(struct mobiveil_pcie *pcie) msi->msi_pages_phys = (phys_addr_t)msg_addr; writel_relaxed(lower_32_bits(msg_addr), - pcie->apb_csr_base + MSI_BASE_LO_OFFSET); + pcie->apb_csr_base + MSI_BASE_LO_OFFSET); writel_relaxed(upper_32_bits(msg_addr), - pcie->apb_csr_base + MSI_BASE_HI_OFFSET); + pcie->apb_csr_base + MSI_BASE_HI_OFFSET); writel_relaxed(4096, pcie->apb_csr_base + MSI_SIZE_OFFSET); writel_relaxed(1, pcie->apb_csr_base + MSI_ENABLE_OFFSET); } @@ -573,7 +576,7 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) */ value = csr_readl(pcie, PCI_COMMAND); csr_writel(pcie, value | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER, PCI_COMMAND); + PCI_COMMAND_MASTER, PCI_COMMAND); /* * program PIO Enable Bit to 1 (and PEX PIO Enable to 1) in PAB_CTRL @@ -581,10 +584,10 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) */ pab_ctrl = csr_readl(pcie, PAB_CTRL); csr_writel(pcie, pab_ctrl | (1 << AMBA_PIO_ENABLE_SHIFT) | - (1 << PEX_PIO_ENABLE_SHIFT), PAB_CTRL); + (1 << PEX_PIO_ENABLE_SHIFT), PAB_CTRL); csr_writel(pcie, (PAB_INTP_INTX_MASK | PAB_INTP_MSI_MASK), - PAB_INTP_AMBA_MISC_ENB); + PAB_INTP_AMBA_MISC_ENB); /* * program PIO Enable Bit to 1 and Config Window Enable Bit to 1 in @@ -675,10 +678,11 @@ static struct irq_chip intx_irq_chip = { /* routine to setup the INTx related data */ static int mobiveil_pcie_intx_map(struct irq_domain *domain, unsigned int irq, - irq_hw_number_t hwirq) + irq_hw_number_t hwirq) { irq_set_chip_and_handler(irq, &intx_irq_chip, handle_level_irq); irq_set_chip_data(irq, domain->host_data); + return 0; } @@ -713,7 +717,7 @@ static void mobiveil_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) } static int mobiveil_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) + const struct cpumask *mask, bool force) { return -EINVAL; } @@ -725,7 +729,8 @@ static struct irq_chip mobiveil_msi_bottom_irq_chip = { }; static int mobiveil_irq_msi_domain_alloc(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs, void *args) + unsigned int virq, + unsigned int nr_irqs, void *args) { struct mobiveil_pcie *pcie = domain->host_data; struct mobiveil_msi *msi = &pcie->msi; @@ -745,13 +750,13 @@ static int mobiveil_irq_msi_domain_alloc(struct irq_domain *domain, mutex_unlock(&msi->lock); irq_domain_set_info(domain, virq, bit, &mobiveil_msi_bottom_irq_chip, - domain->host_data, handle_level_irq, - NULL, NULL); + domain->host_data, handle_level_irq, NULL, NULL); return 0; } static void mobiveil_irq_msi_domain_free(struct irq_domain *domain, - unsigned int virq, unsigned int nr_irqs) + unsigned int virq, + unsigned int nr_irqs) { struct irq_data *d = irq_domain_get_irq_data(domain, virq); struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(d); @@ -759,12 +764,11 @@ static void mobiveil_irq_msi_domain_free(struct irq_domain *domain, mutex_lock(&msi->lock); - if (!test_bit(d->hwirq, msi->msi_irq_in_use)) { + if (!test_bit(d->hwirq, msi->msi_irq_in_use)) dev_err(&pcie->pdev->dev, "trying to free unused MSI#%lu\n", d->hwirq); - } else { + else __clear_bit(d->hwirq, msi->msi_irq_in_use); - } mutex_unlock(&msi->lock); } @@ -788,12 +792,14 @@ static int mobiveil_allocate_msi_domains(struct mobiveil_pcie *pcie) } msi->msi_domain = pci_msi_create_irq_domain(fwnode, - &mobiveil_msi_domain_info, msi->dev_domain); + &mobiveil_msi_domain_info, + msi->dev_domain); if (!msi->msi_domain) { dev_err(dev, "failed to create MSI domain\n"); irq_domain_remove(msi->dev_domain); return -ENOMEM; } + return 0; } @@ -804,8 +810,8 @@ static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie) int ret; /* setup INTx */ - pcie->intx_domain = irq_domain_add_linear(node, - PCI_NUM_INTX, &intx_domain_ops, pcie); + pcie->intx_domain = irq_domain_add_linear(node, PCI_NUM_INTX, + &intx_domain_ops, pcie); if (!pcie->intx_domain) { dev_err(dev, "Failed to get a INTx IRQ domain\n"); @@ -925,10 +931,10 @@ MODULE_DEVICE_TABLE(of, mobiveil_pcie_of_match); static struct platform_driver mobiveil_pcie_driver = { .probe = mobiveil_pcie_probe, .driver = { - .name = "mobiveil-pcie", - .of_match_table = mobiveil_pcie_of_match, - .suppress_bind_attrs = true, - }, + .name = "mobiveil-pcie", + .of_match_table = mobiveil_pcie_of_match, + .suppress_bind_attrs = true, + }, }; builtin_platform_driver(mobiveil_pcie_driver); -- cgit v1.2.3-55-g7522 From 91347af1c5c32e1fe9a8294a526e1cfdfa7b6ac2 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:42 +0800 Subject: PCI: mobiveil: Make some register updates more readable To make some register updates more readable use a temporary value to hold the register value and carry out the update. Change the register update sequence to: - Read out the original value from the target register - Update the value - Program the updated value back to the register Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian --- drivers/pci/controller/pcie-mobiveil.c | 43 ++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 18 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 0767f1906fe6..906299b0d10e 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -299,6 +299,7 @@ static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, int where) { struct mobiveil_pcie *pcie = bus->sysdata; + u32 value; if (!mobiveil_pcie_valid_device(bus, devfn)) return NULL; @@ -313,10 +314,12 @@ static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus, * (BDF) in PAB_AXI_AMAP_PEX_WIN_L0 Register. * Relies on pci_lock serialization */ - csr_writel(pcie, bus->number << PAB_BUS_SHIFT | - PCI_SLOT(devfn) << PAB_DEVICE_SHIFT | - PCI_FUNC(devfn) << PAB_FUNCTION_SHIFT, - PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0)); + value = bus->number << PAB_BUS_SHIFT | + PCI_SLOT(devfn) << PAB_DEVICE_SHIFT | + PCI_FUNC(devfn) << PAB_FUNCTION_SHIFT; + + csr_writel(pcie, value, PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0)); + return pcie->config_axi_slave_base + where; } @@ -463,19 +466,20 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, } pio_ctrl_val = csr_readl(pcie, PAB_PEX_PIO_CTRL); - csr_writel(pcie, pio_ctrl_val | (1 << PIO_ENABLE_SHIFT), - PAB_PEX_PIO_CTRL); - amap_ctrl_dw = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); - amap_ctrl_dw = (amap_ctrl_dw | (type << AMAP_CTRL_TYPE_SHIFT)); - amap_ctrl_dw = (amap_ctrl_dw | (1 << AMAP_CTRL_EN_SHIFT)); + pio_ctrl_val |= 1 << PIO_ENABLE_SHIFT; + csr_writel(pcie, pio_ctrl_val, PAB_PEX_PIO_CTRL); - csr_writel(pcie, amap_ctrl_dw | lower_32_bits(size64), - PAB_PEX_AMAP_CTRL(win_num)); + amap_ctrl_dw = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); + amap_ctrl_dw |= (type << AMAP_CTRL_TYPE_SHIFT) | + (1 << AMAP_CTRL_EN_SHIFT) | + lower_32_bits(size64); + csr_writel(pcie, amap_ctrl_dw, PAB_PEX_AMAP_CTRL(win_num)); csr_writel(pcie, upper_32_bits(size64), PAB_EXT_PEX_AMAP_SIZEN(win_num)); csr_writel(pcie, pci_addr, PAB_PEX_AMAP_AXI_WIN(win_num)); + csr_writel(pcie, pci_addr, PAB_PEX_AMAP_PEX_WIN_L(win_num)); csr_writel(pcie, 0, PAB_PEX_AMAP_PEX_WIN_H(win_num)); } @@ -575,16 +579,16 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) * Space */ value = csr_readl(pcie, PCI_COMMAND); - csr_writel(pcie, value | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER, PCI_COMMAND); + value |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + csr_writel(pcie, value, PCI_COMMAND); /* * program PIO Enable Bit to 1 (and PEX PIO Enable to 1) in PAB_CTRL * register */ pab_ctrl = csr_readl(pcie, PAB_CTRL); - csr_writel(pcie, pab_ctrl | (1 << AMBA_PIO_ENABLE_SHIFT) | - (1 << PEX_PIO_ENABLE_SHIFT), PAB_CTRL); + pab_ctrl |= (1 << AMBA_PIO_ENABLE_SHIFT) | (1 << PEX_PIO_ENABLE_SHIFT); + csr_writel(pcie, pab_ctrl, PAB_CTRL); csr_writel(pcie, (PAB_INTP_INTX_MASK | PAB_INTP_MSI_MASK), PAB_INTP_AMBA_MISC_ENB); @@ -594,7 +598,8 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) * PAB_AXI_PIO_CTRL Register */ value = csr_readl(pcie, PAB_AXI_PIO_CTRL); - csr_writel(pcie, value | APIO_EN_MASK, PAB_AXI_PIO_CTRL); + value |= APIO_EN_MASK; + csr_writel(pcie, value, PAB_AXI_PIO_CTRL); /* * we'll program one outbound window for config reads and @@ -649,7 +654,8 @@ static void mobiveil_mask_intx_irq(struct irq_data *data) mask = 1 << ((data->hwirq + PAB_INTX_START) - 1); raw_spin_lock_irqsave(&pcie->intx_mask_lock, flags); shifted_val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); - csr_writel(pcie, (shifted_val & (~mask)), PAB_INTP_AMBA_MISC_ENB); + shifted_val &= ~mask; + csr_writel(pcie, shifted_val, PAB_INTP_AMBA_MISC_ENB); raw_spin_unlock_irqrestore(&pcie->intx_mask_lock, flags); } @@ -664,7 +670,8 @@ static void mobiveil_unmask_intx_irq(struct irq_data *data) mask = 1 << ((data->hwirq + PAB_INTX_START) - 1); raw_spin_lock_irqsave(&pcie->intx_mask_lock, flags); shifted_val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); - csr_writel(pcie, (shifted_val | mask), PAB_INTP_AMBA_MISC_ENB); + shifted_val |= mask; + csr_writel(pcie, shifted_val, PAB_INTP_AMBA_MISC_ENB); raw_spin_unlock_irqrestore(&pcie->intx_mask_lock, flags); } -- cgit v1.2.3-55-g7522 From f97441f4c398b4ae29a4eb8439f9a09fcf26225a Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:43 +0800 Subject: PCI: mobiveil: Refactor the MEM/IO outbound window initialization Move the resource type check into an if/else block and only set-up the outbound window for MEM and IO resource. No functional change intended. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian --- drivers/pci/controller/pcie-mobiveil.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 906299b0d10e..965f89ae73d7 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -565,7 +565,7 @@ static void mobiveil_pcie_enable_msi(struct mobiveil_pcie *pcie) static int mobiveil_host_init(struct mobiveil_pcie *pcie) { - u32 value, pab_ctrl, type = 0; + u32 value, pab_ctrl, type; struct resource_entry *win; /* setup bus numbers */ @@ -617,18 +617,18 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) /* Get the I/O and memory ranges from DT */ resource_list_for_each_entry(win, &pcie->resources) { - type = 0; if (resource_type(win->res) == IORESOURCE_MEM) type = MEM_WINDOW_TYPE; - if (resource_type(win->res) == IORESOURCE_IO) + else if (resource_type(win->res) == IORESOURCE_IO) type = IO_WINDOW_TYPE; - if (type) { - /* configure outbound translation window */ - program_ob_windows(pcie, pcie->ob_wins_configured, - win->res->start, - win->res->start - win->offset, - type, resource_size(win->res)); - } + else + continue; + + /* configure outbound translation window */ + program_ob_windows(pcie, pcie->ob_wins_configured, + win->res->start, + win->res->start - win->offset, + type, resource_size(win->res)); } /* fixup for PCIe class register */ -- cgit v1.2.3-55-g7522 From e0a7e56368a4a58488c80693f0b630a0a05f6c8d Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:44 +0800 Subject: PCI: mobiveil: Fix error return values Some error return values in the host controller driver are either unconventional or plain wrong. Update them all with the expected return values. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 965f89ae73d7..51cbe5331343 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -822,7 +822,7 @@ static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie) if (!pcie->intx_domain) { dev_err(dev, "Failed to get a INTx IRQ domain\n"); - return -ENODEV; + return -ENOMEM; } raw_spin_lock_init(&pcie->intx_mask_lock); @@ -848,7 +848,7 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) /* allocate the PCIe port */ bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); if (!bridge) - return -ENODEV; + return -ENOMEM; pcie = pci_host_bridge_priv(bridge); if (!pcie) @@ -869,7 +869,7 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) &pcie->resources, &iobase); if (ret) { dev_err(dev, "Getting bridge resources failed\n"); - return -ENOMEM; + return ret; } /* -- cgit v1.2.3-55-g7522 From afd899d3c8549cc82c8b329c0145e213c0755e60 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:45 +0800 Subject: PCI: mobiveil: Remove an unnecessary return value check The memory for the host controller private structure (struct mobiveil_pcie) is allocated together with the pci_host_bridge structure in function devm_pci_alloc_host_bridge(), so it is unnecessary to check the return value when get the private structure pointer. Remove the useless check. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 51cbe5331343..ddc20d3f49e3 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -851,8 +851,6 @@ static int mobiveil_pcie_probe(struct platform_device *pdev) return -ENOMEM; pcie = pci_host_bridge_priv(bridge); - if (!pcie) - return -ENOMEM; pcie->pdev = pdev; -- cgit v1.2.3-55-g7522 From ea0f1c959fb71dca26d30bbb9e3dccfb9da07a41 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:46 +0800 Subject: PCI: mobiveil: Clean-up program_{ib/ob}_windows() In function program_ob_windows(), remove the redundant read operations to registers PAB_AXI_AMAP_AXI_WIN and PAB_AXI_AMAP_PEX_WIN_H, and remove the useless definition of 'value'. Rename the parameter 'config_io_bit' to 'type' and then remove the definition of 'type'. In function program_ib_windows(), remove the definitions of 'pio_ctrl_val' and 'amap_ctrl_dw' and reduce to only one variable 'value' to keep the temporary value read from registers. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index ddc20d3f49e3..ccf9bb1008e1 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -455,8 +455,7 @@ static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie) static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, int pci_addr, u32 type, u64 size) { - int pio_ctrl_val; - int amap_ctrl_dw; + u32 value; u64 size64 = ~(size - 1); if ((pcie->ib_wins_configured + 1) > pcie->ppio_wins) { @@ -465,15 +464,15 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, return; } - pio_ctrl_val = csr_readl(pcie, PAB_PEX_PIO_CTRL); - pio_ctrl_val |= 1 << PIO_ENABLE_SHIFT; - csr_writel(pcie, pio_ctrl_val, PAB_PEX_PIO_CTRL); + value = csr_readl(pcie, PAB_PEX_PIO_CTRL); + value |= 1 << PIO_ENABLE_SHIFT; + csr_writel(pcie, value, PAB_PEX_PIO_CTRL); - amap_ctrl_dw = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); - amap_ctrl_dw |= (type << AMAP_CTRL_TYPE_SHIFT) | + value = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); + value |= (type << AMAP_CTRL_TYPE_SHIFT) | (1 << AMAP_CTRL_EN_SHIFT) | lower_32_bits(size64); - csr_writel(pcie, amap_ctrl_dw, PAB_PEX_AMAP_CTRL(win_num)); + csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num)); csr_writel(pcie, upper_32_bits(size64), PAB_EXT_PEX_AMAP_SIZEN(win_num)); @@ -488,11 +487,8 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, * routine to program the outbound windows */ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, - u64 cpu_addr, u64 pci_addr, - u32 config_io_bit, u64 size) + u64 cpu_addr, u64 pci_addr, u32 type, u64 size) { - - u32 value, type; u64 size64 = ~(size - 1); if ((pcie->ob_wins_configured + 1) > pcie->apio_wins) { @@ -505,8 +501,6 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit * to 4 KB in PAB_AXI_AMAP_CTRL register */ - type = config_io_bit; - value = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); csr_writel(pcie, 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | lower_32_bits(size64), PAB_AXI_AMAP_CTRL(win_num)); @@ -516,12 +510,9 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, * program AXI window base with appropriate value in * PAB_AXI_AMAP_AXI_WIN0 register */ - value = csr_readl(pcie, PAB_AXI_AMAP_AXI_WIN(win_num)); csr_writel(pcie, cpu_addr & (~AXI_WINDOW_ALIGN_MASK), PAB_AXI_AMAP_AXI_WIN(win_num)); - value = csr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_H(win_num)); - csr_writel(pcie, lower_32_bits(pci_addr), PAB_AXI_AMAP_PEX_WIN_L(win_num)); csr_writel(pcie, upper_32_bits(pci_addr), -- cgit v1.2.3-55-g7522 From ccd34dac2ed596b1f26079912bdf638e002a3979 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:47 +0800 Subject: PCI: mobiveil: Fix the valid check for inbound and outbound windows In program_ib/ob_windows() check the window index from the function parameter instead of the total number of initialized windows to determine if the specified window is valid. Fixes: 9af6bcb11e12 ("PCI: mobiveil: Add Mobiveil PCIe Host Bridge IP driver") Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index ccf9bb1008e1..76653d946486 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -458,7 +458,7 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, u32 value; u64 size64 = ~(size - 1); - if ((pcie->ib_wins_configured + 1) > pcie->ppio_wins) { + if (win_num >= pcie->ppio_wins) { dev_err(&pcie->pdev->dev, "ERROR: max inbound windows reached !\n"); return; @@ -491,7 +491,7 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, { u64 size64 = ~(size - 1); - if ((pcie->ob_wins_configured + 1) > pcie->apio_wins) { + if (win_num >= pcie->apio_wins) { dev_err(&pcie->pdev->dev, "ERROR: max outbound windows reached !\n"); return; -- cgit v1.2.3-55-g7522 From 4b7e2e59f0e50f13d6498190da41e758e01e6489 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:48 +0800 Subject: PCI: mobiveil: Add configured inbound windows counter Current code only keeps track of the number of outbound windows configured but it does not for inbound windows. Add a counter to keep track of initialized inbound windows. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 76653d946486..0560344dc588 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -481,6 +481,7 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, csr_writel(pcie, pci_addr, PAB_PEX_AMAP_PEX_WIN_L(win_num)); csr_writel(pcie, 0, PAB_PEX_AMAP_PEX_WIN_H(win_num)); + pcie->ib_wins_configured++; } /* -- cgit v1.2.3-55-g7522 From cf22c591f93dbc92f9a1ec69343a17c74aac9743 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:49 +0800 Subject: PCI: mobiveil: Clear the control fields before updating it While programming the inbound and outbound windows in program_{ib/ob}_windows() we shoud clear the control fields in the registers before programming it with a new value to prevent stale bits from older configuration. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 0560344dc588..7d18e5976fc1 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -65,6 +65,8 @@ #define PAB_AXI_AMAP_CTRL(win) PAB_REG_ADDR(0x0ba0, win) #define WIN_ENABLE_SHIFT 0 #define WIN_TYPE_SHIFT 1 +#define WIN_TYPE_MASK 0x3 +#define WIN_SIZE_MASK 0xfffffc00 #define PAB_EXT_AXI_AMAP_SIZE(win) PAB_EXT_REG_ADDR(0xbaf0, win) @@ -82,6 +84,7 @@ #define PAB_PEX_AMAP_CTRL(win) PAB_REG_ADDR(0x4ba0, win) #define AMAP_CTRL_EN_SHIFT 0 #define AMAP_CTRL_TYPE_SHIFT 1 +#define AMAP_CTRL_TYPE_MASK 3 #define PAB_EXT_PEX_AMAP_SIZEN(win) PAB_EXT_REG_ADDR(0xbef0, win) #define PAB_PEX_AMAP_AXI_WIN(win) PAB_REG_ADDR(0x4ba4, win) @@ -469,9 +472,9 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, csr_writel(pcie, value, PAB_PEX_PIO_CTRL); value = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); - value |= (type << AMAP_CTRL_TYPE_SHIFT) | - (1 << AMAP_CTRL_EN_SHIFT) | - lower_32_bits(size64); + value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | WIN_SIZE_MASK); + value |= type << AMAP_CTRL_TYPE_SHIFT | 1 << AMAP_CTRL_EN_SHIFT | + lower_32_bits(size64); csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num)); csr_writel(pcie, upper_32_bits(size64), @@ -490,6 +493,7 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr, u64 pci_addr, u32 type, u64 size) { + u32 value; u64 size64 = ~(size - 1); if (win_num >= pcie->apio_wins) { @@ -502,8 +506,11 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit * to 4 KB in PAB_AXI_AMAP_CTRL register */ - csr_writel(pcie, 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | - lower_32_bits(size64), PAB_AXI_AMAP_CTRL(win_num)); + value = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); + value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | WIN_SIZE_MASK); + value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | + lower_32_bits(size64); + csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num)); csr_writel(pcie, upper_32_bits(size64), PAB_EXT_AXI_AMAP_SIZE(win_num)); -- cgit v1.2.3-55-g7522 From 7717c7d7da31befa025c4402ac1179356b00012a Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:50 +0800 Subject: PCI: mobiveil: Mask out hardcoded bits in inbound/outbound windows setup The lower 10 bits of window size field are hardcoded to zero in HW so they can't really be changed but the lower 10-bit of PAB_AXI_AMAP_CTRL register are used for control fields, so while programming inbound and outbout windows decoding we should mask out the lower 10-bit of window size to prevent overriding the control bits. Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 7d18e5976fc1..a9559c68ece0 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -474,7 +474,7 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, value = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | WIN_SIZE_MASK); value |= type << AMAP_CTRL_TYPE_SHIFT | 1 << AMAP_CTRL_EN_SHIFT | - lower_32_bits(size64); + (lower_32_bits(size64) & WIN_SIZE_MASK); csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num)); csr_writel(pcie, upper_32_bits(size64), @@ -509,7 +509,7 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, value = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | WIN_SIZE_MASK); value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | - lower_32_bits(size64); + (lower_32_bits(size64) & WIN_SIZE_MASK); csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num)); csr_writel(pcie, upper_32_bits(size64), PAB_EXT_AXI_AMAP_SIZE(win_num)); -- cgit v1.2.3-55-g7522 From 6f7374b871d5e55e772b532fe1c571da0fcc7164 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:51 +0800 Subject: PCI: mobiveil: Add upper 32-bit CPU base address setup in outbound window Current code erroneously sets-up only the lower 32-bit CPU base address in the outbound window, which results in outbound transactions not working in 64-bit platforms. Fix it. Fixes: 9af6bcb11e12 ("PCI: mobiveil: Add Mobiveil PCIe Host Bridge IP driver") Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index a9559c68ece0..a09fc6cafb46 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -70,6 +70,7 @@ #define PAB_EXT_AXI_AMAP_SIZE(win) PAB_EXT_REG_ADDR(0xbaf0, win) +#define PAB_EXT_AXI_AMAP_AXI_WIN(win) PAB_EXT_REG_ADDR(0x80a0, win) #define PAB_AXI_AMAP_AXI_WIN(win) PAB_REG_ADDR(0x0ba4, win) #define AXI_WINDOW_ALIGN_MASK 3 @@ -518,8 +519,10 @@ static void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, * program AXI window base with appropriate value in * PAB_AXI_AMAP_AXI_WIN0 register */ - csr_writel(pcie, cpu_addr & (~AXI_WINDOW_ALIGN_MASK), + csr_writel(pcie, lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK), PAB_AXI_AMAP_AXI_WIN(win_num)); + csr_writel(pcie, upper_32_bits(cpu_addr), + PAB_EXT_AXI_AMAP_AXI_WIN(win_num)); csr_writel(pcie, lower_32_bits(pci_addr), PAB_AXI_AMAP_PEX_WIN_L(win_num)); -- cgit v1.2.3-55-g7522 From 4e00aca3ba0b54d496c224888b468207c601463c Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:52 +0800 Subject: PCI: mobiveil: Add upper 32-bit PCI base address setup in inbound window Current code erroneously sets-up the lower 32-bit PCI base address in the inbound window, which results in inbound transactions not working in 64-bit platforms. Fixes: 9af6bcb11e12 ("PCI: mobiveil: Add Mobiveil PCIe Host Bridge IP driver") Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index a09fc6cafb46..8ba15c6cb51e 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -457,7 +457,7 @@ static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie) } static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, - int pci_addr, u32 type, u64 size) + u64 pci_addr, u32 type, u64 size) { u32 value; u64 size64 = ~(size - 1); @@ -483,8 +483,11 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, csr_writel(pcie, pci_addr, PAB_PEX_AMAP_AXI_WIN(win_num)); - csr_writel(pcie, pci_addr, PAB_PEX_AMAP_PEX_WIN_L(win_num)); - csr_writel(pcie, 0, PAB_PEX_AMAP_PEX_WIN_H(win_num)); + csr_writel(pcie, lower_32_bits(pci_addr), + PAB_PEX_AMAP_PEX_WIN_L(win_num)); + csr_writel(pcie, upper_32_bits(pci_addr), + PAB_PEX_AMAP_PEX_WIN_H(win_num)); + pcie->ib_wins_configured++; } -- cgit v1.2.3-55-g7522 From fe83fa7e7eac505c40b269eeb892240d83c6b279 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:54 +0800 Subject: PCI: mobiveil: Move PCIe PIO enablement out of inbound window routine Move the PCIe PIO master enablement to function mobiveil_host_init(). Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa --- drivers/pci/controller/pcie-mobiveil.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index 8ba15c6cb51e..f57d2d83dbd8 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -468,10 +468,6 @@ static void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, return; } - value = csr_readl(pcie, PAB_PEX_PIO_CTRL); - value |= 1 << PIO_ENABLE_SHIFT; - csr_writel(pcie, value, PAB_PEX_PIO_CTRL); - value = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | WIN_SIZE_MASK); value |= type << AMAP_CTRL_TYPE_SHIFT | 1 << AMAP_CTRL_EN_SHIFT | @@ -606,6 +602,11 @@ static int mobiveil_host_init(struct mobiveil_pcie *pcie) value |= APIO_EN_MASK; csr_writel(pcie, value, PAB_AXI_PIO_CTRL); + /* Enable PCIe PIO master */ + value = csr_readl(pcie, PAB_PEX_PIO_CTRL); + value |= 1 << PIO_ENABLE_SHIFT; + csr_writel(pcie, value, PAB_PEX_PIO_CTRL); + /* * we'll program one outbound window for config reads and * another default inbound window for all the upstream traffic -- cgit v1.2.3-55-g7522 From 526c101dde6453e45ff5b25fbda8c3ff736f9788 Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:55 +0800 Subject: PCI: mobiveil: Fix infinite-loop in the INTx handling function In the loop handling INTx interrupts in mobiveil_pcie_isr(), there is no code to update the loop control variable, which is causing an infinite loop. Fix the code by reading the interrupt status registers inside the loop. Fixes: 9af6bcb11e12 ("PCI: mobiveil: Add Mobiveil PCIe Host Bridge IP driver") Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa Acked-by: Karthikeyan Mitran Tested-by: Karthikeyan Mitran --- drivers/pci/controller/pcie-mobiveil.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index f57d2d83dbd8..b3ff6555e9aa 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -358,8 +358,9 @@ static void mobiveil_pcie_isr(struct irq_desc *desc) /* Handle INTx */ if (intr_status & PAB_INTP_INTX_MASK) { - shifted_status = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT) >> - PAB_INTX_START; + shifted_status = csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT); + shifted_status &= PAB_INTP_INTX_MASK; + shifted_status >>= PAB_INTX_START; do { for_each_set_bit(bit, &shifted_status, PCI_NUM_INTX) { virq = irq_find_mapping(pcie->intx_domain, @@ -375,7 +376,12 @@ static void mobiveil_pcie_isr(struct irq_desc *desc) shifted_status << PAB_INTX_START, PAB_INTP_AMBA_MISC_STAT); } - } while ((shifted_status >> PAB_INTX_START) != 0); + + shifted_status = csr_readl(pcie, + PAB_INTP_AMBA_MISC_STAT); + shifted_status &= PAB_INTP_INTX_MASK; + shifted_status >>= PAB_INTX_START; + } while (shifted_status != 0); } /* read extra MSI status register */ -- cgit v1.2.3-55-g7522 From 8a4f9fea2697ae11a0f542f158636f00d10b800f Mon Sep 17 00:00:00 2001 From: Hou Zhiqiang Date: Fri, 5 Jul 2019 17:56:56 +0800 Subject: PCI: mobiveil: Fix INTx interrupt clearing in mobiveil_pcie_isr() The current INTx handling function clears all interrupts after handling the first pending; this can potentially cause missing INTx detection. Fix the code to clear only the handled INTx IRQ. Fixes: 9af6bcb11e12 ("PCI: mobiveil: Add Mobiveil PCIe Host Bridge IP driver") Signed-off-by: Hou Zhiqiang Signed-off-by: Lorenzo Pieralisi Reviewed-by: Minghuan Lian Reviewed-by: Subrahmanya Lingappa Acked-by: Karthikeyan Mitran Tested-by: Karthikeyan Mitran --- drivers/pci/controller/pcie-mobiveil.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/pcie-mobiveil.c b/drivers/pci/controller/pcie-mobiveil.c index b3ff6555e9aa..672e633601c7 100644 --- a/drivers/pci/controller/pcie-mobiveil.c +++ b/drivers/pci/controller/pcie-mobiveil.c @@ -371,9 +371,8 @@ static void mobiveil_pcie_isr(struct irq_desc *desc) dev_err_ratelimited(dev, "unexpected IRQ, INT%d\n", bit); - /* clear interrupt */ - csr_writel(pcie, - shifted_status << PAB_INTX_START, + /* clear interrupt handled */ + csr_writel(pcie, 1 << (PAB_INTX_START + bit), PAB_INTP_AMBA_MISC_STAT); } -- cgit v1.2.3-55-g7522 From f6b6aefee70aa5261deec7feab80c249bf58397f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 30 May 2019 08:05:58 -0500 Subject: PCI: Fix typos and whitespace errors Fix typos in drivers/pci. Comment and whitespace changes only. Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki Reviewed-by: Randy Dunlap --- drivers/pci/ats.c | 2 +- drivers/pci/controller/dwc/pcie-armada8k.c | 2 +- drivers/pci/controller/dwc/pcie-kirin.c | 2 +- drivers/pci/controller/pci-aardvark.c | 2 +- drivers/pci/controller/pcie-iproc-platform.c | 2 +- drivers/pci/controller/pcie-iproc.c | 2 +- drivers/pci/controller/vmd.c | 2 +- drivers/pci/mmap.c | 2 +- drivers/pci/msi.c | 43 ++++++++++++++-------------- drivers/pci/p2pdma.c | 6 ++-- drivers/pci/pci-bridge-emul.c | 2 +- drivers/pci/pci-pf-stub.c | 2 +- drivers/pci/pci.c | 2 +- drivers/pci/pcie/aer_inject.c | 2 +- include/linux/pci.h | 2 +- include/linux/pci_ids.h | 6 ++-- 16 files changed, 41 insertions(+), 40 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 97c08146534a..e18499243f84 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -432,7 +432,7 @@ EXPORT_SYMBOL_GPL(pci_prg_resp_pasid_required); * @pdev: PCI device structure * * Returns negative value when PASID capability is not present. - * Otherwise it returns the numer of supported PASIDs. + * Otherwise it returns the number of supported PASIDs. */ int pci_max_pasids(struct pci_dev *pdev) { diff --git a/drivers/pci/controller/dwc/pcie-armada8k.c b/drivers/pci/controller/dwc/pcie-armada8k.c index 0c389a30ef5d..9012d5f60be9 100644 --- a/drivers/pci/controller/dwc/pcie-armada8k.c +++ b/drivers/pci/controller/dwc/pcie-armada8k.c @@ -55,7 +55,7 @@ struct armada8k_pcie { #define PCIE_ARUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x5C) #define PCIE_AWUSER_REG (PCIE_VENDOR_REGS_OFFSET + 0x60) /* - * AR/AW Cache defauls: Normal memory, Write-Back, Read / Write + * AR/AW Cache defaults: Normal memory, Write-Back, Read / Write * allocate */ #define ARCACHE_DEFAULT_VALUE 0x3511 diff --git a/drivers/pci/controller/dwc/pcie-kirin.c b/drivers/pci/controller/dwc/pcie-kirin.c index 9b599296205d..8df1914226be 100644 --- a/drivers/pci/controller/dwc/pcie-kirin.c +++ b/drivers/pci/controller/dwc/pcie-kirin.c @@ -2,7 +2,7 @@ /* * PCIe host controller driver for Kirin Phone SoCs * - * Copyright (C) 2017 Hilisicon Electronics Co., Ltd. + * Copyright (C) 2017 HiSilicon Electronics Co., Ltd. * http://www.huawei.com * * Author: Xiaowei Song diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c index 134e0306ff00..fc0fe4d4de49 100644 --- a/drivers/pci/controller/pci-aardvark.c +++ b/drivers/pci/controller/pci-aardvark.c @@ -308,7 +308,7 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie) advk_writel(pcie, PCIE_ISR1_ALL_MASK, PCIE_ISR1_MASK_REG); - /* Unmask all MSI's */ + /* Unmask all MSIs */ advk_writel(pcie, 0, PCIE_MSI_MASK_REG); /* Enable summary interrupt for GIC SPI source */ diff --git a/drivers/pci/controller/pcie-iproc-platform.c b/drivers/pci/controller/pcie-iproc-platform.c index f30f5f3fb5c1..5a3550b6bb29 100644 --- a/drivers/pci/controller/pcie-iproc-platform.c +++ b/drivers/pci/controller/pcie-iproc-platform.c @@ -87,7 +87,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) /* * DT nodes are not used by all platforms that use the iProc PCIe - * core driver. For platforms that require explict inbound mapping + * core driver. For platforms that require explicit inbound mapping * configuration, "dma-ranges" would have been present in DT */ pcie->need_ib_cfg = of_property_read_bool(np, "dma-ranges"); diff --git a/drivers/pci/controller/pcie-iproc.c b/drivers/pci/controller/pcie-iproc.c index e3ca46497470..2d457bfdaf66 100644 --- a/drivers/pci/controller/pcie-iproc.c +++ b/drivers/pci/controller/pcie-iproc.c @@ -163,7 +163,7 @@ enum iproc_pcie_ib_map_type { * @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or * SZ_1G * @region_sizes: list of supported inbound mapping region sizes in KB, MB, or - * GB, depedning on the size unit + * GB, depending on the size unit * @nr_sizes: number of supported inbound mapping region sizes * @nr_windows: number of supported inbound mapping windows for the region * @imap_addr_offset: register offset between the upper and lower 32-bit diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index 999a5509e57e..4575e0c6dc4b 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -627,7 +627,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features) * 32-bit resources. __pci_assign_resource() enforces that * artificial restriction to make sure everything will fit. * - * The only way we could use a 64-bit non-prefechable MEMBAR is + * The only way we could use a 64-bit non-prefetchable MEMBAR is * if its address is <4GB so that we can convert it to a 32-bit * resource. To be visible to the host OS, all VMD endpoints must * be initially configured by platform BIOS, which includes setting diff --git a/drivers/pci/mmap.c b/drivers/pci/mmap.c index 24505b08de40..b8c9011987f4 100644 --- a/drivers/pci/mmap.c +++ b/drivers/pci/mmap.c @@ -73,7 +73,7 @@ int pci_mmap_resource_range(struct pci_dev *pdev, int bar, #elif defined(HAVE_PCI_MMAP) /* && !ARCH_GENERIC_PCI_MMAP_RESOURCE */ /* - * Legacy setup: Impement pci_mmap_resource_range() as a wrapper around + * Legacy setup: Implement pci_mmap_resource_range() as a wrapper around * the architecture's pci_mmap_page_range(), converting to "user visible" * addresses as necessary. */ diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index e039b740fe74..59a6d232f77a 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -237,7 +237,7 @@ static void msi_set_mask_bit(struct irq_data *data, u32 flag) } /** - * pci_msi_mask_irq - Generic irq chip callback to mask PCI/MSI interrupts + * pci_msi_mask_irq - Generic IRQ chip callback to mask PCI/MSI interrupts * @data: pointer to irqdata associated to that interrupt */ void pci_msi_mask_irq(struct irq_data *data) @@ -247,7 +247,7 @@ void pci_msi_mask_irq(struct irq_data *data) EXPORT_SYMBOL_GPL(pci_msi_mask_irq); /** - * pci_msi_unmask_irq - Generic irq chip callback to unmask PCI/MSI interrupts + * pci_msi_unmask_irq - Generic IRQ chip callback to unmask PCI/MSI interrupts * @data: pointer to irqdata associated to that interrupt */ void pci_msi_unmask_irq(struct irq_data *data) @@ -588,11 +588,11 @@ static int msi_verify_entries(struct pci_dev *dev) * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function * @nvec: number of interrupts to allocate - * @affd: description of automatic irq affinity assignments (may be %NULL) + * @affd: description of automatic IRQ affinity assignments (may be %NULL) * * Setup the MSI capability structure of the device with the requested * number of interrupts. A return value of zero indicates the successful - * setup of an entry with the new MSI irq. A negative return value indicates + * setup of an entry with the new MSI IRQ. A negative return value indicates * an error, and a positive return value indicates the number of interrupts * which could have been allocated. */ @@ -609,7 +609,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec, if (!entry) return -ENOMEM; - /* All MSIs are unmasked by default, Mask them all */ + /* All MSIs are unmasked by default; mask them all */ mask = msi_mask(entry->msi_attrib.multi_cap); msi_mask_irq(entry, mask, mask); @@ -637,7 +637,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec, return ret; } - /* Set MSI enabled bits */ + /* Set MSI enabled bits */ pci_intx_for_msi(dev, 0); pci_msi_set_enable(dev, 1); dev->msi_enabled = 1; @@ -729,11 +729,11 @@ static void msix_program_entries(struct pci_dev *dev, * @dev: pointer to the pci_dev data structure of MSI-X device function * @entries: pointer to an array of struct msix_entry entries * @nvec: number of @entries - * @affd: Optional pointer to enable automatic affinity assignement + * @affd: Optional pointer to enable automatic affinity assignment * * Setup the MSI-X capability structure of device function with a - * single MSI-X irq. A return of zero indicates the successful setup of - * requested MSI-X entries with allocated irqs or non-zero for otherwise. + * single MSI-X IRQ. A return of zero indicates the successful setup of + * requested MSI-X entries with allocated IRQs or non-zero for otherwise. **/ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, int nvec, struct irq_affinity *affd) @@ -789,7 +789,7 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, out_avail: if (ret < 0) { /* - * If we had some success, report the number of irqs + * If we had some success, report the number of IRQs * we succeeded in setting up. */ struct msi_desc *entry; @@ -812,7 +812,7 @@ out_free: /** * pci_msi_supported - check whether MSI may be enabled on a device * @dev: pointer to the pci_dev data structure of MSI device function - * @nvec: how many MSIs have been requested ? + * @nvec: how many MSIs have been requested? * * Look at global flags, the device itself, and its parent buses * to determine if MSI/-X are supported for the device. If MSI/-X is @@ -896,7 +896,7 @@ static void pci_msi_shutdown(struct pci_dev *dev) /* Keep cached state to be restored */ __pci_msi_desc_mask_irq(desc, mask, ~mask); - /* Restore dev->irq to its default pin-assertion irq */ + /* Restore dev->irq to its default pin-assertion IRQ */ dev->irq = desc->msi_attrib.default_irq; pcibios_alloc_irq(dev); } @@ -958,7 +958,7 @@ static int __pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, } } - /* Check whether driver already requested for MSI irq */ + /* Check whether driver already requested for MSI IRQ */ if (dev->msi_enabled) { pci_info(dev, "can't enable MSI-X (MSI IRQ already assigned)\n"); return -EINVAL; @@ -1026,7 +1026,7 @@ static int __pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec, if (!pci_msi_supported(dev, minvec)) return -EINVAL; - /* Check whether driver already requested MSI-X irqs */ + /* Check whether driver already requested MSI-X IRQs */ if (dev->msix_enabled) { pci_info(dev, "can't enable MSI (MSI-X already enabled)\n"); return -EINVAL; @@ -1113,8 +1113,8 @@ static int __pci_enable_msix_range(struct pci_dev *dev, * pci_enable_msix_range - configure device's MSI-X capability structure * @dev: pointer to the pci_dev data structure of MSI-X device function * @entries: pointer to an array of MSI-X entries - * @minvec: minimum number of MSI-X irqs requested - * @maxvec: maximum number of MSI-X irqs requested + * @minvec: minimum number of MSI-X IRQs requested + * @maxvec: maximum number of MSI-X IRQs requested * * Setup the MSI-X capability structure of device function with a maximum * possible number of interrupts in the range between @minvec and @maxvec @@ -1179,7 +1179,7 @@ int pci_alloc_irq_vectors_affinity(struct pci_dev *dev, unsigned int min_vecs, return msi_vecs; } - /* use legacy irq if allowed */ + /* use legacy IRQ if allowed */ if (flags & PCI_IRQ_LEGACY) { if (min_vecs == 1 && dev->irq) { /* @@ -1248,7 +1248,7 @@ int pci_irq_vector(struct pci_dev *dev, unsigned int nr) EXPORT_SYMBOL(pci_irq_vector); /** - * pci_irq_get_affinity - return the affinity of a particular msi vector + * pci_irq_get_affinity - return the affinity of a particular MSI vector * @dev: PCI device to operate on * @nr: device-relative interrupt vector index (0-based). */ @@ -1280,7 +1280,7 @@ const struct cpumask *pci_irq_get_affinity(struct pci_dev *dev, int nr) EXPORT_SYMBOL(pci_irq_get_affinity); /** - * pci_irq_get_node - return the numa node of a particular msi vector + * pci_irq_get_node - return the NUMA node of a particular MSI vector * @pdev: PCI device to operate on * @vec: device-relative interrupt vector index (0-based). */ @@ -1330,7 +1330,7 @@ void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg) /** * pci_msi_domain_calc_hwirq - Generate a unique ID for an MSI source * @dev: Pointer to the PCI device - * @desc: Pointer to the msi descriptor + * @desc: Pointer to the MSI descriptor * * The ID number is only used within the irqdomain. */ @@ -1348,7 +1348,8 @@ static inline bool pci_msi_desc_is_multi_msi(struct msi_desc *desc) } /** - * pci_msi_domain_check_cap - Verify that @domain supports the capabilities for @dev + * pci_msi_domain_check_cap - Verify that @domain supports the capabilities + * for @dev * @domain: The interrupt domain to check * @info: The domain info for verification * @dev: The device to check diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 742928d0053e..d953cc7d9a54 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -223,7 +223,7 @@ EXPORT_SYMBOL_GPL(pci_p2pdma_add_resource); /* * Note this function returns the parent PCI device with a - * reference taken. It is the caller's responsibily to drop + * reference taken. It is the caller's responsibility to drop * the reference. */ static struct pci_dev *find_parent_pci_dev(struct device *dev) @@ -380,7 +380,7 @@ static int upstream_bridge_distance(struct pci_dev *provider, /* * Allow the connection if both devices are on a whitelisted root - * complex, but add an arbitary large value to the distance. + * complex, but add an arbitrary large value to the distance. */ if (root_complex_whitelist(provider) && root_complex_whitelist(client)) @@ -439,7 +439,7 @@ static int upstream_bridge_distance_warn(struct pci_dev *provider, } /** - * pci_p2pdma_distance_many - Determive the cumulative distance between + * pci_p2pdma_distance_many - Determine the cumulative distance between * a p2pdma provider and the clients in use. * @provider: p2pdma provider to check against the client list * @clients: array of devices to check (NULL-terminated) diff --git a/drivers/pci/pci-bridge-emul.c b/drivers/pci/pci-bridge-emul.c index 83fb077d0b41..06083b86d4f4 100644 --- a/drivers/pci/pci-bridge-emul.c +++ b/drivers/pci/pci-bridge-emul.c @@ -305,7 +305,7 @@ int pci_bridge_emul_init(struct pci_bridge_emul *bridge, } /* - * Cleanup a pci_bridge_emul structure that was previously initilized + * Cleanup a pci_bridge_emul structure that was previously initialized * using pci_bridge_emul_init(). */ void pci_bridge_emul_cleanup(struct pci_bridge_emul *bridge) diff --git a/drivers/pci/pci-pf-stub.c b/drivers/pci/pci-pf-stub.c index 9795649fc6f9..ef293e735c55 100644 --- a/drivers/pci/pci-pf-stub.c +++ b/drivers/pci/pci-pf-stub.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* pci-pf-stub - simple stub driver for PCI SR-IOV PF device * - * This driver is meant to act as a "whitelist" for devices that provde + * This driver is meant to act as a "whitelist" for devices that provide * SR-IOV functionality while at the same time not actually needing a * driver of their own. */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8abc843b1615..3fd4eaa32b21 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4501,7 +4501,7 @@ static int pci_af_flr(struct pci_dev *dev, int probe) /* * Wait for Transaction Pending bit to clear. A word-aligned test - * is used, so we use the conrol offset rather than status and shift + * is used, so we use the control offset rather than status and shift * the test bit to match. */ if (!pci_wait_for_pending(dev, pos + PCI_AF_CTRL, diff --git a/drivers/pci/pcie/aer_inject.c b/drivers/pci/pcie/aer_inject.c index 043b8b0cfcc5..6988fe7389b9 100644 --- a/drivers/pci/pcie/aer_inject.c +++ b/drivers/pci/pcie/aer_inject.c @@ -2,7 +2,7 @@ /* * PCIe AER software error injection support. * - * Debuging PCIe AER code is quite difficult because it is hard to + * Debugging PCIe AER code is quite difficult because it is hard to * trigger various real hardware errors. Software based error * injection can fake almost all kinds of errors with the help of a * user space helper tool aer-inject, which can be gotten from: diff --git a/include/linux/pci.h b/include/linux/pci.h index 4a5a84d7bdd4..fb207a22d686 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -382,7 +382,7 @@ struct pci_dev { unsigned int is_busmaster:1; /* Is busmaster */ unsigned int no_msi:1; /* May not use MSI */ - unsigned int no_64bit_msi:1; /* May only use 32-bit MSIs */ + unsigned int no_64bit_msi:1; /* May only use 32-bit MSIs */ unsigned int block_cfg_access:1; /* Config space access blocked */ unsigned int broken_parity_status:1; /* Generates false positive parity */ unsigned int irq_reroute_variant:2; /* Needs IRQ rerouting variant */ diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 70e86148cb1e..0dd239f11e91 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1112,7 +1112,7 @@ #define PCI_VENDOR_ID_AL 0x10b9 #define PCI_DEVICE_ID_AL_M1533 0x1533 -#define PCI_DEVICE_ID_AL_M1535 0x1535 +#define PCI_DEVICE_ID_AL_M1535 0x1535 #define PCI_DEVICE_ID_AL_M1541 0x1541 #define PCI_DEVICE_ID_AL_M1563 0x1563 #define PCI_DEVICE_ID_AL_M1621 0x1621 @@ -1752,7 +1752,7 @@ #define PCI_VENDOR_ID_STALLION 0x124d /* Allied Telesyn */ -#define PCI_VENDOR_ID_AT 0x1259 +#define PCI_VENDOR_ID_AT 0x1259 #define PCI_SUBDEVICE_ID_AT_2700FX 0x2701 #define PCI_SUBDEVICE_ID_AT_2701FX 0x2703 @@ -2550,7 +2550,7 @@ #define PCI_DEVICE_ID_KORENIX_JETCARDF2 0x1700 #define PCI_DEVICE_ID_KORENIX_JETCARDF3 0x17ff -#define PCI_VENDOR_ID_HUAWEI 0x19e5 +#define PCI_VENDOR_ID_HUAWEI 0x19e5 #define PCI_VENDOR_ID_NETRONOME 0x19ee #define PCI_DEVICE_ID_NETRONOME_NFP4000 0x4000 -- cgit v1.2.3-55-g7522 From 381ed79c8655a40268ee7391f716edd90c5c3a97 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 14 Jun 2019 23:40:44 +0800 Subject: PCI: dwc: pci-dra7xx: Fix compilation when !CONFIG_GPIOLIB If CONFIG_GPIOLIB is not selected the compilation results in the following build errors: drivers/pci/controller/dwc/pci-dra7xx.c: In function dra7xx_pcie_probe: drivers/pci/controller/dwc/pci-dra7xx.c:777:10: error: implicit declaration of function devm_gpiod_get_optional; did you mean devm_regulator_get_optional? [-Werror=implicit-function-declaration] reset = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); drivers/pci/controller/dwc/pci-dra7xx.c:778:45: error: ‘GPIOD_OUT_HIGH’ undeclared (first use in this function); did you mean ‘GPIOF_INIT_HIGH’? reset = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH); ^~~~~~~~~~~~~~ GPIOF_INIT_HIGH Fix them by including the appropriate header file. Reported-by: Hulk Robot Signed-off-by: YueHaibing [lorenzo.pieralisi@arm.com: commit log] Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/controller/dwc/pci-dra7xx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci') diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index 419451efd58c..4234ddb4722f 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "../../pci.h" #include "pcie-designware.h" -- cgit v1.2.3-55-g7522 From b516ea586d717472178e6ef1c152e85608b0ce32 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 8 Jul 2019 13:17:44 +0800 Subject: PCI: Enable NVIDIA HDA controllers Many NVIDIA GPUs can be configured as either a single-function video device or a multi-function device with video at function 0 and an HDA audio controller at function 1. The HDA controller can be enabled or disabled by a bit in the function 0 config space. Some BIOSes leave the HDA disabled, which means the HDMI connector from the NVIDIA GPU may not work. Sometimes the BIOS enables the HDA if an HDMI cable is connected at boot time, but that doesn't handle hotplug cases. Enable the HDA controller on device enumeration and resume and re-read the header type, which tells us whether the GPU is a multi-function device. This quirk is limited to NVIDIA PCI devices with the VGA Controller device class. This is expected to correspond to product configurations where the NVIDIA GPU has connectors attached. Other products where the device class is 3D Controller are expected to correspond to configurations where the NVIDIA GPU is dedicated (dGPU) and has no connectors. See original post (URL below) for more details. This commit takes inspiration from an earlier patch by Daniel Drake. Link: https://lore.kernel.org/r/20190708051744.24039-1-drake@endlessm.com v2 Link: https://lore.kernel.org/r/20190613063514.15317-1-drake@endlessm.com v1 Link: https://devtalk.nvidia.com/default/topic/1024022 Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=75985 Signed-off-by: Lukas Wunner Signed-off-by: Daniel Drake [bhelgaas: commit log, log message, return early if already enabled] Signed-off-by: Bjorn Helgaas Cc: Aaron Plattner Cc: Peter Wu Cc: Ilia Mirkin Cc: Karol Herbst Cc: Maik Freudenberg --- drivers/pci/quirks.c | 30 ++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 1 + 2 files changed, 31 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index c66c0ca446c4..208aacf39329 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5011,6 +5011,36 @@ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_CLASS_SERIAL_UNKNOWN, 8, quirk_gpu_usb_typec_ucsi); +/* + * Enable the NVIDIA GPU integrated HDA controller if the BIOS left it + * disabled. https://devtalk.nvidia.com/default/topic/1024022 + */ +static void quirk_nvidia_hda(struct pci_dev *gpu) +{ + u8 hdr_type; + u32 val; + + /* There was no integrated HDA controller before MCP89 */ + if (gpu->device < PCI_DEVICE_ID_NVIDIA_GEFORCE_320M) + return; + + /* Bit 25 at offset 0x488 enables the HDA controller */ + pci_read_config_dword(gpu, 0x488, &val); + if (val & BIT(25)) + return; + + pci_info(gpu, "Enabling HDA controller\n"); + pci_write_config_dword(gpu, 0x488, val | BIT(25)); + + /* The GPU becomes a multi-function device when the HDA is enabled */ + pci_read_config_byte(gpu, PCI_HEADER_TYPE, &hdr_type); + gpu->multifunction = !!(hdr_type & 0x80); +} +DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_BASE_CLASS_DISPLAY, 16, quirk_nvidia_hda); +DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_BASE_CLASS_DISPLAY, 16, quirk_nvidia_hda); + /* * Some IDT switches incorrectly flag an ACS Source Validation error on * completions for config read requests even though PCIe r4.0, sec diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 70e86148cb1e..66898463b81f 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1336,6 +1336,7 @@ #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP78S_SMBUS 0x0752 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE 0x0759 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_SMBUS 0x07D8 +#define PCI_DEVICE_ID_NVIDIA_GEFORCE_320M 0x08A0 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP79_SMBUS 0x0AA2 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP89_SATA 0x0D85 -- cgit v1.2.3-55-g7522