diff options
Diffstat (limited to 'target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch')
-rw-r--r-- | target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch | 753 |
1 files changed, 0 insertions, 753 deletions
diff --git a/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch b/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch deleted file mode 100644 index a0c9c7c36e..0000000000 --- a/target/linux/ipq806x/patches-3.18/111-PCI-qcom-Add-Qualcomm-PCIe-controller-driver.patch +++ /dev/null @@ -1,753 +0,0 @@ -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Subject: [v2,4/5] PCI: qcom: Add Qualcomm PCIe controller driver -From: Stanimir Varbanov <svarbanov@mm-sol.com> -X-Patchwork-Id: 6326161 -Message-Id: <1430743338-10441-5-git-send-email-svarbanov@mm-sol.com> -To: Rob Herring <robh+dt@kernel.org>, Kumar Gala <galak@codeaurora.org>, - Mark Rutland <mark.rutland@arm.com>, - Grant Likely <grant.likely@linaro.org>, - Bjorn Helgaas <bhelgaas@google.com>, - Kishon Vijay Abraham I <kishon@ti.com>, - Russell King <linux@arm.linux.org.uk>, Arnd Bergmann <arnd@arndb.de> -Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org, - linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org, - linux-pci@vger.kernel.org, Mathieu Olivari <mathieu@codeaurora.org>, - Srinivas Kandagatla <srinivas.kandagatla@linaro.org>, - Stanimir Varbanov <svarbanov@mm-sol.com> -Date: Mon, 4 May 2015 15:42:17 +0300 - -The PCIe driver reuse the Designware common code for host -and MSI initialization, and also program the Qualcomm -application specific registers. - -Signed-off-by: Stanimir Varbanov <svarbanov@mm-sol.com> - ---- -MAINTAINERS | 7 + - drivers/pci/host/Kconfig | 9 + - drivers/pci/host/Makefile | 1 + - drivers/pci/host/pcie-qcom.c | 677 ++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 694 insertions(+), 0 deletions(-) - create mode 100644 drivers/pci/host/pcie-qcom.c - ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -7127,6 +7127,13 @@ L: linux-pci@vger.kernel.org - S: Maintained - F: drivers/pci/host/*spear* - -+PCIE DRIVER FOR QUALCOMM MSM -+M: Stanimir Varbanov <svarbanov@mm-sol.com> -+L: linux-pci@vger.kernel.org -+L: linux-arm-msm@vger.kernel.org -+S: Maintained -+F: drivers/pci/host/*qcom* -+ - PCMCIA SUBSYSTEM - P: Linux PCMCIA Team - L: linux-pcmcia@lists.infradead.org ---- a/drivers/pci/host/Kconfig -+++ b/drivers/pci/host/Kconfig -@@ -91,4 +91,13 @@ config PCI_XGENE - There are 5 internal PCIe ports available. Each port is GEN3 capable - and have varied lanes from x1 to x8. - -+config PCIE_QCOM -+ bool "Qualcomm PCIe controller" -+ depends on ARCH_QCOM && OF || (ARM && COMPILE_TEST) -+ select PCIE_DW -+ select PCIEPORTBUS -+ help -+ Say Y here to enable PCIe controller support on Qualcomm SoCs. The -+ PCIe controller use Designware core plus Qualcomm specific hardware -+ wrappers. - endmenu ---- /dev/null -+++ b/drivers/pci/host/pcie-qcom.c -@@ -0,0 +1,677 @@ -+/* -+ * Copyright (c) 2014, The Linux Foundation. All rights reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 and -+ * only version 2 as published by the Free Software Foundation. -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ */ -+ -+#include <linux/clk.h> -+#include <linux/delay.h> -+#include <linux/gpio.h> -+#include <linux/interrupt.h> -+#include <linux/io.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/of_gpio.h> -+#include <linux/pci.h> -+#include <linux/platform_device.h> -+#include <linux/phy/phy.h> -+#include <linux/regulator/consumer.h> -+#include <linux/reset.h> -+#include <linux/slab.h> -+#include <linux/types.h> -+ -+#include "pcie-designware.h" -+ -+#define PCIE20_PARF_PHY_CTRL 0x40 -+#define PCIE20_PARF_PHY_REFCLK 0x4C -+#define PCIE20_PARF_DBI_BASE_ADDR 0x168 -+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16c -+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x178 -+ -+#define PCIE20_ELBI_SYS_CTRL 0x04 -+#define PCIE20_ELBI_SYS_STTS 0x08 -+#define XMLH_LINK_UP BIT(10) -+ -+#define PCIE20_CAP 0x70 -+#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) -+ -+#define PERST_DELAY_MIN_US 1000 -+#define PERST_DELAY_MAX_US 1005 -+ -+#define LINKUP_DELAY_MIN_US 5000 -+#define LINKUP_DELAY_MAX_US 5100 -+#define LINKUP_RETRIES_COUNT 20 -+ -+#define PCIE_V0 0 /* apq8064 */ -+#define PCIE_V1 1 /* apq8084 */ -+ -+struct qcom_pcie_resources_v0 { -+ struct clk *iface_clk; -+ struct clk *core_clk; -+ struct clk *phy_clk; -+ struct reset_control *pci_reset; -+ struct reset_control *axi_reset; -+ struct reset_control *ahb_reset; -+ struct reset_control *por_reset; -+ struct reset_control *phy_reset; -+ struct regulator *vdda; -+ struct regulator *vdda_phy; -+ struct regulator *vdda_refclk; -+}; -+ -+struct qcom_pcie_resources_v1 { -+ struct clk *iface; -+ struct clk *aux; -+ struct clk *master_bus; -+ struct clk *slave_bus; -+ struct reset_control *core; -+ struct regulator *vdda; -+}; -+ -+union pcie_resources { -+ struct qcom_pcie_resources_v0 v0; -+ struct qcom_pcie_resources_v1 v1; -+}; -+ -+struct qcom_pcie { -+ struct pcie_port pp; -+ struct device *dev; -+ union pcie_resources res; -+ void __iomem *parf; -+ void __iomem *dbi; -+ void __iomem *elbi; -+ struct phy *phy; -+ struct gpio_desc *reset; -+ unsigned int version; -+}; -+ -+#define to_qcom_pcie(x) container_of(x, struct qcom_pcie, pp) -+ -+static inline void -+writel_masked(void __iomem *addr, u32 clear_mask, u32 set_mask) -+{ -+ u32 val = readl(addr); -+ -+ val &= ~clear_mask; -+ val |= set_mask; -+ writel(val, addr); -+} -+ -+static void qcom_ep_reset_assert_deassert(struct qcom_pcie *pcie, int assert) -+{ -+ int val, active_low; -+ -+ if (IS_ERR_OR_NULL(pcie->reset)) -+ return; -+ -+ active_low = gpiod_is_active_low(pcie->reset); -+ -+ if (assert) -+ val = !!active_low; -+ else -+ val = !active_low; -+ -+ gpiod_set_value(pcie->reset, val); -+ -+ usleep_range(PERST_DELAY_MIN_US, PERST_DELAY_MAX_US); -+} -+ -+static void qcom_ep_reset_assert(struct qcom_pcie *pcie) -+{ -+ qcom_ep_reset_assert_deassert(pcie, 1); -+} -+ -+static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) -+{ -+ qcom_ep_reset_assert_deassert(pcie, 0); -+} -+ -+static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) -+{ -+ struct pcie_port *pp = arg; -+ -+ return dw_handle_msi_irq(pp); -+} -+ -+static int qcom_pcie_link_up(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ u32 val = readl(pcie->dbi + PCIE20_CAP_LINKCTRLSTATUS); -+ -+ return val & BIT(29) ? 1 : 0; -+} -+ -+static void qcom_pcie_disable_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ -+ reset_control_assert(res->pci_reset); -+ reset_control_assert(res->axi_reset); -+ reset_control_assert(res->ahb_reset); -+ reset_control_assert(res->por_reset); -+ reset_control_assert(res->pci_reset); -+ clk_disable_unprepare(res->iface_clk); -+ clk_disable_unprepare(res->core_clk); -+ clk_disable_unprepare(res->phy_clk); -+ regulator_disable(res->vdda); -+ regulator_disable(res->vdda_phy); -+ regulator_disable(res->vdda_refclk); -+} -+ -+static void qcom_pcie_disable_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ -+ reset_control_assert(res->core); -+ clk_disable_unprepare(res->slave_bus); -+ clk_disable_unprepare(res->master_bus); -+ clk_disable_unprepare(res->iface); -+ clk_disable_unprepare(res->aux); -+ regulator_disable(res->vdda); -+} -+ -+static int qcom_pcie_enable_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ ret = regulator_enable(res->vdda); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda regulator\n"); -+ return ret; -+ } -+ -+ ret = regulator_enable(res->vdda_refclk); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda_refclk regulator\n"); -+ goto err_refclk; -+ } -+ -+ ret = regulator_enable(res->vdda_phy); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda_phy regulator\n"); -+ goto err_vdda_phy; -+ } -+ -+ ret = clk_prepare_enable(res->iface_clk); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable iface clock\n"); -+ goto err_iface; -+ } -+ -+ ret = clk_prepare_enable(res->core_clk); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable core clock\n"); -+ goto err_clk_core; -+ } -+ -+ ret = clk_prepare_enable(res->phy_clk); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable phy clock\n"); -+ goto err_clk_phy; -+ } -+ -+ ret = reset_control_deassert(res->ahb_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert ahb reset\n"); -+ goto err_reset_ahb; -+ } -+ -+ return 0; -+ -+err_reset_ahb: -+ clk_disable_unprepare(res->phy_clk); -+err_clk_phy: -+ clk_disable_unprepare(res->core_clk); -+err_clk_core: -+ clk_disable_unprepare(res->iface_clk); -+err_iface: -+ regulator_disable(res->vdda_phy); -+err_vdda_phy: -+ regulator_disable(res->vdda_refclk); -+err_refclk: -+ regulator_disable(res->vdda); -+ return ret; -+} -+ -+static int qcom_pcie_enable_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ ret = reset_control_deassert(res->core); -+ if (ret) { -+ dev_err(dev, "cannot deassert core reset\n"); -+ return ret; -+ } -+ -+ ret = clk_prepare_enable(res->aux); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable aux clock\n"); -+ goto err_res; -+ } -+ -+ ret = clk_prepare_enable(res->iface); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable iface clock\n"); -+ goto err_aux; -+ } -+ -+ ret = clk_prepare_enable(res->master_bus); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable master_bus clock\n"); -+ goto err_iface; -+ } -+ -+ ret = clk_prepare_enable(res->slave_bus); -+ if (ret) { -+ dev_err(dev, "cannot prepare/enable slave_bus clock\n"); -+ goto err_master; -+ } -+ -+ ret = regulator_enable(res->vdda); -+ if (ret) { -+ dev_err(dev, "cannot enable vdda regulator\n"); -+ goto err_slave; -+ } -+ -+ return 0; -+ -+err_slave: -+ clk_disable_unprepare(res->slave_bus); -+err_master: -+ clk_disable_unprepare(res->master_bus); -+err_iface: -+ clk_disable_unprepare(res->iface); -+err_aux: -+ clk_disable_unprepare(res->aux); -+err_res: -+ reset_control_assert(res->core); -+ -+ return ret; -+} -+ -+static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ -+ res->vdda = devm_regulator_get(dev, "vdda"); -+ if (IS_ERR(res->vdda)) -+ return PTR_ERR(res->vdda); -+ -+ res->vdda_phy = devm_regulator_get(dev, "vdda_phy"); -+ if (IS_ERR(res->vdda_phy)) -+ return PTR_ERR(res->vdda_phy); -+ -+ res->vdda_refclk = devm_regulator_get(dev, "vdda_refclk"); -+ if (IS_ERR(res->vdda_refclk)) -+ return PTR_ERR(res->vdda_refclk); -+ -+ res->iface_clk = devm_clk_get(dev, "iface"); -+ if (IS_ERR(res->iface_clk)) -+ return PTR_ERR(res->iface_clk); -+ -+ res->core_clk = devm_clk_get(dev, "core"); -+ if (IS_ERR(res->core_clk)) -+ return PTR_ERR(res->core_clk); -+ -+ res->phy_clk = devm_clk_get(dev, "phy"); -+ if (IS_ERR(res->phy_clk)) -+ return PTR_ERR(res->phy_clk); -+ -+ res->pci_reset = devm_reset_control_get(dev, "pci"); -+ if (IS_ERR(res->pci_reset)) -+ return PTR_ERR(res->pci_reset); -+ -+ res->axi_reset = devm_reset_control_get(dev, "axi"); -+ if (IS_ERR(res->axi_reset)) -+ return PTR_ERR(res->axi_reset); -+ -+ res->ahb_reset = devm_reset_control_get(dev, "ahb"); -+ if (IS_ERR(res->ahb_reset)) -+ return PTR_ERR(res->ahb_reset); -+ -+ res->por_reset = devm_reset_control_get(dev, "por"); -+ if (IS_ERR(res->por_reset)) -+ return PTR_ERR(res->por_reset); -+ -+ res->phy_reset = devm_reset_control_get(dev, "phy"); -+ if (IS_ERR(res->phy_reset)) -+ return PTR_ERR(res->phy_reset); -+ -+ return 0; -+} -+ -+static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie) -+{ -+ struct qcom_pcie_resources_v1 *res = &pcie->res.v1; -+ struct device *dev = pcie->dev; -+ -+ res->vdda = devm_regulator_get(dev, "vdda"); -+ if (IS_ERR(res->vdda)) -+ return PTR_ERR(res->vdda); -+ -+ res->iface = devm_clk_get(dev, "iface"); -+ if (IS_ERR(res->iface)) -+ return PTR_ERR(res->iface); -+ -+ res->aux = devm_clk_get(dev, "aux"); -+ if (IS_ERR(res->aux) && PTR_ERR(res->aux) == -EPROBE_DEFER) -+ return -EPROBE_DEFER; -+ else if (IS_ERR(res->aux)) -+ res->aux = NULL; -+ -+ res->master_bus = devm_clk_get(dev, "master_bus"); -+ if (IS_ERR(res->master_bus)) -+ return PTR_ERR(res->master_bus); -+ -+ res->slave_bus = devm_clk_get(dev, "slave_bus"); -+ if (IS_ERR(res->slave_bus)) -+ return PTR_ERR(res->slave_bus); -+ -+ res->core = devm_reset_control_get(dev, "core"); -+ if (IS_ERR(res->core)) -+ return PTR_ERR(res->core); -+ -+ return 0; -+} -+ -+static int qcom_pcie_enable_link_training(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ struct device *dev = pp->dev; -+ int retries; -+ u32 val; -+ -+ /* enable link training */ -+ writel_masked(pcie->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0)); -+ -+ /* wait for up to 100ms for the link to come up */ -+ retries = LINKUP_RETRIES_COUNT; -+ do { -+ val = readl(pcie->elbi + PCIE20_ELBI_SYS_STTS); -+ if (val & XMLH_LINK_UP) -+ break; -+ usleep_range(LINKUP_DELAY_MIN_US, LINKUP_DELAY_MAX_US); -+ } while (retries--); -+ -+ if (retries < 0 || !dw_pcie_link_up(pp)) { -+ dev_err(dev, "link initialization failed\n"); -+ return -ENXIO; -+ } -+ -+ return 0; -+} -+ -+static void qcom_pcie_host_init_v1(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ int ret; -+ -+ qcom_ep_reset_assert(pcie); -+ -+ ret = qcom_pcie_enable_resources_v1(pcie); -+ if (ret) -+ return; -+ -+ /* change DBI base address */ -+ writel(0, pcie->parf + PCIE20_PARF_DBI_BASE_ADDR); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ writel_masked(pcie->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT, -+ 0, BIT(31)); -+ -+ ret = phy_init(pcie->phy); -+ if (ret) -+ goto err_res; -+ -+ ret = phy_power_on(pcie->phy); -+ if (ret) -+ goto err_phy; -+ -+ dw_pcie_setup_rc(pp); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ dw_pcie_msi_init(pp); -+ -+ qcom_ep_reset_deassert(pcie); -+ -+ ret = qcom_pcie_enable_link_training(pp); -+ if (ret) -+ goto err; -+ -+ return; -+ -+err: -+ qcom_ep_reset_assert(pcie); -+ phy_power_off(pcie->phy); -+err_phy: -+ phy_exit(pcie->phy); -+err_res: -+ qcom_pcie_disable_resources_v1(pcie); -+} -+ -+static void qcom_pcie_host_init_v0(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ struct qcom_pcie_resources_v0 *res = &pcie->res.v0; -+ struct device *dev = pcie->dev; -+ int ret; -+ -+ qcom_ep_reset_assert(pcie); -+ -+ ret = qcom_pcie_enable_resources_v0(pcie); -+ if (ret) -+ return; -+ -+ writel_masked(pcie->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0); -+ -+ /* enable external reference clock */ -+ writel_masked(pcie->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16)); -+ -+ ret = reset_control_deassert(res->phy_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert phy reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->pci_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert pci reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->por_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert por reset\n"); -+ return; -+ } -+ -+ ret = reset_control_deassert(res->axi_reset); -+ if (ret) { -+ dev_err(dev, "cannot deassert axi reset\n"); -+ return; -+ } -+ -+ /* wait 150ms for clock acquisition */ -+ usleep_range(10000, 15000); -+ -+ dw_pcie_setup_rc(pp); -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) -+ dw_pcie_msi_init(pp); -+ -+ qcom_ep_reset_deassert(pcie); -+ -+ ret = qcom_pcie_enable_link_training(pp); -+ if (ret) -+ goto err; -+ -+ return; -+err: -+ qcom_ep_reset_assert(pcie); -+ qcom_pcie_disable_resources_v0(pcie); -+} -+ -+static void qcom_pcie_host_init(struct pcie_port *pp) -+{ -+ struct qcom_pcie *pcie = to_qcom_pcie(pp); -+ -+ if (pcie->version == PCIE_V0) -+ return qcom_pcie_host_init_v0(pp); -+ else -+ return qcom_pcie_host_init_v1(pp); -+} -+ -+static int -+qcom_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val) -+{ -+ /* the device class is not reported correctly from the register */ -+ if (where == PCI_CLASS_REVISION && size == 4) { -+ *val = readl(pp->dbi_base + PCI_CLASS_REVISION); -+ *val &= ~(0xffff << 16); -+ *val |= PCI_CLASS_BRIDGE_PCI << 16; -+ return PCIBIOS_SUCCESSFUL; -+ } -+ -+ return dw_pcie_cfg_read(pp->dbi_base + (where & ~0x3), where, -+ size, val); -+} -+ -+static struct pcie_host_ops qcom_pcie_ops = { -+ .link_up = qcom_pcie_link_up, -+ .host_init = qcom_pcie_host_init, -+ .rd_own_conf = qcom_pcie_rd_own_conf, -+}; -+ -+static const struct of_device_id qcom_pcie_match[] = { -+ { .compatible = "qcom,pcie-v0", .data = (void *)PCIE_V0 }, -+ { .compatible = "qcom,pcie-v1", .data = (void *)PCIE_V1 }, -+ { } -+}; -+ -+static int qcom_pcie_probe(struct platform_device *pdev) -+{ -+ struct device *dev = &pdev->dev; -+ const struct of_device_id *match; -+ struct resource *res; -+ struct qcom_pcie *pcie; -+ struct pcie_port *pp; -+ int ret; -+ -+ match = of_match_node(qcom_pcie_match, dev->of_node); -+ if (!match) -+ return -ENXIO; -+ -+ pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); -+ if (!pcie) -+ return -ENOMEM; -+ -+ pcie->version = (unsigned int)match->data; -+ -+ pcie->reset = devm_gpiod_get_optional(dev, "perst"); -+ if (IS_ERR(pcie->reset) && PTR_ERR(pcie->reset) == -EPROBE_DEFER) -+ return PTR_ERR(pcie->reset); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); -+ pcie->parf = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->parf)) -+ return PTR_ERR(pcie->parf); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); -+ pcie->dbi = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->dbi)) -+ return PTR_ERR(pcie->dbi); -+ -+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); -+ pcie->elbi = devm_ioremap_resource(dev, res); -+ if (IS_ERR(pcie->elbi)) -+ return PTR_ERR(pcie->elbi); -+ -+ pcie->phy = devm_phy_optional_get(dev, "pciephy"); -+ if (IS_ERR(pcie->phy)) -+ return PTR_ERR(pcie->phy); -+ -+ pcie->dev = dev; -+ -+ if (pcie->version == PCIE_V0) -+ ret = qcom_pcie_get_resources_v0(pcie); -+ else -+ ret = qcom_pcie_get_resources_v1(pcie); -+ -+ if (ret) -+ return ret; -+ -+ pp = &pcie->pp; -+ pp->dev = dev; -+ pp->dbi_base = pcie->dbi; -+ pp->root_bus_nr = -1; -+ pp->ops = &qcom_pcie_ops; -+ -+ if (IS_ENABLED(CONFIG_PCI_MSI)) { -+ pp->msi_irq = platform_get_irq_byname(pdev, "msi"); -+ if (pp->msi_irq < 0) { -+ dev_err(dev, "cannot get msi irq\n"); -+ return pp->msi_irq; -+ } -+ -+ ret = devm_request_irq(dev, pp->msi_irq, -+ qcom_pcie_msi_irq_handler, -+ IRQF_SHARED, "qcom-pcie-msi", pp); -+ if (ret) { -+ dev_err(dev, "cannot request msi irq\n"); -+ return ret; -+ } -+ } -+ -+ ret = dw_pcie_host_init(pp); -+ if (ret) { -+ dev_err(dev, "cannot initialize host\n"); -+ return ret; -+ } -+ -+ platform_set_drvdata(pdev, pcie); -+ -+ return 0; -+} -+ -+static int qcom_pcie_remove(struct platform_device *pdev) -+{ -+ struct qcom_pcie *pcie = platform_get_drvdata(pdev); -+ -+ qcom_ep_reset_assert(pcie); -+ phy_power_off(pcie->phy); -+ phy_exit(pcie->phy); -+ if (pcie->version == PCIE_V0) -+ qcom_pcie_disable_resources_v0(pcie); -+ else -+ qcom_pcie_disable_resources_v1(pcie); -+ -+ return 0; -+} -+ -+static struct platform_driver qcom_pcie_driver = { -+ .probe = qcom_pcie_probe, -+ .remove = qcom_pcie_remove, -+ .driver = { -+ .name = "qcom-pcie", -+ .of_match_table = qcom_pcie_match, -+ }, -+}; -+ -+module_platform_driver(qcom_pcie_driver); -+ -+MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>"); -+MODULE_DESCRIPTION("Qualcomm PCIe root complex driver"); -+MODULE_LICENSE("GPL v2"); -+MODULE_ALIAS("platform:qcom-pcie"); ---- a/drivers/pci/host/Makefile -+++ b/drivers/pci/host/Makefile -@@ -11,3 +11,4 @@ obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spe - obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o - obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o - obj-$(CONFIG_PCI_XGENE) += pci-xgene.o -+obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o |