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 | 726 |
1 files changed, 726 insertions, 0 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 new file mode 100644 index 0000000000..b1ff04b90d --- /dev/null +++ b/target/linux/ipq806x/patches/0138-PCI-qcom-Add-support-for-pcie-controllers-on-IPQ8064.patch @@ -0,0 +1,726 @@ +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 + +diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig +index 63502cc..4d242e5 100644 +--- 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. +diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile +index 13fb333..be80744 100644 +--- 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 +diff --git a/drivers/pci/host/pci-qcom.c b/drivers/pci/host/pci-qcom.c +new file mode 100644 +index 0000000..76d7b88 +--- /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); +-- +1.7.10.4 + |