msm: pcie: MSM PCIe controller driver

MSM PCIe controller driver enables the PCIe core along with the
Root Complex (RC) port in the MSM chipsets.

Change-Id: Id66d1f00392b3d5cd1ea00ff2e24f50de134bfa2
Signed-off-by: Niranjana Vishwanathapura <nvishwan@codeaurora.org>
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 6f33d86..c294d59 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -242,6 +242,8 @@
 	select FIX_MOVABLE_ZONE if ENABLE_DMM
 	select CLEANCACHE
 	select QCACHE
+	select MIGHT_HAVE_PCI
+	select ARCH_SUPPORTS_MSI
 
 config ARCH_MSMCOPPER
 	bool "MSM Copper"
@@ -1216,6 +1218,13 @@
 	    prompt "Package 4"
 endchoice
 
+config MSM_PCIE
+	bool "MSM PCIe Controller driver"
+	depends on PCI && PCI_MSI
+	help
+	  Enables the PCIe functionality by configures PCIe core on
+	  MSM chipset and by enabling the ARM PCI framework extension.
+
 config MSM_RPC_SDIO_XPRT
 	depends on MSM_SDIO_AL
 	default y
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index c5b0081..000cf43 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -156,6 +156,8 @@
 obj-$(CONFIG_MSM_PM) += pm.o
 obj-$(CONFIG_MSM_NOPM) += no-pm.o
 
+obj-$(CONFIG_MSM_PCIE) += pcie.o pcie_irq.o
+
 obj-$(CONFIG_MSM_SPM_V1) += spm.o
 obj-$(CONFIG_MSM_SPM_V2) += spm-v2.o spm_devices.o
 
diff --git a/arch/arm/mach-msm/include/mach/hardware.h b/arch/arm/mach-msm/include/mach/hardware.h
index 2d12609..f1095af 100644
--- a/arch/arm/mach-msm/include/mach/hardware.h
+++ b/arch/arm/mach-msm/include/mach/hardware.h
@@ -1,6 +1,7 @@
 /* arch/arm/mach-msm/include/mach/hardware.h
  *
  * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -14,5 +15,10 @@
  */
 
 #ifndef __ASM_ARCH_MSM_HARDWARE_H
+#define __ASM_ARCH_MSM_HARDWARE_H
+
+#define PCIBIOS_MIN_IO                  0x10000000
+#define PCIBIOS_MIN_MEM                 0x10000000
+#define pcibios_assign_all_busses()     1
 
 #endif
diff --git a/arch/arm/mach-msm/include/mach/irqs.h b/arch/arm/mach-msm/include/mach/irqs.h
index ff011a8..e3904b4 100644
--- a/arch/arm/mach-msm/include/mach/irqs.h
+++ b/arch/arm/mach-msm/include/mach/irqs.h
@@ -45,8 +45,14 @@
 #define NR_WCD9XXX_IRQS 49
 #define NR_TABLA_IRQS NR_WCD9XXX_IRQS
 #define NR_GPIO_EXPANDER_IRQS 64
+#ifdef CONFIG_PCI_MSI
+#define NR_PCIE_MSI_IRQS 256
+#define NR_BOARD_IRQS (NR_PM8921_IRQS + NR_PM8821_IRQS + \
+		NR_WCD9XXX_IRQS + NR_GPIO_EXPANDER_IRQS + NR_PCIE_MSI_IRQS)
+#else
 #define NR_BOARD_IRQS (NR_PM8921_IRQS + NR_PM8821_IRQS + \
 		NR_WCD9XXX_IRQS + NR_GPIO_EXPANDER_IRQS)
+#endif
 #define NR_TLMM_MSM_DIR_CONN_IRQ 8 /*Need to Verify this Count*/
 #define NR_MSM_GPIOS NR_GPIO_IRQS
 
@@ -85,4 +91,9 @@
 #define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0)
 #define MSM_INT_TO_REG(base, irq) (base + irq / 32)
 
+#if defined(CONFIG_PCI_MSI) && defined(CONFIG_MSM_PCIE)
+#define MSM_PCIE_MSI_INT(n) (NR_MSM_IRQS + NR_GPIO_IRQS + NR_PM8921_IRQS +  \
+		NR_PM8821_IRQS + NR_TABLA_IRQS + NR_GPIO_EXPANDER_IRQS + (n))
+#endif
+
 #endif
