diff options
Diffstat (limited to 'target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch')
-rw-r--r-- | target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch | 716 |
1 files changed, 0 insertions, 716 deletions
diff --git a/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch b/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch deleted file mode 100644 index 8aaa766..0000000 --- a/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch +++ /dev/null @@ -1,716 +0,0 @@ -From 98567c99b4dcd80fc9e5dd97229ebb9a7f6dab03 Mon Sep 17 00:00:00 2001 -From: Kumar Gala <galak@codeaurora.org> -Date: Fri, 16 May 2014 11:53:23 -0500 -Subject: [PATCH 138/182] PCI: qcom: Add support for pcie controllers on - IPQ8064 - ---- - arch/arm/mach-qcom/Kconfig | 2 + - drivers/pci/host/Makefile | 1 + - drivers/pci/host/pci-qcom.c | 682 +++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 685 insertions(+) - create mode 100644 drivers/pci/host/pci-qcom.c - ---- a/arch/arm/mach-qcom/Kconfig -+++ b/arch/arm/mach-qcom/Kconfig -@@ -7,6 +7,8 @@ config ARCH_QCOM - select GENERIC_CLOCKEVENTS - select HAVE_SMP - select PINCTRL -+ select MIGHT_HAVE_PCI -+ select PCI_DOMAINS if PCI - select QCOM_SCM if SMP - help - Support for Qualcomm's devicetree based systems. ---- a/drivers/pci/host/Makefile -+++ b/drivers/pci/host/Makefile -@@ -4,3 +4,4 @@ obj-$(CONFIG_PCI_IMX6) += pci-imx6.o - obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o - obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o - obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o -+obj-$(CONFIG_ARCH_QCOM) += pci-qcom.o ---- /dev/null -+++ b/drivers/pci/host/pci-qcom.c -@@ -0,0 +1,682 @@ -+/* Copyright (c) 2012-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. -+ */ -+ -+/* -+ * QCOM MSM PCIe controller driver. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/pci.h> -+#include <linux/gpio.h> -+#include <linux/of_gpio.h> -+#include <linux/platform_device.h> -+#include <linux/of_address.h> -+#include <linux/clk.h> -+#include <linux/reset.h> -+#include <linux/delay.h> -+ -+/* Root Complex Port vendor/device IDs */ -+#define PCIE_VENDOR_ID_RCP 0x17cb -+#define PCIE_DEVICE_ID_RCP 0x0101 -+ -+#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b)) -+ -+#define PCIE20_PARF_PCS_DEEMPH 0x34 -+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(x) __set(x, 21, 16) -+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) __set(x, 13, 8) -+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) __set(x, 5, 0) -+ -+#define PCIE20_PARF_PCS_SWING 0x38 -+#define PCIE20_PARF_PCS_SWING_TX_SWING_FULL(x) __set(x, 14, 8) -+#define PCIE20_PARF_PCS_SWING_TX_SWING_LOW(x) __set(x, 6, 0) -+ -+#define PCIE20_PARF_PHY_CTRL 0x40 -+#define PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(x) __set(x, 20, 16) -+#define PCIE20_PARF_PHY_CTRL_PHY_LOS_LEVEL(x) __set(x, 12, 8) -+#define PCIE20_PARF_PHY_CTRL_PHY_RTUNE_REQ (1 << 4) -+#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BURNIN (1 << 2) -+#define PCIE20_PARF_PHY_CTRL_PHY_TEST_BYPASS (1 << 1) -+#define PCIE20_PARF_PHY_CTRL_PHY_TEST_PWR_DOWN (1 << 0) -+ -+#define PCIE20_PARF_PHY_REFCLK 0x4C -+#define PCIE20_PARF_CONFIG_BITS 0x50 -+ -+#define PCIE20_ELBI_SYS_CTRL 0x04 -+#define PCIE20_ELBI_SYS_CTRL_LTSSM_EN 0x01 -+ -+#define PCIE20_CAP 0x70 -+#define PCIE20_CAP_LINKCTRLSTATUS (PCIE20_CAP + 0x10) -+ -+#define PCIE20_COMMAND_STATUS 0x04 -+#define PCIE20_BUSNUMBERS 0x18 -+#define PCIE20_MEMORY_BASE_LIMIT 0x20 -+ -+#define PCIE20_AXI_MSTR_RESP_COMP_CTRL0 0x818 -+#define PCIE20_AXI_MSTR_RESP_COMP_CTRL1 0x81c -+#define PCIE20_PLR_IATU_VIEWPORT 0x900 -+#define PCIE20_PLR_IATU_CTRL1 0x904 -+#define PCIE20_PLR_IATU_CTRL2 0x908 -+#define PCIE20_PLR_IATU_LBAR 0x90C -+#define PCIE20_PLR_IATU_UBAR 0x910 -+#define PCIE20_PLR_IATU_LAR 0x914 -+#define PCIE20_PLR_IATU_LTAR 0x918 -+#define PCIE20_PLR_IATU_UTAR 0x91c -+ -+#define MSM_PCIE_DEV_CFG_ADDR 0x01000000 -+ -+#define RD 0 -+#define WR 1 -+ -+#define MAX_RC_NUM 3 -+#define PCIE_BUS_PRIV_DATA(pdev) \ -+ (((struct pci_sys_data *)pdev->bus->sysdata)->private_data) -+ -+/* PCIe TLP types that we are interested in */ -+#define PCI_CFG0_RDWR 0x4 -+#define PCI_CFG1_RDWR 0x5 -+ -+#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \ -+({ \ -+ unsigned long timeout = jiffies + usecs_to_jiffies(timeout_us); \ -+ might_sleep_if(timeout_us); \ -+ for (;;) { \ -+ (val) = readl(addr); \ -+ if (cond) \ -+ break; \ -+ if (timeout_us && time_after(jiffies, timeout)) { \ -+ (val) = readl(addr); \ -+ break; \ -+ } \ -+ if (sleep_us) \ -+ usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \ -+ } \ -+ (cond) ? 0 : -ETIMEDOUT; \ -+}) -+ -+struct qcom_pcie { -+ void __iomem *elbi_base; -+ void __iomem *parf_base; -+ void __iomem *dwc_base; -+ void __iomem *cfg_base; -+ int reset_gpio; -+ struct clk *iface_clk; -+ struct clk *bus_clk; -+ struct clk *phy_clk; -+ int irq_int[4]; -+ struct reset_control *axi_reset; -+ struct reset_control *ahb_reset; -+ struct reset_control *por_reset; -+ struct reset_control *pci_reset; -+ struct reset_control *phy_reset; -+ -+ struct resource conf; -+ struct resource io; -+ struct resource mem; -+}; -+ -+static int qcom_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin); -+static int qcom_pcie_setup(int nr, struct pci_sys_data *sys); -+static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, -+ int size, u32 *val); -+static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn, -+ int where, int size, u32 val); -+ -+static struct pci_ops qcom_pcie_ops = { -+ .read = msm_pcie_rd_conf, -+ .write = msm_pcie_wr_conf, -+}; -+ -+static struct hw_pci qcom_hw_pci[MAX_RC_NUM] = { -+ { -+#ifdef CONFIG_PCI_DOMAINS -+ .domain = 0, -+#endif -+ .ops = &qcom_pcie_ops, -+ .nr_controllers = 1, -+ .swizzle = pci_common_swizzle, -+ .setup = qcom_pcie_setup, -+ .map_irq = qcom_pcie_map_irq, -+ }, -+ { -+#ifdef CONFIG_PCI_DOMAINS -+ .domain = 1, -+#endif -+ .ops = &qcom_pcie_ops, -+ .nr_controllers = 1, -+ .swizzle = pci_common_swizzle, -+ .setup = qcom_pcie_setup, -+ .map_irq = qcom_pcie_map_irq, -+ }, -+ { -+#ifdef CONFIG_PCI_DOMAINS -+ .domain = 2, -+#endif -+ .ops = &qcom_pcie_ops, -+ .nr_controllers = 1, -+ .swizzle = pci_common_swizzle, -+ .setup = qcom_pcie_setup, -+ .map_irq = qcom_pcie_map_irq, -+ }, -+}; -+ -+static int nr_controllers; -+static DEFINE_SPINLOCK(qcom_hw_pci_lock); -+ -+static inline struct qcom_pcie *sys_to_pcie(struct pci_sys_data *sys) -+{ -+ return sys->private_data; -+} -+ -+inline int is_msm_pcie_rc(struct pci_bus *bus) -+{ -+ return (bus->number == 0); -+} -+ -+static int qcom_pcie_is_link_up(struct qcom_pcie *dev) -+{ -+ return readl_relaxed(dev->dwc_base + PCIE20_CAP_LINKCTRLSTATUS) & BIT(29); -+} -+ -+inline int msm_pcie_get_cfgtype(struct pci_bus *bus) -+{ -+ /* -+ * http://www.tldp.org/LDP/tlk/dd/pci.html -+ * Pass it onto the secondary bus interface unchanged if the -+ * bus number specified is greater than the secondary bus -+ * number and less than or equal to the subordinate bus -+ * number. -+ * -+ * Read/Write to the RC and Device/Switch connected to the RC -+ * are CFG0 type transactions. Rest have to be forwarded -+ * down stream as CFG1 transactions. -+ * -+ */ -+ if (bus->number == 0) -+ return PCI_CFG0_RDWR; -+ -+ return PCI_CFG0_RDWR; -+} -+ -+void msm_pcie_config_cfgtype(struct pci_bus *bus, u32 devfn) -+{ -+ uint32_t bdf, cfgtype; -+ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata); -+ -+ cfgtype = msm_pcie_get_cfgtype(bus); -+ -+ if (cfgtype == PCI_CFG0_RDWR) { -+ bdf = MSM_PCIE_DEV_CFG_ADDR; -+ } else { -+ /* -+ * iATU Lower Target Address Register -+ * Bits Description -+ * *-1:0 Forms bits [*:0] of the -+ * start address of the new -+ * address of the translated -+ * region. The start address -+ * must be aligned to a -+ * CX_ATU_MIN_REGION_SIZE kB -+ * boundary, so these bits are -+ * always 0. A write to this -+ * location is ignored by the -+ * PCIe core. -+ * 31:*1 Forms bits [31:*] of the of -+ * the new address of the -+ * translated region. -+ * -+ * * is log2(CX_ATU_MIN_REGION_SIZE) -+ */ -+ bdf = (((bus->number & 0xff) << 24) & 0xff000000) | -+ (((devfn & 0xff) << 16) & 0x00ff0000); -+ } -+ -+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT); -+ wmb(); -+ -+ /* Program Bdf Address */ -+ writel_relaxed(bdf, dev->dwc_base + PCIE20_PLR_IATU_LTAR); -+ wmb(); -+ -+ /* Write Config Request Type */ -+ writel_relaxed(cfgtype, dev->dwc_base + PCIE20_PLR_IATU_CTRL1); -+ wmb(); -+} -+ -+static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper, -+ int where, int size, u32 *val) -+{ -+ uint32_t word_offset, byte_offset, mask; -+ uint32_t rd_val, wr_val; -+ struct qcom_pcie *dev = sys_to_pcie(bus->sysdata); -+ void __iomem *config_base; -+ int rc; -+ -+ rc = is_msm_pcie_rc(bus); -+ -+ /* -+ * For downstream bus, make sure link is up -+ */ -+ if (rc && (devfn != 0)) { -+ *val = ~0; -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ } else if ((!rc) && (!qcom_pcie_is_link_up(dev))) { -+ *val = ~0; -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ } -+ -+ msm_pcie_config_cfgtype(bus, devfn); -+ -+ word_offset = where & ~0x3; -+ byte_offset = where & 0x3; -+ mask = (~0 >> (8 * (4 - size))) << (8 * byte_offset); -+ -+ config_base = (rc) ? dev->dwc_base : dev->cfg_base; -+ rd_val = readl_relaxed(config_base + word_offset); -+ -+ if (oper == RD) { -+ *val = ((rd_val & mask) >> (8 * byte_offset)); -+ } else { -+ wr_val = (rd_val & ~mask) | -+ ((*val << (8 * byte_offset)) & mask); -+ writel_relaxed(wr_val, config_base + word_offset); -+ wmb(); /* ensure config data is written to hardware register */ -+ } -+ -+ return 0; -+} -+ -+static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, -+ int size, u32 *val) -+{ -+ return msm_pcie_oper_conf(bus, devfn, RD, where, size, val); -+} -+ -+static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn, -+ int where, int size, u32 val) -+{ -+ /* -+ *Attempt to reset secondary bus is causing PCIE core to reset. -+ *Disable secondary bus reset functionality. -+ */ -+ if ((bus->number == 0) && (where == PCI_BRIDGE_CONTROL) && -+ (val & PCI_BRIDGE_CTL_BUS_RESET)) { -+ pr_info("PCIE secondary bus reset not supported\n"); -+ val &= ~PCI_BRIDGE_CTL_BUS_RESET; -+ } -+ -+ return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val); -+} -+ -+static int qcom_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -+{ -+ struct qcom_pcie *pcie_dev = PCIE_BUS_PRIV_DATA(dev); -+ -+ return pcie_dev->irq_int[pin-1]; -+} -+ -+static int qcom_pcie_setup(int nr, struct pci_sys_data *sys) -+{ -+ struct qcom_pcie *qcom_pcie = sys->private_data; -+ -+ /* -+ * specify linux PCI framework to allocate device memory (BARs) -+ * from msm_pcie_dev.dev_mem_res resource. -+ */ -+ sys->mem_offset = 0; -+ sys->io_offset = 0; -+ -+ pci_add_resource(&sys->resources, &qcom_pcie->mem); -+ pci_add_resource(&sys->resources, &qcom_pcie->io); -+ -+ return 1; -+} -+ -+static inline void qcom_elbi_writel_relaxed(struct qcom_pcie *pcie, u32 val, u32 reg) -+{ -+ writel_relaxed(val, pcie->elbi_base + reg); -+} -+ -+static inline u32 qcom_elbi_readl_relaxed(struct qcom_pcie *pcie, u32 reg) -+{ -+ return readl_relaxed(pcie->elbi_base + reg); -+} -+ -+static inline void qcom_parf_writel_relaxed(struct qcom_pcie *pcie, u32 val, u32 reg) -+{ -+ writel_relaxed(val, pcie->parf_base + reg); -+} -+ -+static inline u32 qcom_parf_readl_relaxed(struct qcom_pcie *pcie, u32 reg) -+{ -+ return readl_relaxed(pcie->parf_base + reg); -+} -+ -+static void msm_pcie_write_mask(void __iomem *addr, -+ uint32_t clear_mask, uint32_t set_mask) -+{ -+ uint32_t val; -+ -+ val = (readl_relaxed(addr) & ~clear_mask) | set_mask; -+ writel_relaxed(val, addr); -+ wmb(); /* ensure data is written to hardware register */ -+} -+ -+static void qcom_pcie_config_controller(struct qcom_pcie *dev) -+{ -+ /* -+ * program and enable address translation region 0 (device config -+ * address space); region type config; -+ * axi config address range to device config address range -+ */ -+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT); -+ /* ensure that hardware locks the region before programming it */ -+ wmb(); -+ -+ writel_relaxed(4, dev->dwc_base + PCIE20_PLR_IATU_CTRL1); -+ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2); -+ writel_relaxed(dev->conf.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR); -+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR); -+ writel_relaxed(dev->conf.end, dev->dwc_base + PCIE20_PLR_IATU_LAR); -+ writel_relaxed(MSM_PCIE_DEV_CFG_ADDR, -+ dev->dwc_base + PCIE20_PLR_IATU_LTAR); -+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR); -+ /* ensure that hardware registers the configuration */ -+ wmb(); -+ -+ /* -+ * program and enable address translation region 2 (device resource -+ * address space); region type memory; -+ * axi device bar address range to device bar address range -+ */ -+ writel_relaxed(2, dev->dwc_base + PCIE20_PLR_IATU_VIEWPORT); -+ /* ensure that hardware locks the region before programming it */ -+ wmb(); -+ -+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_CTRL1); -+ writel_relaxed(BIT(31), dev->dwc_base + PCIE20_PLR_IATU_CTRL2); -+ writel_relaxed(dev->mem.start, dev->dwc_base + PCIE20_PLR_IATU_LBAR); -+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UBAR); -+ writel_relaxed(dev->mem.end, dev->dwc_base + PCIE20_PLR_IATU_LAR); -+ writel_relaxed(dev->mem.start, -+ dev->dwc_base + PCIE20_PLR_IATU_LTAR); -+ writel_relaxed(0, dev->dwc_base + PCIE20_PLR_IATU_UTAR); -+ /* ensure that hardware registers the configuration */ -+ wmb(); -+ -+ /* 1K PCIE buffer setting */ -+ writel_relaxed(0x3, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL0); -+ writel_relaxed(0x1, dev->dwc_base + PCIE20_AXI_MSTR_RESP_COMP_CTRL1); -+ /* ensure that hardware registers the configuration */ -+ wmb(); -+} -+ -+static int qcom_pcie_probe(struct platform_device *pdev) -+{ -+ unsigned long flags; -+ struct qcom_pcie *qcom_pcie; -+ struct device_node *np = pdev->dev.of_node; -+ struct resource *elbi_base, *parf_base, *dwc_base; -+ struct hw_pci *hw; -+ struct of_pci_range range; -+ struct of_pci_range_parser parser; -+ int ret, i; -+ u32 val; -+ -+ qcom_pcie = devm_kzalloc(&pdev->dev, sizeof(*qcom_pcie), GFP_KERNEL); -+ if (!qcom_pcie) { -+ dev_err(&pdev->dev, "no memory for qcom_pcie\n"); -+ return -ENOMEM; -+ } -+ -+ elbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); -+ qcom_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base); -+ if (IS_ERR(qcom_pcie->elbi_base)) { -+ dev_err(&pdev->dev, "Failed to ioremap elbi space\n"); -+ return PTR_ERR(qcom_pcie->elbi_base); -+ } -+ -+ parf_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "parf"); -+ qcom_pcie->parf_base = devm_ioremap_resource(&pdev->dev, parf_base); -+ if (IS_ERR(qcom_pcie->parf_base)) { -+ dev_err(&pdev->dev, "Failed to ioremap parf space\n"); -+ return PTR_ERR(qcom_pcie->parf_base); -+ } -+ -+ dwc_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); -+ qcom_pcie->dwc_base = devm_ioremap_resource(&pdev->dev, dwc_base); -+ if (IS_ERR(qcom_pcie->dwc_base)) { -+ dev_err(&pdev->dev, "Failed to ioremap dwc_base space\n"); -+ return PTR_ERR(qcom_pcie->dwc_base); -+ } -+ -+ if (of_pci_range_parser_init(&parser, np)) { -+ dev_err(&pdev->dev, "missing ranges property\n"); -+ return -EINVAL; -+ } -+ -+ /* Get the I/O and memory ranges from DT */ -+ for_each_of_pci_range(&parser, &range) { -+ switch (range.pci_space & 0x3) { -+ case 0: /* cfg */ -+ of_pci_range_to_resource(&range, np, &qcom_pcie->conf); -+ qcom_pcie->conf.flags = IORESOURCE_MEM; -+ break; -+ case 1: /* io */ -+ of_pci_range_to_resource(&range, np, &qcom_pcie->io); -+ break; -+ default: /* mem */ -+ of_pci_range_to_resource(&range, np, &qcom_pcie->mem); -+ break; -+ } -+ } -+ -+ qcom_pcie->cfg_base = devm_ioremap_resource(&pdev->dev, &qcom_pcie->conf); -+ if (IS_ERR(qcom_pcie->cfg_base)) { -+ dev_err(&pdev->dev, "Failed to ioremap PCIe cfg space\n"); -+ return PTR_ERR(qcom_pcie->cfg_base); -+ } -+ -+ qcom_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); -+ if (!gpio_is_valid(qcom_pcie->reset_gpio)) { -+ dev_err(&pdev->dev, "pcie reset gpio is not valid\n"); -+ return -EINVAL; -+ } -+ -+ ret = devm_gpio_request_one(&pdev->dev, qcom_pcie->reset_gpio, -+ GPIOF_DIR_OUT, "pcie_reset"); -+ if (ret) { -+ dev_err(&pdev->dev, "Failed to request pcie reset gpio\n"); -+ return ret; -+ } -+ -+ qcom_pcie->iface_clk = devm_clk_get(&pdev->dev, "iface"); -+ if (IS_ERR(qcom_pcie->iface_clk)) { -+ dev_err(&pdev->dev, "Failed to get pcie iface clock\n"); -+ return PTR_ERR(qcom_pcie->iface_clk); -+ } -+ -+ qcom_pcie->phy_clk = devm_clk_get(&pdev->dev, "phy"); -+ if (IS_ERR(qcom_pcie->phy_clk)) { -+ dev_err(&pdev->dev, "Failed to get pcie phy clock\n"); -+ return PTR_ERR(qcom_pcie->phy_clk); -+ } -+ -+ qcom_pcie->bus_clk = devm_clk_get(&pdev->dev, "core"); -+ if (IS_ERR(qcom_pcie->bus_clk)) { -+ dev_err(&pdev->dev, "Failed to get pcie core clock\n"); -+ return PTR_ERR(qcom_pcie->bus_clk); -+ } -+ -+ qcom_pcie->axi_reset = devm_reset_control_get(&pdev->dev, "axi"); -+ if (IS_ERR(qcom_pcie->axi_reset)) { -+ dev_err(&pdev->dev, "Failed to get axi reset\n"); -+ return PTR_ERR(qcom_pcie->axi_reset); -+ } -+ -+ qcom_pcie->ahb_reset = devm_reset_control_get(&pdev->dev, "ahb"); -+ if (IS_ERR(qcom_pcie->ahb_reset)) { -+ dev_err(&pdev->dev, "Failed to get ahb reset\n"); -+ return PTR_ERR(qcom_pcie->ahb_reset); -+ } -+ -+ qcom_pcie->por_reset = devm_reset_control_get(&pdev->dev, "por"); -+ if (IS_ERR(qcom_pcie->por_reset)) { -+ dev_err(&pdev->dev, "Failed to get por reset\n"); -+ return PTR_ERR(qcom_pcie->por_reset); -+ } -+ -+ qcom_pcie->pci_reset = devm_reset_control_get(&pdev->dev, "pci"); -+ if (IS_ERR(qcom_pcie->pci_reset)) { -+ dev_err(&pdev->dev, "Failed to get pci reset\n"); -+ return PTR_ERR(qcom_pcie->pci_reset); -+ } -+ -+ qcom_pcie->phy_reset = devm_reset_control_get(&pdev->dev, "phy"); -+ if (IS_ERR(qcom_pcie->phy_reset)) { -+ dev_err(&pdev->dev, "Failed to get phy reset\n"); -+ return PTR_ERR(qcom_pcie->phy_reset); -+ } -+ -+ for (i = 0; i < 4; i++) { -+ qcom_pcie->irq_int[i] = platform_get_irq(pdev, i+1); -+ if (qcom_pcie->irq_int[i] < 0) { -+ dev_err(&pdev->dev, "failed to get irq resource\n"); -+ return qcom_pcie->irq_int[i]; -+ } -+ } -+ -+ gpio_set_value(qcom_pcie->reset_gpio, 0); -+ usleep_range(10000, 15000); -+ -+ /* assert PCIe PARF reset while powering the core */ -+ reset_control_assert(qcom_pcie->ahb_reset); -+ -+ /* enable clocks */ -+ ret = clk_prepare_enable(qcom_pcie->iface_clk); -+ if (ret) -+ return ret; -+ ret = clk_prepare_enable(qcom_pcie->phy_clk); -+ if (ret) -+ return ret; -+ ret = clk_prepare_enable(qcom_pcie->bus_clk); -+ if (ret) -+ return ret; -+ -+ /* -+ * de-assert PCIe PARF reset; -+ * wait 1us before accessing PARF registers -+ */ -+ reset_control_deassert(qcom_pcie->ahb_reset); -+ udelay(1); -+ -+ /* enable PCIe clocks and resets */ -+ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_CTRL, BIT(0), 0); -+ -+ /* Set Tx Termination Offset */ -+ val = qcom_parf_readl_relaxed(qcom_pcie, PCIE20_PARF_PHY_CTRL); -+ val |= PCIE20_PARF_PHY_CTRL_PHY_TX0_TERM_OFFST(7); -+ qcom_parf_writel_relaxed(qcom_pcie, val, PCIE20_PARF_PHY_CTRL); -+ -+ /* PARF programming */ -+ qcom_parf_writel_relaxed(qcom_pcie, PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(0x18) | -+ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(0x18) | -+ PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(0x22), -+ PCIE20_PARF_PCS_DEEMPH); -+ qcom_parf_writel_relaxed(qcom_pcie, PCIE20_PARF_PCS_SWING_TX_SWING_FULL(0x78) | -+ PCIE20_PARF_PCS_SWING_TX_SWING_LOW(0x78), -+ PCIE20_PARF_PCS_SWING); -+ qcom_parf_writel_relaxed(qcom_pcie, (4<<24), PCIE20_PARF_CONFIG_BITS); -+ /* ensure that hardware registers the PARF configuration */ -+ wmb(); -+ -+ /* enable reference clock */ -+ msm_pcie_write_mask(qcom_pcie->parf_base + PCIE20_PARF_PHY_REFCLK, BIT(12), BIT(16)); -+ -+ /* ensure that access is enabled before proceeding */ -+ wmb(); -+ -+ /* de-assert PICe PHY, Core, POR and AXI clk domain resets */ -+ reset_control_deassert(qcom_pcie->phy_reset); -+ reset_control_deassert(qcom_pcie->pci_reset); -+ reset_control_deassert(qcom_pcie->por_reset); -+ reset_control_deassert(qcom_pcie->axi_reset); -+ -+ /* wait 150ms for clock acquisition */ -+ usleep_range(10000, 15000); -+ -+ /* de-assert PCIe reset link to bring EP out of reset */ -+ gpio_set_value(qcom_pcie->reset_gpio, 1 - 0); -+ usleep_range(10000, 15000); -+ -+ /* enable link training */ -+ val = qcom_elbi_readl_relaxed(qcom_pcie, PCIE20_ELBI_SYS_CTRL); -+ val |= PCIE20_ELBI_SYS_CTRL_LTSSM_EN; -+ qcom_elbi_writel_relaxed(qcom_pcie, val, PCIE20_ELBI_SYS_CTRL); -+ wmb(); -+ -+ /* poll for link to come up for upto 100ms */ -+ ret = readl_poll_timeout( -+ (qcom_pcie->dwc_base + PCIE20_CAP_LINKCTRLSTATUS), -+ val, (val & BIT(29)), 10000, 100000); -+ -+ printk("link initialized %d\n", ret); -+ -+ qcom_pcie_config_controller(qcom_pcie); -+ -+ platform_set_drvdata(pdev, qcom_pcie); -+ -+ spin_lock_irqsave(&qcom_hw_pci_lock, flags); -+ qcom_hw_pci[nr_controllers].private_data = (void **)&qcom_pcie; -+ hw = &qcom_hw_pci[nr_controllers]; -+ nr_controllers++; -+ spin_unlock_irqrestore(&qcom_hw_pci_lock, flags); -+ -+ pci_common_init(hw); -+ -+ return 0; -+} -+ -+static int __exit qcom_pcie_remove(struct platform_device *pdev) -+{ -+ struct qcom_pcie *qcom_pcie = platform_get_drvdata(pdev); -+ -+ return 0; -+} -+ -+static struct of_device_id qcom_pcie_match[] = { -+ { .compatible = "qcom,pcie-ipq8064", }, -+ {} -+}; -+ -+static struct platform_driver qcom_pcie_driver = { -+ .probe = qcom_pcie_probe, -+ .remove = qcom_pcie_remove, -+ .driver = { -+ .name = "qcom_pcie", -+ .owner = THIS_MODULE, -+ .of_match_table = qcom_pcie_match, -+ }, -+}; -+ -+static int qcom_pcie_init(void) -+{ -+ return platform_driver_register(&qcom_pcie_driver); -+} -+subsys_initcall(qcom_pcie_init); -+ -+/* RC do not represent the right class; set it to PCI_CLASS_BRIDGE_PCI */ -+static void msm_pcie_fixup_early(struct pci_dev *dev) -+{ -+ if (dev->hdr_type == 1) -+ dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8); -+} -+DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP, msm_pcie_fixup_early); |