Merge "ipq806x: Add PCIE entries for AP148 board"
diff --git a/arch/arm/dts/ipq806x-ap148.dts b/arch/arm/dts/ipq806x-ap148.dts
index 71e950e..1f282e0 100644
--- a/arch/arm/dts/ipq806x-ap148.dts
+++ b/arch/arm/dts/ipq806x-ap148.dts
@@ -25,6 +25,8 @@
 		nand = "/nand@1A600000";
 		gmac_gpio = "/gmac1_gpio";
 		i2c0 = "/i2c@16380000";
+		pci0 = "/pci@1b500000";
+		pci1 = "/pci@1b700000";
 	};
 
 	serial@16340000 {
@@ -65,4 +67,29 @@
 			phy_name = "IPQ MDIO2";
 		};
 	};
+
+	pci@1b500000 {
+		pci_gpio {
+			gpio3 {
+				gpio = <3>;
+				func = <1>;
+				pull = <GPIO_PULL_UP>;
+				drvstr = <GPIO_12MA>;
+				oe = <GPIO_OE_ENABLE>;
+			};
+		};
+	};
+
+	pci@1b700000 {
+		pci_gpio {
+			gpio48 {
+				gpio = <48>;
+				func = <1>;
+				pull = <GPIO_PULL_UP>;
+				drvstr = <GPIO_12MA>;
+				oe = <GPIO_OE_ENABLE>;
+			};
+		};
+	};
+
 };
diff --git a/arch/arm/dts/ipq806x-soc.dtsi b/arch/arm/dts/ipq806x-soc.dtsi
index e40ab1b..cb5ac7b 100644
--- a/arch/arm/dts/ipq806x-soc.dtsi
+++ b/arch/arm/dts/ipq806x-soc.dtsi
@@ -408,6 +408,36 @@
 		clock-frequency  = <24000>;
 	};
 