diff --git a/arch/arm/mach-msm/include/mach/msm_pcie.h b/arch/arm/mach-msm/include/mach/msm_pcie.h
new file mode 100644
index 0000000..008c984
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_pcie.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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.
+ */
+
+#ifndef __ASM_ARCH_MSM_PCIE_H
+#define __ASM_ARCH_MSM_PCIE_H
+
+#include <linux/types.h>
+
+/* gpios */
+enum msm_pcie_gpio {
+	MSM_PCIE_GPIO_RST_N,
+	MSM_PCIE_GPIO_PWR_EN,
+	MSM_PCIE_MAX_GPIO
+};
+
+/* gpio info structrue */
+struct msm_pcie_gpio_info_t {
+	char      *name;
+	uint32_t   num;
+	uint32_t   on;
+};
+
+/* msm pcie platfrom data */
+struct msm_pcie_platform {
+	struct msm_pcie_gpio_info_t  *gpio;
+};
+
+#endif
diff --git a/arch/arm/mach-msm/pcie.c b/arch/arm/mach-msm/pcie.c
new file mode 100644
index 0000000..4e2b1083
--- /dev/null
+++ b/arch/arm/mach-msm/pcie.c
@@ -0,0 +1,669 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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.
+ */
+
+/*
+ * MSM PCIe controller driver.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <asm/mach/pci.h>
+#include <mach/gpiomux.h>
+#include <mach/hardware.h>
+#include <mach/msm_iomap.h>
+
+#include "pcie.h"
+
+/* Root Complex Port vendor/device IDs */
+#define PCIE_VENDOR_ID_RCP             0x17cb
+#define PCIE_DEVICE_ID_RCP             0x0101
+
+#define PCIE20_PARF_PCS_DEEMPH         0x34
+#define PCIE20_PARF_PCS_SWING          0x38
+#define PCIE20_PARF_PHY_CTRL           0x40
+#define PCIE20_PARF_PHY_REFCLK         0x4C
+#define PCIE20_PARF_CONFIG_BITS        0x50
+
+#define PCIE20_ELBI_SYS_CTRL           0x04
+
+#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_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 PCIE_RESET                     (MSM_CLK_CTL_BASE + 0x22dc)
+#define PCIE_SFAB_AXI_S5_FCLK_CTL      (MSM_CLK_CTL_BASE + 0x2154)
+
+#define MSM_PCIE_DEV_BAR_ADDR          PCIBIOS_MIN_MEM
+#define MSM_PCIE_DEV_CFG_ADDR          0x01000000
+
+#define RD 0
+#define WR 1
+
+/* debug mask sys interface */
+static int msm_pcie_debug_mask;
+module_param_named(debug_mask, msm_pcie_debug_mask,
+			    int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+/* resources from device file */
+enum msm_pcie_res {
+	MSM_PCIE_RES_PARF,
+	MSM_PCIE_RES_ELBI,
+	MSM_PCIE_RES_PCIE20,
+	MSM_PCIE_RES_AXI_BAR,
+	MSM_PCIE_RES_AXI_CONF,
+	MSM_PCIE_MAX_RES
+};
+
+/* msm pcie device data */
+static struct msm_pcie_dev_t msm_pcie_dev;
+
+/* regulators */
+static struct msm_pcie_vreg_info_t msm_pcie_vreg_info[MSM_PCIE_MAX_VREG] = {
+	{NULL, "vp_pcie",      1050000, 1050000, 40900},
+	{NULL, "vptx_pcie",    1050000, 1050000, 18200},
+	{NULL, "vdd_pcie_vph",       0,       0,     0},
+	{NULL, "pcie_ext_3p3v",      0,       0,     0}
+};
+
+/* clocks */
+static struct msm_pcie_clk_info_t msm_pcie_clk_info[MSM_PCIE_MAX_CLK] = {
+	{NULL, "bus_clk"},
+	{NULL, "iface_clk"},
+	{NULL, "ref_clk"}
+};
+
+/* resources */
+static struct msm_pcie_res_info_t msm_pcie_res_info[MSM_PCIE_MAX_RES] = {
+	{"parf",     0, 0, 0},
+	{"elbi",     0, 0, 0},
+	{"pcie20",   0, 0, 0},
+	{"axi_bar",  0, 0, 0},
+	{"axi_conf", 0, 0, 0},
+};
+
+int msm_pcie_get_debug_mask(void)
+{
+	return msm_pcie_debug_mask;
+}
+
+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 int msm_pcie_is_link_up(void)
+{
+	return readl_relaxed(msm_pcie_dev.pcie20 + PCIE20_CAP_LINKCTRLSTATUS) &
+				BIT(29);
+}
+
+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 msm_pcie_dev_t *dev = &msm_pcie_dev;
+	void __iomem *config_base;
+
+	/*
+	 * Only buses 0 and 1 are supported. RC port on bus 0 and EP in bus 1.
+	 * For downstream bus (1), make sure link is up
+	 */
+	if ((bus->number > 1) || (devfn != 0)) {
+		PCIE_DBG("invalid %s - bus %d devfn %d\n",
+			 (oper == RD) ? "rd" : "wr", bus->number, devfn);
+		*val = ~0;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	} else if ((bus->number != 0) && !msm_pcie_is_link_up()) {
+		PCIE_DBG("%s fail, link down - bus %d devfn %d\n",
+			 (oper == RD) ? "rd" : "wr", bus->number, devfn);
+		*val = ~0;
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	word_offset = where & ~0x3;
+	byte_offset = where & 0x3;
+	mask = (~0 >> (8 * (4 - size))) << (8 * byte_offset);
+
+	config_base = (bus->number == 0) ? dev->pcie20 : dev->axi_conf;
+	rd_val = readl_relaxed(config_base + word_offset);
+
+	if (oper == RD) {
+		*val = ((rd_val & mask) >> (8 * byte_offset));
+
+		PCIE_DBG("%d:0x%02x + 0x%04x[%d] -> 0x%08x; rd 0x%08x\n",
+			 bus->number, devfn, where, size, *val, rd_val);
+	} 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 */
+
+		PCIE_DBG("%d:0x%02x + 0x%04x[%d] <- 0x%08x;"
+			 " rd 0x%08x val 0x%08x\n", bus->number,
+			 devfn, where, size, wr_val, rd_val, *val);
+	}
+
+	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)
+{
+	return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val);
+}
+
+static struct pci_ops msm_pcie_ops = {
+	.read = msm_pcie_rd_conf,
+	.write = msm_pcie_wr_conf,
+};
+
+static int __devinit msm_pcie_gpio_init(void)
+{
+	int rc, i;
+	struct msm_pcie_gpio_info_t *info;
+
+	for (i = 0; i < MSM_PCIE_MAX_GPIO; i++) {
+		info = &msm_pcie_dev.gpio[i];
+
+		rc = gpio_request(info->num, info->name);
+		if (rc) {
+			pr_err("can't get gpio %s; %d\n", info->name, rc);
+			break;
+		}
+
+		rc = gpio_direction_output(info->num, 0);
+		if (rc) {
+			pr_err("can't set gpio direction %s; %d\n",
+			       info->name, rc);
+			gpio_free(info->num);
+			break;
+		}
+	}
+
+	if (rc)
+		while (i--)
+			gpio_free(msm_pcie_dev.gpio[i].num);
+
+	return rc;
+}
+
+static void msm_pcie_gpio_deinit(void)
+{
+	int i;
+
+	for (i = 0; i < MSM_PCIE_MAX_GPIO; i++)
+		gpio_free(msm_pcie_dev.gpio[i].num);
+}
+
+static int __devinit msm_pcie_vreg_init(struct device *dev)
+{
+	int i, rc = 0;
+	struct regulator *vreg;
+	struct msm_pcie_vreg_info_t *info;
+
+	for (i = 0; i < MSM_PCIE_MAX_VREG; i++) {
+		info = &msm_pcie_dev.vreg[i];
+
+		vreg = regulator_get(dev, info->name);
+		if (!vreg || IS_ERR(vreg)) {
+			rc = (PTR_ERR(vreg)) ? PTR_ERR(vreg) : -ENODEV;
+			pr_err("can't get %s; %d\n", info->name, rc);
+			break;
+		}
+
+		if (info->max_v) {
+			rc = regulator_set_voltage(vreg,
+						   info->min_v, info->max_v);
+			if (rc) {
+				pr_err("can't set voltage %s; %d\n",
+				       info->name, rc);
+				regulator_put(vreg);
+				break;
+			}
+		}
+
+		if (info->opt_mode) {
+			rc = regulator_set_optimum_mode(vreg, info->opt_mode);
+			if (rc < 0) {
+				pr_err("can't set mode %s; %d\n",
+				       info->name, rc);
+				regulator_put(vreg);
+				break;
+			}
+		}
+
+		rc = regulator_enable(vreg);
+		if (rc) {
+			pr_err("can't enable %s, %d\n", info->name, rc);
+			regulator_put(vreg);
+			break;
+		}
+		info->hdl = vreg;
+	}
+
+	if (rc)
+		while (i--) {
+			regulator_disable(msm_pcie_dev.vreg[i].hdl);
+			regulator_put(msm_pcie_dev.vreg[i].hdl);
+			msm_pcie_dev.vreg[i].hdl = NULL;
+		}
+
+	return rc;
+}
+
+static void msm_pcie_vreg_deinit(void)
+{
+	int i;
+
+	for (i = 0; i < MSM_PCIE_MAX_VREG; i++) {
+		regulator_disable(msm_pcie_dev.vreg[i].hdl);
+		regulator_put(msm_pcie_dev.vreg[i].hdl);
+		msm_pcie_dev.vreg[i].hdl = NULL;
+	}
+}
+
+static int __devinit msm_pcie_clk_init(struct device *dev)
+{
+	int i, rc = 0;
+	struct clk *clk_hdl;
+	struct msm_pcie_clk_info_t *info;
+
+	for (i = 0; i < MSM_PCIE_MAX_CLK; i++) {
+		info = &msm_pcie_dev.clk[i];
+
+		clk_hdl = clk_get(dev, info->name);
+		if (!clk_hdl || IS_ERR(clk_hdl)) {
+			rc = (PTR_ERR(clk_hdl)) ? PTR_ERR(clk_hdl) : -ENODEV;
+			pr_err("can't get clk %s; %d\n", info->name, rc);
+			break;
+		}
+		clk_prepare_enable(clk_hdl);
+		info->hdl = clk_hdl;
+	}
+
+	if (rc)
+		while (i--) {
+			clk_disable_unprepare(msm_pcie_dev.clk[i].hdl);
+			clk_put(msm_pcie_dev.clk[i].hdl);
+			msm_pcie_dev.clk[i].hdl = NULL;
+		}
+
+	return rc;
+}
+
+static void msm_pcie_clk_deinit(void)
+{
+	int i;
+
+	for (i = 0; i < MSM_PCIE_MAX_CLK; i++) {
+		clk_disable_unprepare(msm_pcie_dev.clk[i].hdl);
+		clk_put(msm_pcie_dev.clk[i].hdl);
+		msm_pcie_dev.clk[i].hdl = NULL;
+	}
+}
+
+static void __devinit msm_pcie_config_controller(void)
+{
+	struct msm_pcie_dev_t *dev = &msm_pcie_dev;
+	struct msm_pcie_res_info_t *axi_bar = &dev->res[MSM_PCIE_RES_AXI_BAR];
+	struct msm_pcie_res_info_t *axi_conf = &dev->res[MSM_PCIE_RES_AXI_CONF];
+
+	/*
+	 * 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->pcie20 + PCIE20_PLR_IATU_VIEWPORT);
+	/* ensure that hardware locks the region before programming it */
+	wmb();
+
+	writel_relaxed(4, dev->pcie20 + PCIE20_PLR_IATU_CTRL1);
+	writel_relaxed(BIT(31), dev->pcie20 + PCIE20_PLR_IATU_CTRL2);
+	writel_relaxed(axi_conf->start, dev->pcie20 + PCIE20_PLR_IATU_LBAR);
+	writel_relaxed(0, dev->pcie20 + PCIE20_PLR_IATU_UBAR);
+	writel_relaxed(axi_conf->end, dev->pcie20 + PCIE20_PLR_IATU_LAR);
+	writel_relaxed(MSM_PCIE_DEV_CFG_ADDR,
+		       dev->pcie20 + PCIE20_PLR_IATU_LTAR);
+	writel_relaxed(0, dev->pcie20 + 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->pcie20 + PCIE20_PLR_IATU_VIEWPORT);
+	/* ensure that hardware locks the region before programming it */
+	wmb();
+
+	writel_relaxed(0, dev->pcie20 + PCIE20_PLR_IATU_CTRL1);
+	writel_relaxed(BIT(31), dev->pcie20 + PCIE20_PLR_IATU_CTRL2);
+	writel_relaxed(axi_bar->start, dev->pcie20 + PCIE20_PLR_IATU_LBAR);
+	writel_relaxed(0, dev->pcie20 + PCIE20_PLR_IATU_UBAR);
+	writel_relaxed(axi_bar->end, dev->pcie20 + PCIE20_PLR_IATU_LAR);
+	writel_relaxed(MSM_PCIE_DEV_BAR_ADDR,
+		       dev->pcie20 + PCIE20_PLR_IATU_LTAR);
+	writel_relaxed(0, dev->pcie20 + PCIE20_PLR_IATU_UTAR);
+	/* ensure that hardware registers the configuration */
+	wmb();
+}
+
+static int __devinit msm_pcie_get_resources(struct platform_device *pdev)
+{
+	int i, rc = 0;
+	struct resource *res;
+	struct msm_pcie_res_info_t *info;
+	struct msm_pcie_dev_t *dev = &msm_pcie_dev;
+
+	for (i = 0; i < MSM_PCIE_MAX_RES; i++) {
+		info = &dev->res[i];
+
+		res = platform_get_resource_byname(pdev,
+						   IORESOURCE_MEM, info->name);
+		if (!res) {
+			pr_err("can't get %s resource\n", info->name);
+			rc = -ENOMEM;
+			break;
+		}
+
+		info->base = ioremap(res->start, resource_size(res));
+		if (!info->base) {
+			pr_err("can't remap %s\n", info->name);
+			rc = -ENOMEM;
+			break;
+		}
+
+		info->start = res->start;
+		info->end = res->end;
+	}
+
+	if (rc) {
+		while (i--) {
+			iounmap(dev->res[i].base);
+			dev->res[i].base = NULL;
+		}
+	} else {
+		dev->parf = dev->res[MSM_PCIE_RES_PARF].base;
+		dev->elbi = dev->res[MSM_PCIE_RES_ELBI].base;
+		dev->pcie20 = dev->res[MSM_PCIE_RES_PCIE20].base;
+		dev->axi_conf = dev->res[MSM_PCIE_RES_AXI_CONF].base;
+	}
+
+	return rc;
+}
+
+static void __devexit msm_pcie_release_resources(void)
+{
+	int i;
+
+	for (i = 0; i < MSM_PCIE_MAX_RES; i++) {
+		iounmap(msm_pcie_dev.res[i].base);
+		msm_pcie_dev.res[i].base = NULL;
+	}
+
+	msm_pcie_dev.parf = NULL;
+	msm_pcie_dev.elbi = NULL;
+	msm_pcie_dev.pcie20 = NULL;
+	msm_pcie_dev.axi_conf = NULL;
+}
+
+static int __devinit msm_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+	int rc;
+	struct msm_pcie_dev_t *dev = &msm_pcie_dev;
+	uint32_t val;
+
+	PCIE_DBG("bus %d\n", nr);
+	if (nr != 0)
+		return 0;
+
+	/* assert PCIe reset link to keep EP in reset */
+	gpio_set_value_cansleep(dev->gpio[MSM_PCIE_GPIO_RST_N].num,
+				dev->gpio[MSM_PCIE_GPIO_RST_N].on);
+
+	/* enable power */
+	rc = msm_pcie_vreg_init(&dev->pdev->dev);
+	if (rc)
+		goto out;
+
+	/* assert PCIe PARF reset while powering the core */
+	msm_pcie_write_mask(PCIE_RESET, 0, BIT(2));
+
+	/* enable clocks */
+	rc = msm_pcie_clk_init(&dev->pdev->dev);
+	if (rc)
+		goto clk_fail;
+
+	/* enable pcie power; wait 3ms for clock to stabilize */
+	gpio_set_value_cansleep(dev->gpio[MSM_PCIE_GPIO_PWR_EN].num,
+				dev->gpio[MSM_PCIE_GPIO_PWR_EN].on);
+	usleep(3000);
+
+	/*
+	 * de-assert PCIe PARF reset;
+	 * wait 1us before accessing PARF registers
+	 */
+	msm_pcie_write_mask(PCIE_RESET, BIT(2), 0);
+	udelay(1);
+
+	/* enable PCIe clocks and resets */
+	msm_pcie_write_mask(dev->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
+
+	/* PARF programming */
+	writel_relaxed(0x282828, dev->parf + PCIE20_PARF_PCS_DEEMPH);
+	writel_relaxed(0x7F7F, dev->parf + PCIE20_PARF_PCS_SWING);
+	writel_relaxed((4<<24), dev->parf + PCIE20_PARF_CONFIG_BITS);
+	/* ensure that hardware registers the PARF configuration */
+	wmb();
+
+	/* enable reference clock */
+	msm_pcie_write_mask(dev->parf + PCIE20_PARF_PHY_REFCLK, 0, BIT(16));
+
+	/* enable access to PCIe slave port on system fabric */
+	writel_relaxed(BIT(4), PCIE_SFAB_AXI_S5_FCLK_CTL);
+	/* ensure that access is enabled before proceeding */
+	wmb();
+
+	/* de-assert PICe PHY, Core, POR and AXI clk domain resets */
+	msm_pcie_write_mask(PCIE_RESET, BIT(5), 0);
+	msm_pcie_write_mask(PCIE_RESET, BIT(4), 0);
+	msm_pcie_write_mask(PCIE_RESET, BIT(3), 0);
+	msm_pcie_write_mask(PCIE_RESET, BIT(0), 0);
+
+	/* wait 150ms for clock acquisition */
+	udelay(150);
+
+	/* de-assert PCIe reset link to bring EP out of reset */
+	gpio_set_value_cansleep(dev->gpio[MSM_PCIE_GPIO_RST_N].num,
+				!dev->gpio[MSM_PCIE_GPIO_RST_N].on);
+
+	/* enable link training */
+	msm_pcie_write_mask(dev->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0));
+
+	/* poll for link to come up for upto 100ms */
+	rc = readl_poll_timeout(
+			(msm_pcie_dev.pcie20 + PCIE20_CAP_LINKCTRLSTATUS),
+			val, (val & BIT(29)), 10000, 100000);
+	if (rc) {
+		pr_err("link initialization failed\n");
+		goto link_fail;
+	} else
+		pr_info("link initialized\n");
+
+	msm_pcie_config_controller();
+	rc = msm_pcie_irq_init(dev);
+	if (!rc)
+		goto out;
+
+link_fail:
+	msm_pcie_clk_deinit();
+clk_fail:
+	msm_pcie_vreg_deinit();
+out:
+	return (rc) ? 0 : 1;
+}
+
+static struct pci_bus __devinit *msm_pcie_scan_bus(int nr,
+						   struct pci_sys_data *sys)
+{
+	struct pci_bus *bus = NULL;
+
+	PCIE_DBG("bus %d\n", nr);
+	if (nr == 0)
+		bus = pci_scan_bus(sys->busnr, &msm_pcie_ops, sys);
+
+	return bus;
+}
+
+static int __devinit msm_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+	PCIE_DBG("slot %d pin %d\n", slot, pin);
+	return (pin <= 4) ? (PCIE20_INTA + pin - 1) : 0;
+}
+
+static struct hw_pci msm_pci __devinitdata = {
+	.nr_controllers = 1,
+	.swizzle = pci_std_swizzle,
+	.setup = msm_pcie_setup,
+	.scan = msm_pcie_scan_bus,
+	.map_irq = msm_pcie_map_irq,
+};
+
+static int __devinit msm_pcie_probe(struct platform_device *pdev)
+{
+	const struct msm_pcie_platform *pdata;
+	int rc;
+
+	PCIE_DBG("\n");
+
+	msm_pcie_dev.pdev = pdev;
+	pdata = pdev->dev.platform_data;
+	msm_pcie_dev.gpio = pdata->gpio;
+	msm_pcie_dev.vreg = msm_pcie_vreg_info;
+	msm_pcie_dev.clk = msm_pcie_clk_info;
+	msm_pcie_dev.res = msm_pcie_res_info;
+
+	rc = msm_pcie_get_resources(msm_pcie_dev.pdev);
+	if (rc)
+		return rc;
+
+	rc = msm_pcie_gpio_init();
+	if (rc) {
+		msm_pcie_release_resources();
+		return rc;
+	}
+
+	/* kick start ARM PCI configuration framework */
+	pci_common_init(&msm_pci);
+	return 0;
+}
+
+static int __devexit msm_pcie_remove(struct platform_device *pdev)
+{
+	PCIE_DBG("\n");
+
+	msm_pcie_irq_deinit(&msm_pcie_dev);
+	msm_pcie_vreg_deinit();
+	msm_pcie_clk_deinit();
+	msm_pcie_gpio_deinit();
+	msm_pcie_release_resources();
+
+	msm_pcie_dev.pdev = NULL;
+	msm_pcie_dev.vreg = NULL;
+	msm_pcie_dev.clk = NULL;
+	msm_pcie_dev.gpio = NULL;
+	return 0;
+}
+
+static struct platform_driver msm_pcie_driver = {
+	.probe = msm_pcie_probe,
+	.remove = __devexit_p(msm_pcie_remove),
+	.driver = {
+		.name = "msm_pcie",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_pcie_init(void)
+{
+	PCIE_DBG("\n");
+	return platform_driver_register(&msm_pcie_driver);
+}
+subsys_initcall(msm_pcie_init);
+
+/* RC do not represent the right class; set it to PCI_CLASS_BRIDGE_PCI */
+static void __devinit msm_pcie_fixup_header(struct pci_dev *dev)
+{
+	PCIE_DBG("hdr_type %d\n", dev->hdr_type);
+	if (dev->hdr_type == 1)
+		dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8);
+}
+DECLARE_PCI_FIXUP_HEADER(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+			 msm_pcie_fixup_header);
+
+/*
+ * actual physical (BAR) address of the device resources starts from 0x10xxxxxx;
+ * the system axi address for the device resources starts from 0x08xxxxxx;
+ * correct the device resource structure here; address translation unit handles
+ * the required translations
+ */
+static void __devinit msm_pcie_fixup_final(struct pci_dev *dev)
+{
+	int i;
+
+	PCIE_DBG("vendor 0x%x 0x%x\n", dev->vendor, dev->device);
+	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+		if (dev->resource[i].start & 0xFF000000) {
+			dev->resource[i].start &= 0x00FFFFFF;
+			dev->resource[i].start |= 0x08000000;
+			dev->resource[i].end &= 0x00FFFFFF;
+			dev->resource[i].end |= 0x08000000;
+		}
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, msm_pcie_fixup_final);
diff --git a/arch/arm/mach-msm/pcie.h b/arch/arm/mach-msm/pcie.h
new file mode 100644
index 0000000..4866ec5
--- /dev/null
+++ b/arch/arm/mach-msm/pcie.h
@@ -0,0 +1,73 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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.
+ */
+
+#ifndef __ARCH_ARM_MACH_MSM_PCIE_H
+#define __ARCH_ARM_MACH_MSM_PCIE_H
+
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <mach/msm_pcie.h>
+
+#define MSM_PCIE_MAX_VREG 4
+#define MSM_PCIE_MAX_CLK  3
+
+#define PCIE_DBG(x...) do {              \
+	if (msm_pcie_get_debug_mask())   \
+		pr_info(x);              \
+	} while (0)
+
+/* voltage regulator info structrue */
+struct msm_pcie_vreg_info_t {
+	struct regulator  *hdl;
+	char              *name;
+	uint32_t           max_v;
+	uint32_t           min_v;
+	uint32_t           opt_mode;
+};
+
+/* clock info structure */
+struct msm_pcie_clk_info_t {
+	struct clk  *hdl;
+	char        *name;
+};
+
+/* resource info structure */
+struct msm_pcie_res_info_t {
+	char          *name;
+	uint32_t       start;
+	uint32_t       end;
+	void __iomem  *base;
+};
+
+/* msm pcie device structure */
+struct msm_pcie_dev_t {
+	struct platform_device       *pdev;
+
+	struct msm_pcie_vreg_info_t  *vreg;
+	struct msm_pcie_gpio_info_t  *gpio;
+	struct msm_pcie_clk_info_t   *clk;
+	struct msm_pcie_res_info_t   *res;
+
+	void __iomem                 *parf;
+	void __iomem                 *elbi;
+	void __iomem                 *pcie20;
+	void __iomem                 *axi_conf;
+};
+
+extern uint32_t msm_pcie_irq_init(struct msm_pcie_dev_t *dev);
+extern void msm_pcie_irq_deinit(struct msm_pcie_dev_t *dev);
+extern int msm_pcie_get_debug_mask(void);
+
+#endif
diff --git a/arch/arm/mach-msm/pcie_irq.c b/arch/arm/mach-msm/pcie_irq.c
new file mode 100644
index 0000000..df100db
--- /dev/null
+++ b/arch/arm/mach-msm/pcie_irq.c
@@ -0,0 +1,170 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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.
+ */
+
+/*
+ * MSM PCIe controller IRQ driver.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/msi.h>
+#include <linux/pci.h>
+#include <mach/irqs.h>
+
+#include "pcie.h"
+
+/* Any address will do here, as it won't be dereferenced */
+#define MSM_PCIE_MSI_PHY 0xa0000000
+
+#define PCIE20_MSI_CTRL_ADDR            (0x820)
+#define PCIE20_MSI_CTRL_UPPER_ADDR      (0x824)
+#define PCIE20_MSI_CTRL_INTR_EN         (0x828)
+#define PCIE20_MSI_CTRL_INTR_MASK       (0x82C)
+#define PCIE20_MSI_CTRL_INTR_STATUS     (0x830)
+
+#define PCIE20_MSI_CTRL_MAX 8
+
+static DECLARE_BITMAP(msi_irq_in_use, NR_PCIE_MSI_IRQS);
+
+irqreturn_t handle_msi_irq(int irq, void *data)
+{
+	int i, j;
+	unsigned long val;
+	struct msm_pcie_dev_t *dev = data;
+	void __iomem *ctrl_status;
+
+	/* check for set bits, clear it by setting that bit
+	   and trigger corresponding irq */
+	for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++) {
+		ctrl_status = dev->pcie20 +
+				PCIE20_MSI_CTRL_INTR_STATUS + (i * 12);
+
+		val = readl_relaxed(ctrl_status);
+		while (val) {
+			j = find_first_bit(&val, 32);
+			writel_relaxed(BIT(j), ctrl_status);
+			/* ensure that interrupt is cleared (acked) */
+			wmb();
+
+			generic_handle_irq(MSM_PCIE_MSI_INT(j + (32 * i)));
+			val = readl_relaxed(ctrl_status);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+uint32_t __devinit msm_pcie_irq_init(struct msm_pcie_dev_t *dev)
+{
+	int i, rc;
+
+	PCIE_DBG("\n");
+
+	/* program MSI controller and enable all interrupts */
+	writel_relaxed(MSM_PCIE_MSI_PHY, dev->pcie20 + PCIE20_MSI_CTRL_ADDR);
+	writel_relaxed(0, dev->pcie20 + PCIE20_MSI_CTRL_UPPER_ADDR);
+
+	for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++)
+		writel_relaxed(~0, dev->pcie20 +
+			       PCIE20_MSI_CTRL_INTR_EN + (i * 12));
+
+	/* ensure that hardware is configured before proceeding */
+	wmb();
+
+	/* register handler for physical MSI interrupt line */
+	rc = request_irq(PCIE20_INT_MSI, handle_msi_irq, IRQF_TRIGGER_RISING,
+			 "msm_pcie_msi", dev);
+	if (rc)
+		pr_err("Unable to allocate msi interrupt\n");
+
+	return rc;
+}
+
+void msm_pcie_irq_deinit(struct msm_pcie_dev_t *dev)
+{
+	free_irq(PCIE20_INT_MSI, dev);
+}
+
+void msm_pcie_destroy_irq(unsigned int irq)
+{
+	int pos = irq - MSM_PCIE_MSI_INT(0);
+
+	dynamic_irq_cleanup(irq);
+	clear_bit(pos, msi_irq_in_use);
+}
+
+/* hookup to linux pci msi framework */
+void arch_teardown_msi_irq(unsigned int irq)
+{
+	PCIE_DBG("irq %d deallocated\n", irq);
+	msm_pcie_destroy_irq(irq);
+}
+
+static void msm_pcie_msi_nop(struct irq_data *d)
+{
+	return;
+}
+
+static struct irq_chip pcie_msi_chip = {
+	.name = "msm-pcie-msi",
+	.irq_ack = msm_pcie_msi_nop,
+	.irq_enable = unmask_msi_irq,
+	.irq_disable = mask_msi_irq,
+	.irq_mask = mask_msi_irq,
+	.irq_unmask = unmask_msi_irq,
+};
+
+static int msm_pcie_create_irq(void)
+{
+	int irq, pos;
+
+again:
+	pos = find_first_zero_bit(msi_irq_in_use, NR_PCIE_MSI_IRQS);
+	irq = MSM_PCIE_MSI_INT(pos);
+	if (irq >= (MSM_PCIE_MSI_INT(0) + NR_PCIE_MSI_IRQS))
+		return -ENOSPC;
+
+	if (test_and_set_bit(pos, msi_irq_in_use))
+		goto again;
+
+	dynamic_irq_init(irq);
+	return irq;
+}
+
+/* hookup to linux pci msi framework */
+int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
+{
+	int irq;
+	struct msi_msg msg;
+
+	irq = msm_pcie_create_irq();
+	if (irq < 0)
+		return irq;
+
+	PCIE_DBG("irq %d allocated\n", irq);
+
+	irq_set_msi_desc(irq, desc);
+
+	/* write msi vector and data */
+	msg.address_hi = 0;
+	msg.address_lo = MSM_PCIE_MSI_PHY;
+	msg.data = irq - MSM_PCIE_MSI_INT(0);
+	write_msi_msg(irq, &msg);
+
+	irq_set_chip_and_handler(irq, &pcie_msi_chip, handle_simple_irq);
+	set_irq_flags(irq, IRQF_VALID);
+	return 0;
+}