+	pci@1b500000 {
+		compatible = "qcom,ipq806x-pcie";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0x1b500000 0xf1d
+			0x1b600000 0x2000
+			0x1b502000 0xa8
+			0x08000000 0x08000000
+			0x0ff00000 0x100000
+			0x009022DC 0x40>;
+		reg-names = "pci_dbi", "parf", "elbi", "axi_bars",
+				"axi_conf", "pci_rst";
+		perst_gpio = <3>;
+	};
+
+	pci@1b700000 {
+		compatible = "qcom,ipq806x-pcie";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0x1b700000 0xf1d
+			0x1b800000 0x2000
+			0x1b702000 0xa8
+			0x2E000000 0x04000000
+			0x31f00000 0x100000
+			0x00903A9C 0x40>;
+		reg-names = "pci_dbi", "parf", "elbi", "axi_bars",
+				"axi_conf", "pci_rst";
+		perst_gpio = <48>;
+	};
+
 	spi {
 		spi0 {
 			mosi_miso_clk {
diff --git a/arch/arm/include/asm/arch-ipq806x/clk.h b/arch/arm/include/asm/arch-ipq806x/clk.h
index 3881fef..35e5ca4 100644
--- a/arch/arm/include/asm/arch-ipq806x/clk.h
+++ b/arch/arm/include/asm/arch-ipq806x/clk.h
@@ -73,11 +73,49 @@
 #define emmc_clk_ns_mask (BM(BIT_POS_23, BIT_POS_16) | BM(BIT_POS_6, BIT_POS_0))
 #define emmc_en_mask BIT(11)
 
+#define PCIE_0_ACLK_CTL                         0x9022C0
+#define PCIE_1_ACLK_CTL                         0x903a80
+#define PCIE_2_ACLK_CTL                         0x903ac0
+#define PCIE_0_PCLK_CTL                         0x9022D0
+#define PCIE_1_PCLK_CTL                         0x903A90
+#define PCIE_2_PCLK_CTL                         0x903AD0
+#define PCIE_0_HCLK_CTL                         0x9022CC
+#define PCIE_1_HCLK_CTL                         0x903A8C
+#define PCIE_2_HCLK_CTL                         0x903ACC
+#define PCIE_0_AUX_CLK_CTL                      0x9022C8
+#define PCIE_1_AUX_CLK_CTL                      0x903A88
+#define PCIE_2_AUX_CLK_CTL                      0x903AC8
+#define PCIE_0_ALT_REF_CLK_NS                   0x903860
+#define PCIE_1_ALT_REF_CLK_NS                   0x903AA0
+#define PCIE_2_ALT_REF_CLK_NS                   0x903AE0
+#define PCIE_0_ALT_REF_CLK_ACR                  0x901344
+#define PCIE_1_ALT_REF_CLK_ACR                  0x901354
+#define PCIE_2_ALT_REF_CLK_ACR                  0x90135C
+#define PCIE_0_ACLK_FS                          0x9022C4
+#define PCIE_0_PCLK_FS                          0x9022D4
+#define PCIE_1_ACLK_FS                          0x903A84
+#define PCIE_1_PCLK_FS                          0x903A94
+#define PCIE_2_ACLK_FS                          0x903AC4
+#define PCIE_2_PCLK_FS                          0x903AD4
+#define PCIE20_0_PARF_PHY_REFCLK                0x1B60004C
+#define PCIE20_1_PARF_PHY_REFCLK                0x1B80004C
+#define PCIE20_2_PARF_PHY_REFCLK                0x1BA0004C
+
+typedef struct {
+        unsigned int aclk_ctl;
+        unsigned int pclk_ctl;
+        unsigned int hclk_ctl;
+        unsigned int aux_clk_ctl;
+        unsigned int alt_ref_clk_ns;
+        unsigned int alt_ref_clk_acr;
+        unsigned int aclk_fs;
+        unsigned int pclk_fs;
+        unsigned int parf_phy_refclk;
+} pci_clk_offset_t;
 
 void i2c_clock_config(void);
 
 /* Uart specific clock settings */
-
 void uart_pll_vote_clk_enable(void);
 void uart_clock_config(unsigned int gsbi_port, unsigned int m, unsigned int n,
 		unsigned int d);
diff --git a/board/qca/arm/ipq40xx/ipq40xx.h b/board/qca/arm/ipq40xx/ipq40xx.h
index 659a634..c7249d2 100644
--- a/board/qca/arm/ipq40xx/ipq40xx.h
+++ b/board/qca/arm/ipq40xx/ipq40xx.h
@@ -73,6 +73,7 @@
 #endif
 
 void board_pci_init(int id);
+__weak void board_pcie_clock_init(int id) {}
 __weak void aquantia_phy_reset(void) {}
 
 /* Board specific parameters */
diff --git a/board/qca/arm/ipq806x/ipq806x.c b/board/qca/arm/ipq806x/ipq806x.c
index 4535ee5..94b94e8 100644
--- a/board/qca/arm/ipq806x/ipq806x.c
+++ b/board/qca/arm/ipq806x/ipq806x.c
@@ -72,6 +72,48 @@
 
 extern int ipq_spi_init(u16);
 
+pci_clk_offset_t pcie_0_clk = {
+	.aclk_ctl = PCIE_0_ACLK_CTL,
+	.pclk_ctl = PCIE_0_PCLK_CTL,
+	.hclk_ctl = PCIE_0_HCLK_CTL,
+	.aux_clk_ctl = PCIE_0_AUX_CLK_CTL,
+	.alt_ref_clk_ns = PCIE_0_ALT_REF_CLK_NS,
+	.alt_ref_clk_acr = PCIE_0_ALT_REF_CLK_ACR,
+	.aclk_fs = PCIE_0_ACLK_FS,
+	.pclk_fs = PCIE_0_PCLK_FS,
+	.parf_phy_refclk = PCIE20_0_PARF_PHY_REFCLK
+};
+
+pci_clk_offset_t pcie_1_clk = {
+	.aclk_ctl = PCIE_1_ACLK_CTL,
+	.pclk_ctl = PCIE_1_PCLK_CTL,
+	.hclk_ctl = PCIE_1_HCLK_CTL,
+	.aux_clk_ctl = PCIE_1_AUX_CLK_CTL,
+	.alt_ref_clk_ns = PCIE_1_ALT_REF_CLK_NS,
+	.alt_ref_clk_acr = PCIE_1_ALT_REF_CLK_ACR,
+	.aclk_fs = PCIE_1_ACLK_FS,
+	.pclk_fs = PCIE_1_PCLK_FS,
+	.parf_phy_refclk = PCIE20_1_PARF_PHY_REFCLK
+};
+
+pci_clk_offset_t pcie_2_clk = {
+	.aclk_ctl = PCIE_2_ACLK_CTL,
+	.pclk_ctl = PCIE_2_PCLK_CTL,
+	.hclk_ctl = PCIE_2_HCLK_CTL,
+	.aux_clk_ctl = PCIE_2_AUX_CLK_CTL,
+	.alt_ref_clk_ns = PCIE_2_ALT_REF_CLK_NS,
+	.alt_ref_clk_acr = PCIE_2_ALT_REF_CLK_ACR,
+	.aclk_fs = PCIE_2_ACLK_FS,
+	.pclk_fs = PCIE_2_PCLK_FS,
+	.parf_phy_refclk = PCIE20_2_PARF_PHY_REFCLK
+};
+
+enum pcie_id {
+	PCIE_0,
+	PCIE_1,
+	PCIE_2,
+};
+
 unsigned long timer_read_counter(void)
 {
 	return 0;
@@ -315,6 +357,38 @@
 			GSBI_CTRL_REG(gsbi_base));
 
 }
+void board_pcie_clock_init(int id)
+{
+	switch(id) {
+		case PCIE_0:
+			pcie_clock_config(&pcie_0_clk);
+			break;
+		case PCIE_1:
+			pcie_clock_config(&pcie_1_clk);
+			break;
+		case PCIE_2:
+			pcie_clock_config(&pcie_2_clk);
+			break;
+	}
+}
+
+void board_pci_init(int id)
+{
+	int node, gpio_node;
+	char name[16];
+
+	sprintf(name, "pci%d", id);
+	node = fdt_path_offset(gd->fdt_blob, name);
+	if (node < 0) {
+		printf("Could not find PCI in device tree\n");
+		return;
+	}
+	gpio_node = fdt_subnode_offset(gd->fdt_blob, node, "pci_gpio");
+	if (gpio_node >= 0)
+		qca_gpio_init(gpio_node);
+
+	return;
+}
 
 void ipq_fdt_fixup_socinfo(void *blob)
 {
diff --git a/board/qca/arm/ipq806x/ipq806x.h b/board/qca/arm/ipq806x/ipq806x.h
index 4b01a3f..c9a26fe 100644
--- a/board/qca/arm/ipq806x/ipq806x.h
+++ b/board/qca/arm/ipq806x/ipq806x.h
@@ -117,4 +117,6 @@
 extern const add_node_t add_node[];
 void reset_crashdump(void);
 void ipq_fdt_fixup_socinfo(void *blob);
+void board_pci_init(int id);
+void board_pcie_clock_init(int id);
 #endif /* _IPQ806X_H_ */
diff --git a/drivers/clk/ipq806x_clk.c b/drivers/clk/ipq806x_clk.c
index e592bd8..ce9ca24 100644
--- a/drivers/clk/ipq806x_clk.c
+++ b/drivers/clk/ipq806x_clk.c
@@ -435,8 +435,7 @@
 }
 #endif
 
-#ifdef CONFIG_IPQ806X_PCI
-void pcie_clock_shutdown(clk_offset_t *pci_clk)
+void pcie_clock_shutdown(pci_clk_offset_t *pci_clk)
 {
 	/* PCIE_ALT_REF_CLK_NS */
 	writel(0x0, pci_clk->alt_ref_clk_ns);
@@ -457,7 +456,7 @@
 	writel(0x0, pci_clk->aux_clk_ctl);
 }
 
-void pcie_clock_config(clk_offset_t *pci_clk)
+void pcie_clock_config(pci_clk_offset_t *pci_clk)
 {
 	/* PCIE_ALT_REF_CLK_NS */
 	writel(0x0A59, pci_clk->alt_ref_clk_ns);
@@ -481,7 +480,6 @@
 	/* PCIE_AUX_CLK_CTL */
 	writel(0x10, pci_clk->aux_clk_ctl);
 }
-#endif /* CONFIG_IPQ806X_PCI */
 
 #ifdef CONFIG_QCA_MMC
 void emmc_pll_vote_clk_enable(void)
diff --git a/drivers/pci/pci_ipq.c b/drivers/pci/pci_ipq.c
index 3a56dbb..464d4b7 100644
--- a/drivers/pci/pci_ipq.c
+++ b/drivers/pci/pci_ipq.c
@@ -159,6 +159,29 @@
 #define PCIE_USB3_PCS_SW_RESET                          0x800
 #define PCIE_USB3_PCS_START_CONTROL                     0x808
 
+#define PCIE20_PARF_PHY_CTRL		0x40
+#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK       (0x1f << 16)
+#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x)         (x << 16)
+
+#define PCIE20_PARF_PHY_REFCLK		0x4C
+#define REF_SSP_EN				BIT(16)
+#define REF_USE_PAD				BIT(12)
+
+#define PCIE20_PARF_PCS_DEEMPH		0x34
+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN1(x) (x << 16)
+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) (x << 8)
+#define PCIE20_PARF_PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) (x << 0)
+
+#define PCIE20_PARF_PCS_SWING		0x38
+#define PCIE20_PARF_PCS_SWING_TX_SWING_FULL(x)	(x << 8)
+#define PCIE20_PARF_PCS_SWING_TX_SWING_LOW(x)	(x << 0)
+
+#define PCIE20_PARF_CONFIG_BITS		0x50
+
+#define PCIE_SFAB_AXI_S5_FCLK_CTL 	0x00902154
+
+#define PCIE20_ELBI_SYS_CTRL		0x04
+
 static unsigned int local_buses[] = { 0, 0 };
 struct pci_controller pci_hose[PCI_MAX_DEVICES];
 static int phy_initialised;
@@ -196,6 +219,24 @@
 	int version;
 };
 
+static void ipq_pcie_write_mask(uint32_t addr,
+				uint32_t clear_mask, uint32_t set_mask)
+{
+	uint32_t val;
+
+	val = (readl(addr) & ~clear_mask) | set_mask;
+	writel(val, addr);
+}
+
+static void ipq_pcie_parf_reset(uint32_t addr, int domain, int assert)
+
+{
+	if (assert)
+		ipq_pcie_write_mask(addr, 0, domain);
+	else
+		ipq_pcie_write_mask(addr, domain, 0);
+}
+
 void ipq_pcie_config_cfgtype(uint32_t phyaddr)
 {
 	uint32_t bdf, cfgtype;
@@ -455,18 +496,88 @@
 	}
 	ipq_pcie_config_controller(pcie);
 }
+
+void pcie_v0_linkup(struct ipq_pcie *pcie, int id)
+{
+	int j;
+	uint32_t val;
+	/* assert PCIe PARF reset while powering the core */
+	ipq_pcie_parf_reset(pcie->pci_rst.start, BIT(6), 0);
+
+	ipq_pcie_parf_reset(pcie->pci_rst.start, BIT(2), 1);
+	board_pcie_clock_init(id);
+	/*
+	 * de-assert PCIe PARF reset;
+	 * wait 1us before accessing PARF registers
+	 */
+	ipq_pcie_parf_reset(pcie->pci_rst.start, BIT(2), 0);
+	udelay(1);
+
+	/* enable PCIe clocks and resets */
+	val = (readl(pcie->parf.start + PCIE20_PARF_PHY_CTRL) & ~BIT(0));
+	writel(val, pcie->parf.start + PCIE20_PARF_PHY_CTRL);
+
+	ipq_pcie_write_mask(pcie->parf.start + PCIE20_PARF_PHY_CTRL,
+				PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK,
+				PHY_CTRL_PHY_TX0_TERM_OFFSET(0));
+
+	/* PARF programming */
+	writel(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),
+			pcie->parf.start + PCIE20_PARF_PCS_DEEMPH);
+
+	writel(PCIE20_PARF_PCS_SWING_TX_SWING_FULL(0x78) |
+			PCIE20_PARF_PCS_SWING_TX_SWING_LOW(0x78),
+			pcie->parf.start + PCIE20_PARF_PCS_SWING);
+
+	writel((4<<24), pcie->parf.start + PCIE20_PARF_CONFIG_BITS);
+
+	ipq_pcie_write_mask(pcie->parf.start + PCIE20_PARF_PHY_REFCLK,
+				REF_USE_PAD, REF_SSP_EN);
+
+	/* enable access to PCIe slave port on system fabric */
+	if (id == 0) {
+		writel(BIT(4), PCIE_SFAB_AXI_S5_FCLK_CTL);
+	}
+
+	udelay(1);
+	/* de-assert PICe PHY, Core, POR and AXI clk domain resets */
+	ipq_pcie_parf_reset(pcie->pci_rst.start, BIT(5), 0);
+	ipq_pcie_parf_reset(pcie->pci_rst.start, BIT(4), 0);
+	ipq_pcie_parf_reset(pcie->pci_rst.start, BIT(3), 0);
+	ipq_pcie_parf_reset(pcie->pci_rst.start, BIT(0), 0);
+
+	/* enable link training */
+	ipq_pcie_write_mask( pcie->elbi.start + PCIE20_ELBI_SYS_CTRL, 0,
+			BIT(0));
+	udelay(500);
+
+	for (j = 0; j < 10; j++) {
+		val = readl(pcie->pci_dbi.start +
+				PCIE_0_TYPE0_LINK_CONTROL_LINK_STATUS_REG_1);
+		if (val & BIT(29)) {
+			printf("PCI%d Link Intialized\n", id);
+			pcie->linkup = 1;
+			break;
+		}
+		udelay(10000);
+	}
+	ipq_pcie_config_controller(pcie);
+
+}
+
 static int ipq_pcie_parse_dt(const void *fdt, int id,
 			       struct ipq_pcie *pcie)
 {
 	int err, rst_gpio, node;
+	char name[16];
 
-	if (id == 0) {
-		 node = fdt_path_offset(fdt, "pci0");
-	} else if (id == 1) {
-		 node = fdt_path_offset(fdt, "pci1");
-	} else {
-		printf("PCI is not defined in the device tree\n");
-		return -1;
+	sprintf(name, "pci%d", id);
+	node = fdt_path_offset(fdt, name);
+	if (node < 0) {
+		printf("PCI%d is not defined in the device tree\n", id);
+		return node;
 	}
 
 	err = fdt_get_named_resource(fdt, node, "reg", "reg-names", "pci_dbi",
@@ -703,6 +814,9 @@
 
 	board_pci_init(id);
 	switch(pcie->version) {
+		case PCIE_V0:
+			pcie_v0_linkup(pcie, id);
+			break;
 		case PCIE_V1:
 			pci_controller_init_v1(pcie);
 			pcie_linkup(pcie);
diff --git a/include/configs/ipq806x.h b/include/configs/ipq806x.h
index cd7bc54..ba3fac7 100644
--- a/include/configs/ipq806x.h
+++ b/include/configs/ipq806x.h
@@ -142,14 +142,15 @@
 /*
  * PCI Configs
  */
-#undef CONFIG_IPQ806X_PCI
-
-#ifdef CONFIG_IPQ806X_PCI
+#define CONFIG_PCI_IPQ
+#define PCI_MAX_DEVICES	3
+#ifdef CONFIG_PCI_IPQ
 #define CONFIG_PCI
 #define CONFIG_CMD_PCI
 #define CONFIG_PCI_SCAN_SHOW
 #endif
 
+
 /*
  * MMC Configs
  */