pcie: add pcie driver support for G12A
authorZan Peng <zan.peng@amlogic.com>
Fri, 2 Feb 2018 04:42:02 +0000 (12:42 +0800)
committerYixun Lan <yixun.lan@amlogic.com>
Fri, 2 Mar 2018 07:07:53 +0000 (15:07 +0800)
PD#156734: pcie: add pcie driver support for G12A

Change-Id: I497e17ac212448934416c8af73206cd88766e406
Signed-off-by: Zan Peng <zan.peng@amlogic.com>
MAINTAINERS
drivers/amlogic/pci/Makefile
drivers/amlogic/pci/pcie-amlogic-v2.c [new file with mode: 0644]

index 0e6bdc3..a6a469d 100644 (file)
@@ -13942,6 +13942,7 @@ F:      arch/arm64/boot/dts/amlogic/axg_s420.dts
 AMLOGIC PCIE DRIVER SUPPORT
 M: Yue Wang <yue.wang@amlogic.com>
 F: drivers/amlogic/pci/pcie-amlogic.c
+F: drivers/amlogic/pci/pcie-amlogic-v2.c
 F: drivers/amlogic/pci/pcie-amlogic.h
 F: drivers/amlogic/pci/Kconfig
 F: drivers/amlogic/pci/Makefile
index 22f19e6..dcaa0b6 100644 (file)
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_AMLOGIC_PCIE)     += pcie-amlogic.o
+obj-$(CONFIG_AMLOGIC_PCIE)     += pcie-amlogic-v2.o
diff --git a/drivers/amlogic/pci/pcie-amlogic-v2.c b/drivers/amlogic/pci/pcie-amlogic-v2.c
new file mode 100644 (file)
index 0000000..4ba2bb6
--- /dev/null
@@ -0,0 +1,1121 @@
+/*
+ * drivers/amlogic/pci/pcie-amlogic-v2.c
+ *
+ * Copyright (C) 2017 Amlogic, Inc. 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include "../drivers/pci/host/pcie-designware.h"
+#include "pcie-amlogic.h"
+
+#include <dt-bindings/clock/amlogic,axg-clkc.h>
+#include <linux/clk-provider.h>
+#include "../clk/clkc.h"
+
+
+struct amlogic_pcie {
+       struct pcie_port        pp;
+       struct pcie_phy         *phy;
+       void __iomem            *elbi_base;     /* DT 0th resource */
+       void __iomem            *cfg_base;      /* DT 2nd resource */
+       int                     reset_gpio;
+       struct clk              *clk;
+       struct clk              *phy_clk;
+       struct clk              *bus_clk;
+       int                     pcie_num;
+       int                     gpio_type;
+       u32                     port_num;
+       u32                     pm_enable;
+       u32                     device_attch;
+       u32                     rst_mod;
+};
+
+#define to_amlogic_pcie(x)     container_of(x, struct amlogic_pcie, pp)
+struct pcie_phy_aml_regs pcie_aml_regs_v2;
+struct pcie_phy                *g_pcie_phy_v2;
+
+static void amlogic_elb_writel(struct amlogic_pcie *amlogic_pcie, u32 val,
+                                                               u32 reg)
+{
+       writel(val, amlogic_pcie->elbi_base + reg);
+}
+
+static u32 amlogic_elb_readl(struct amlogic_pcie *amlogic_pcie, u32 reg)
+{
+       return readl(amlogic_pcie->elbi_base + reg);
+}
+
+static void amlogic_cfg_writel(struct amlogic_pcie *amlogic_pcie, u32 val,
+                                                               u32 reg)
+{
+       writel(val, amlogic_pcie->cfg_base + reg);
+}
+
+static u32 amlogic_cfg_readl(struct amlogic_pcie *amlogic_pcie, u32 reg)
+{
+       return readl(amlogic_pcie->cfg_base + reg);
+}
+
+static void cr_bus_addr(unsigned int addr)
+{
+       union phy_r4 phy_r4 = {.d32 = 0};
+       union phy_r5 phy_r5 = {.d32 = 0};
+       unsigned long timeout_jiffies;
+
+       phy_r4.b.phy_cr_data_in = addr;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+
+       phy_r4.b.phy_cr_cap_addr = 0;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       phy_r4.b.phy_cr_cap_addr = 1;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       timeout_jiffies = jiffies +
+                       msecs_to_jiffies(1000);
+       do {
+               phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]);
+       } while (phy_r5.b.phy_cr_ack == 0 &&
+               time_is_after_jiffies(timeout_jiffies));
+
+       phy_r4.b.phy_cr_cap_addr = 0;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       timeout_jiffies = jiffies +
+                       msecs_to_jiffies(1000);
+       do {
+               phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]);
+       } while (phy_r5.b.phy_cr_ack == 1 &&
+               time_is_after_jiffies(timeout_jiffies));
+}
+
+static int cr_bus_read(unsigned int addr)
+{
+       int data;
+       union phy_r4 phy_r4 = {.d32 = 0};
+       union phy_r5 phy_r5 = {.d32 = 0};
+       unsigned long timeout_jiffies;
+
+       cr_bus_addr(addr);
+
+       phy_r4.b.phy_cr_read = 0;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       phy_r4.b.phy_cr_read = 1;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+
+       timeout_jiffies = jiffies +
+                       msecs_to_jiffies(1000);
+       do {
+               phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]);
+       } while (phy_r5.b.phy_cr_ack == 0 &&
+               time_is_after_jiffies(timeout_jiffies));
+
+       data = phy_r5.b.phy_cr_data_out;
+
+       phy_r4.b.phy_cr_read = 0;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       timeout_jiffies = jiffies +
+                       msecs_to_jiffies(1000);
+       do {
+               phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]);
+       } while (phy_r5.b.phy_cr_ack == 1 &&
+               time_is_after_jiffies(timeout_jiffies));
+
+       return data;
+}
+
+static void cr_bus_write(unsigned int addr, unsigned int data)
+{
+       union phy_r4 phy_r4 = {.d32 = 0};
+       union phy_r5 phy_r5 = {.d32 = 0};
+       unsigned long timeout_jiffies;
+
+       cr_bus_addr(addr);
+
+       phy_r4.b.phy_cr_data_in = data;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+
+       phy_r4.b.phy_cr_cap_data = 0;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       phy_r4.b.phy_cr_cap_data = 1;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       timeout_jiffies = jiffies +
+               msecs_to_jiffies(1000);
+       do {
+               phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]);
+       } while (phy_r5.b.phy_cr_ack == 0 &&
+               time_is_after_jiffies(timeout_jiffies));
+
+       phy_r4.b.phy_cr_cap_data = 0;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       timeout_jiffies = jiffies +
+               msecs_to_jiffies(1000);
+       do {
+               phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]);
+       } while (phy_r5.b.phy_cr_ack == 1 &&
+               time_is_after_jiffies(timeout_jiffies));
+
+       phy_r4.b.phy_cr_write = 0;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       phy_r4.b.phy_cr_write = 1;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       timeout_jiffies = jiffies +
+               msecs_to_jiffies(1000);
+       do {
+               phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]);
+       } while (phy_r5.b.phy_cr_ack == 0 &&
+               time_is_after_jiffies(timeout_jiffies));
+
+       phy_r4.b.phy_cr_write = 0;
+       writel(phy_r4.d32, pcie_aml_regs_v2.pcie_phy_r[4]);
+       timeout_jiffies = jiffies +
+               msecs_to_jiffies(1000);
+       do {
+               phy_r5.d32 = readl(pcie_aml_regs_v2.pcie_phy_r[5]);
+       } while (phy_r5.b.phy_cr_ack == 1 &&
+               time_is_after_jiffies(timeout_jiffies));
+}
+
+static void amlogic_phy_cr_writel(u32 val, u32 reg)
+{
+       cr_bus_write(reg, val);
+}
+
+static u32 amlogic_phy_cr_readl(u32 reg)
+{
+       return cr_bus_read(reg);
+}
+
+static ssize_t show_pcie_cr_read(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       u32 status = 0;
+
+       return sprintf(buf, "%d\n", status);
+}
+
+
+static ssize_t store_pcie_cr_read(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       u32 reg;
+       u32 val;
+
+       if (kstrtouint(buf, 0, &reg) != 0)
+               return -EINVAL;
+
+       val = amlogic_phy_cr_readl(reg);
+       dev_info(dev, "reg 0x%x value is 0x%x\n", reg, val);
+
+       return count;
+}
+DEVICE_ATTR(phyread_v2, 0644, show_pcie_cr_read, store_pcie_cr_read);
+
+static ssize_t show_pcie_cr_write(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       u32 status = 0;
+
+       return sprintf(buf, "%d\n", status);
+}
+
+static ssize_t store_pcie_cr_write(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       unsigned int reg, val;
+       int ret = 0;
+       char *pstr;
+       char *pend;
+       char *strbuf;
+
+       if (count > 40)
+               return -EINVAL;
+       strbuf = kzalloc(count + 2, GFP_KERNEL);
+       if (!strbuf)
+               return -ENOMEM;
+
+       strncpy(strbuf, buf, count);
+       pend = strbuf + count;
+       pstr = strchr(strbuf, ' ');
+       if (!pstr) {
+               ret = -EINVAL;
+               goto cr_end;
+       }
+       *pstr = 0;
+       ret = kstrtouint(strbuf, 0, &reg);
+       pstr++;
+       if (pstr >= pend) {
+               ret = -EINVAL;
+               goto cr_end;
+       }
+       ret = kstrtouint(pstr, 0, &val);
+       if (ret) {
+               ret = -EINVAL;
+               goto cr_end;
+       }
+       amlogic_phy_cr_writel(val, reg);
+       ret = count;
+cr_end:
+       kfree(strbuf);
+       return ret;
+}
+DEVICE_ATTR(phywrite_v2, 0644, show_pcie_cr_write, store_pcie_cr_write);
+
+static void amlogic_pcie_assert_reset(struct amlogic_pcie *amlogic_pcie)
+{
+       struct pcie_port *pp = &amlogic_pcie->pp;
+       struct device *dev = pp->dev;
+
+       if (amlogic_pcie->gpio_type == 0) {
+               dev_info(amlogic_pcie->pp.dev,
+                               "gpio multiplex, don't reset!\n");
+       } else if (amlogic_pcie->gpio_type == 1) {
+               dev_info(amlogic_pcie->pp.dev, "pad gpio\n");
+               if (amlogic_pcie->reset_gpio >= 0)
+                       devm_gpio_request(dev,
+                               amlogic_pcie->reset_gpio, "RESET");
+
+               if (gpio_is_valid(amlogic_pcie->reset_gpio)) {
+                       dev_info(amlogic_pcie->pp.dev,
+                               "GPIO pad: amlogic_pcie_assert_reset\n");
+                       gpio_direction_output(
+                               amlogic_pcie->reset_gpio, 0);
+                       usleep_range(5000, 6000);
+                       gpio_direction_input(
+                               amlogic_pcie->reset_gpio);
+               }
+       } else {
+               dev_info(amlogic_pcie->pp.dev, "normal gpio\n");
+               if (amlogic_pcie->reset_gpio >= 0)
+                       devm_gpio_request(dev,
+                               amlogic_pcie->reset_gpio, "RESET");
+
+               if (gpio_is_valid(amlogic_pcie->reset_gpio)) {
+                       dev_info(amlogic_pcie->pp.dev,
+                               "GPIO normal: amlogic_pcie_assert_reset\n");
+                       gpio_set_value_cansleep(
+                               amlogic_pcie->reset_gpio, 0);
+                       usleep_range(5000, 6000);
+                       gpio_set_value_cansleep(
+                               amlogic_pcie->reset_gpio, 1);
+               }
+       }
+
+}
+
+static void amlogic_set_max_payload(struct amlogic_pcie *amlogic_pcie, int size)
+{
+       int max_payload_size = 1;
+       u32 val = 0;
+
+       switch (size) {
+       case 128:
+               max_payload_size = 0;
+               break;
+       case 256:
+               max_payload_size = 1;
+               break;
+       case 512:
+               max_payload_size = 2;
+               break;
+       case 1024:
+               max_payload_size = 3;
+               break;
+       case 2048:
+               max_payload_size = 4;
+               break;
+       case 4096:
+               max_payload_size = 5;
+               break;
+       default:
+               max_payload_size = 1;
+               break;
+       }
+
+       val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS);
+       val &= (~(0x7<<5));
+       amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS);
+
+       val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS);
+       val |= (max_payload_size<<5);
+       amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS);
+}
+
+static void amlogic_set_max_rd_req_size
+                               (struct amlogic_pcie *amlogic_pcie, int size)
+{
+       int max_rd_req_size = 1;
+       u32 val = 0;
+
+       switch (size) {
+       case 128:
+               max_rd_req_size = 0;
+               break;
+       case 256:
+               max_rd_req_size = 1;
+               break;
+       case 512:
+               max_rd_req_size = 2;
+               break;
+       case 1024:
+               max_rd_req_size = 3;
+               break;
+       case 2048:
+               max_rd_req_size = 4;
+               break;
+       case 4096:
+               max_rd_req_size = 5;
+               break;
+       default:
+               max_rd_req_size = 1;
+               break;
+       }
+
+       val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS);
+       val &= (~(0x7<<12));
+       amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS);
+
+       val = amlogic_elb_readl(amlogic_pcie, PCIE_DEV_CTRL_DEV_STUS);
+       val |= (max_rd_req_size<<12);
+       amlogic_elb_writel(amlogic_pcie, val, PCIE_DEV_CTRL_DEV_STUS);
+}
+
+static void amlogic_pcie_init_dw(struct amlogic_pcie *amlogic_pcie)
+{
+       u32 val = 0;
+
+       val = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG0);
+       val &= (~APP_LTSSM_ENABLE);
+       amlogic_cfg_writel(amlogic_pcie, val, PCIE_CFG0);
+
+       val = amlogic_elb_readl(amlogic_pcie, PCIE_PORT_LINK_CTRL_OFF);
+       val |= (1<<7);
+       amlogic_elb_writel(amlogic_pcie, val, PCIE_PORT_LINK_CTRL_OFF);
+
+       val = amlogic_elb_readl(amlogic_pcie, PCIE_PORT_LINK_CTRL_OFF);
+       val &= (~(0x3f<<16));
+       amlogic_elb_writel(amlogic_pcie, val, PCIE_PORT_LINK_CTRL_OFF);
+
+       val = amlogic_elb_readl(amlogic_pcie, PCIE_PORT_LINK_CTRL_OFF);
+       val |= (0x1<<16);
+       amlogic_elb_writel(amlogic_pcie, val, PCIE_PORT_LINK_CTRL_OFF);
+
+       val = amlogic_elb_readl(amlogic_pcie, PCIE_GEN2_CTRL_OFF);
+       val &= (~(0x1f<<8));
+       amlogic_elb_writel(amlogic_pcie, val, PCIE_GEN2_CTRL_OFF);
+
+       val = amlogic_elb_readl(amlogic_pcie, PCIE_GEN2_CTRL_OFF);
+       val |= (0x1<<8);
+       amlogic_elb_writel(amlogic_pcie, val, PCIE_GEN2_CTRL_OFF);
+
+       val = amlogic_elb_readl(amlogic_pcie, PCIE_GEN2_CTRL_OFF);
+       val |= (1<<17);
+       amlogic_elb_writel(amlogic_pcie, val, PCIE_GEN2_CTRL_OFF);
+
+       amlogic_elb_writel(amlogic_pcie, 0x0, PCIE_BASE_ADDR0);
+       amlogic_elb_writel(amlogic_pcie, 0x0, PCIE_BASE_ADDR1);
+}
+
+static void amlogic_enable_memory_space(struct amlogic_pcie *amlogic_pcie)
+{
+       u32 val = 0;
+
+       dev_info(amlogic_pcie->pp.dev, "Set the RC Bus Master, Memory Space and I/O Space enables.\n");
+       val = amlogic_elb_readl(amlogic_pcie, 0x04);
+       val = 7;
+       amlogic_elb_writel(amlogic_pcie, val, 0x04);
+}
+
+
+static int amlogic_pcie_establish_link(struct amlogic_pcie *amlogic_pcie)
+{
+       struct pcie_port *pp = &amlogic_pcie->pp;
+       u32 val;
+
+       amlogic_pcie_init_dw(amlogic_pcie);
+       amlogic_set_max_payload(amlogic_pcie, 256);
+       amlogic_set_max_rd_req_size(amlogic_pcie, 256);
+
+       dw_pcie_setup_rc(pp);
+       amlogic_enable_memory_space(amlogic_pcie);
+
+       amlogic_pcie_assert_reset(amlogic_pcie);
+
+       val = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG0);
+       val |= APP_LTSSM_ENABLE;
+       amlogic_cfg_writel(amlogic_pcie, val, PCIE_CFG0);
+
+       /* check if the link is up or not */
+       if (!dw_pcie_wait_for_link(pp))
+               return 0;
+
+       return -ETIMEDOUT;
+}
+
+static irqreturn_t amlogic_pcie_irq_handler(int irq, void *arg)
+{
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t amlogic_pcie_msi_irq_handler(int irq, void *arg)
+{
+       struct amlogic_pcie *amlogic_pcie = arg;
+       struct pcie_port *pp = &amlogic_pcie->pp;
+
+       return dw_handle_msi_irq(pp);
+}
+
+static void amlogic_pcie_msi_init(struct amlogic_pcie *amlogic_pcie)
+{
+       struct pcie_port *pp = &amlogic_pcie->pp;
+
+       dw_pcie_msi_init(pp);
+}
+
+static void amlogic_pcie_enable_interrupts(struct amlogic_pcie *amlogic_pcie)
+{
+       if (IS_ENABLED(CONFIG_PCI_MSI))
+               amlogic_pcie_msi_init(amlogic_pcie);
+}
+
+static u32 amlogic_pcie_readl_rc(struct pcie_port *pp, u32 reg)
+{
+       u32 val;
+
+       val = readl(pp->dbi_base + reg);
+       return val;
+}
+
+static void amlogic_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val)
+{
+       writel(val, pp->dbi_base + reg);
+}
+
+static int amlogic_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
+                               u32 *val)
+{
+       int ret;
+       struct amlogic_pcie *amlogic_pcie = to_amlogic_pcie(pp);
+
+       if (amlogic_pcie->device_attch == 0)
+               return 0;
+
+       /* the device class is not reported correctly from the register */
+       if (where == PCI_CLASS_REVISION) {
+               dev_info(pp->dev, "the device class is not reported correctly from the register\n");
+               *val = readl(pp->dbi_base + PCI_CLASS_REVISION);
+               *val &= 0xff;   /* keep revision id */
+               *val |= PCI_CLASS_BRIDGE_PCI << 16;
+               return PCIBIOS_SUCCESSFUL;
+       }
+
+       ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
+       return ret;
+}
+
+static int amlogic_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
+                               u32 val)
+{
+       int ret;
+       struct amlogic_pcie *amlogic_pcie = to_amlogic_pcie(pp);
+
+       if (amlogic_pcie->device_attch == 0)
+               return 0;
+
+       ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
+       return ret;
+}
+
+static int amlogic_pcie_link_up(struct pcie_port *pp)
+{
+       u32   smlh_up = 0;
+       u32   rdlh_up = 0;
+       u32   ltssm_up = 0;
+       u32   speed_okay = 0;
+       u32   current_data_rate;
+       int   cnt = 0;
+       struct amlogic_pcie *amlogic_pcie = to_amlogic_pcie(pp);
+
+       while (smlh_up == 0 || rdlh_up == 0
+               || ltssm_up == 0 || speed_okay == 0) {
+               udelay(20);
+               smlh_up = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG_STATUS12);
+               smlh_up = (smlh_up >> 6) & 0x1;
+
+               rdlh_up = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG_STATUS12);
+               rdlh_up = (rdlh_up >> 16) & 0x1;
+               ltssm_up = amlogic_cfg_readl(amlogic_pcie, PCIE_CFG_STATUS12);
+               ltssm_up = ((ltssm_up >> 10) & 0x1f) == 0x11 ? 1 : 0;
+               current_data_rate = amlogic_cfg_readl(
+                       amlogic_pcie, PCIE_CFG_STATUS17);
+               current_data_rate = (current_data_rate >> 7) & 0x1;
+
+               if ((current_data_rate == PCIE_GEN2) ||
+                        (current_data_rate == PCIE_GEN1))
+                       speed_okay = 1;
+
+               if (smlh_up)
+                       dev_dbg(pp->dev, "smlh_link_up is on\n");
+               if (rdlh_up)
+                       dev_dbg(pp->dev, "rdlh_link_up is on\n");
+               if (ltssm_up)
+                       dev_dbg(pp->dev, "ltssm_up is on\n");
+               if (speed_okay)
+                       dev_dbg(pp->dev, "speed_okay\n");
+
+               cnt++;
+
+               if (cnt >= WAIT_LINKUP_TIMEOUT) {
+                       dev_err(pp->dev, "Error: Wait linkup timeout.\n");
+                       return 0;
+               }
+
+               udelay(20);
+       }
+
+       if (current_data_rate == PCIE_GEN2)
+               dev_info(pp->dev, "PCIE SPEED IS GEN2\n");
+
+       return 1;
+}
+
+static void amlogic_pcie_host_init(struct pcie_port *pp)
+{
+       struct amlogic_pcie *amlogic_pcie = to_amlogic_pcie(pp);
+       int ret;
+
+       ret = amlogic_pcie_establish_link(amlogic_pcie);
+       if (ret)
+               if (amlogic_pcie->phy->device_attch == 0)
+                       return;
+
+       amlogic_pcie->phy->device_attch = 1;
+       if (!ret)
+               amlogic_pcie->device_attch = 1;
+
+       amlogic_pcie_enable_interrupts(amlogic_pcie);
+}
+
+static struct pcie_host_ops amlogic_pcie_host_ops = {
+       .readl_rc = amlogic_pcie_readl_rc,
+       .writel_rc = amlogic_pcie_writel_rc,
+       .rd_own_conf = amlogic_pcie_rd_own_conf,
+       .wr_own_conf = amlogic_pcie_wr_own_conf,
+       .link_up = amlogic_pcie_link_up,
+       .host_init = amlogic_pcie_host_init,
+};
+
+static int __init amlogic_add_pcie_port(struct amlogic_pcie *amlogic_pcie,
+                                      struct platform_device *pdev)
+{
+       struct pcie_port *pp = &amlogic_pcie->pp;
+       struct device *dev = pp->dev;
+       int ret;
+
+       pp->irq = platform_get_irq(pdev, 1);
+       if (!pp->irq) {
+               dev_err(dev, "failed to get irq\n");
+               return -ENODEV;
+       }
+       ret = devm_request_irq(dev, pp->irq, amlogic_pcie_irq_handler,
+                               IRQF_SHARED, "amlogic-pcie", amlogic_pcie);
+       if (ret) {
+               dev_err(dev, "failed to request irq\n");
+               return ret;
+       }
+
+       if (IS_ENABLED(CONFIG_PCI_MSI)) {
+               pp->msi_irq = platform_get_irq(pdev, 0);
+               if (!pp->msi_irq) {
+                       dev_err(dev, "failed to get msi irq\n");
+                       return -ENODEV;
+               }
+
+               ret = devm_request_irq(dev, pp->msi_irq,
+                                       amlogic_pcie_msi_irq_handler,
+                                       IRQF_SHARED | IRQF_NO_THREAD,
+                                       "amlogic-pcie", amlogic_pcie);
+               if (ret) {
+                       dev_err(dev, "failed to request msi irq\n");
+                       return ret;
+               }
+       }
+
+       pp->root_bus_nr = -1;
+       pp->ops = &amlogic_pcie_host_ops;
+       pp->dbi_base = amlogic_pcie->elbi_base;
+
+       ret = dw_pcie_host_init(pp);
+       if (ret) {
+               dev_err(dev, "failed to initialize host\n");
+               return ret;
+       }
+
+       if (amlogic_pcie->device_attch == 0) {
+               dev_err(pp->dev, "link timeout, disable PCIE PLL\n");
+               clk_disable_unprepare(amlogic_pcie->bus_clk);
+               clk_disable_unprepare(amlogic_pcie->clk);
+               dev_err(pp->dev, "power down pcie phy\n");
+               writel(0x1d, pcie_aml_regs_v2.pcie_phy_r[0]);
+               amlogic_pcie->phy->power_state = 0;
+       }
+
+       return 0;
+}
+
+static int __init amlogic_pcie_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct amlogic_pcie *amlogic_pcie;
+       struct pcie_port *pp;
+       struct device_node *np = dev->of_node;
+       struct pcie_phy         *phy;
+       struct resource *elbi_base;
+       struct resource *phy_base;
+       struct resource *cfg_base;
+       struct resource *reset_base;
+       int ret;
+       int pcie_num = 0;
+       int num_lanes = 0;
+       int gpio_type = 0;
+       unsigned long rate;
+#define PCIE_PLL_RATE 100000000
+       int j = 0;
+       u32 val = 0;
+       static u32 port_num;
+       u32 pm_enable = 1;
+       int pcie_apb_rst_bit = 0;
+       int pcie_phy_rst_bit = 0;
+       int pcie_ctrl_a_rst_bit = 0;
+
+       dev_info(&pdev->dev, "amlogic_pcie_probe!\n");
+
+       amlogic_pcie = devm_kzalloc(dev, sizeof(*amlogic_pcie), GFP_KERNEL);
+       if (!amlogic_pcie)
+               return -ENOMEM;
+
+       amlogic_pcie->device_attch = 0;
+       amlogic_pcie->rst_mod = 1;
+       pp = &amlogic_pcie->pp;
+       pp->dev = dev;
+       port_num++;
+       amlogic_pcie->port_num = port_num;
+       if (amlogic_pcie->port_num == 1) {
+               phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+               if (!phy) {
+                       port_num--;
+                       return -ENOMEM;
+               }
+               g_pcie_phy_v2 = phy;
+       }
+
+       amlogic_pcie->phy = g_pcie_phy_v2;
+
+       ret = of_property_read_u32(np, "pcie-apb-rst-bit", &pcie_apb_rst_bit);
+       if (ret)
+               amlogic_pcie->rst_mod = 0;
+
+       ret = of_property_read_u32(np, "pcie-phy-rst-bit", &pcie_phy_rst_bit);
+       if (ret)
+               amlogic_pcie->rst_mod = 0;
+
+       ret = of_property_read_u32(np, "pcie-ctrl-a-rst-bit",
+                               &pcie_ctrl_a_rst_bit);
+       if (ret)
+               amlogic_pcie->rst_mod = 0;
+
+       ret = of_property_read_u32(np, "pcie-num", &pcie_num);
+       if (ret)
+               amlogic_pcie->pcie_num = 0;
+
+       amlogic_pcie->pcie_num = pcie_num;
+
+       ret = of_property_read_u32(np, "pm-enable", &pm_enable);
+       if (ret)
+               amlogic_pcie->pm_enable = 1;
+       else
+               amlogic_pcie->pm_enable = pm_enable;
+
+       ret = of_property_read_u32(np, "num-lanes", &num_lanes);
+       if (ret)
+               pp->lanes = 0;
+       pp->lanes = num_lanes;
+
+       if (!amlogic_pcie->phy->phy_base) {
+               phy_base = platform_get_resource_byname(
+                       pdev, IORESOURCE_MEM, "phy");
+               amlogic_pcie->phy->phy_base =
+                        devm_ioremap_resource(dev, phy_base);
+               if (IS_ERR(amlogic_pcie->phy->phy_base)) {
+                       ret = PTR_ERR(amlogic_pcie->phy->phy_base);
+                       port_num--;
+                       return ret;
+               }
+       }
+
+       if (!amlogic_pcie->phy->power_state) {
+               for (j = 0; j < 7; j++)
+                       pcie_aml_regs_v2.pcie_phy_r[j] = (void __iomem *)
+                               ((unsigned long)amlogic_pcie->phy->phy_base
+                                        + 4*j);
+               writel(0x1c, pcie_aml_regs_v2.pcie_phy_r[0]);
+               amlogic_pcie->phy->power_state = 1;
+       }
+
+       ret = of_property_read_u32(np, "gpio-type", &gpio_type);
+       amlogic_pcie->gpio_type = gpio_type;
+
+       amlogic_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
+
+       if (!amlogic_pcie->phy->reset_base) {
+               reset_base = platform_get_resource_byname(
+                       pdev, IORESOURCE_MEM, "reset");
+               amlogic_pcie->phy->reset_base = devm_ioremap_resource(
+                       dev, reset_base);
+               if (IS_ERR(amlogic_pcie->phy->reset_base)) {
+                       ret = PTR_ERR(amlogic_pcie->phy->reset_base);
+                       goto fail_pcie;
+               }
+       }
+
+       /* RESET0[1,2,6,7] = 0*/
+       if (!amlogic_pcie->phy->reset_state) {
+               if (amlogic_pcie->rst_mod == 0) {
+                       val = readl(amlogic_pcie->phy->reset_base);
+                       val &= ~((0x3<<6) | (0x3<<1));
+                       writel(val, amlogic_pcie->phy->reset_base);
+               } else {
+                       val = readl(amlogic_pcie->phy->reset_base);
+                       val &= ~((0x1<<pcie_apb_rst_bit)|(0x1<<pcie_phy_rst_bit)
+                                       | (0x1<<pcie_ctrl_a_rst_bit));
+                       writel(val, amlogic_pcie->phy->reset_base);
+               }
+       }
+       amlogic_pcie->phy_clk = devm_clk_get(dev, "pcie_phy");
+       if (IS_ERR(amlogic_pcie->phy_clk)) {
+               dev_err(dev, "Failed to get pcie pcie_phy clock\n");
+               ret = PTR_ERR(amlogic_pcie->phy_clk);
+               goto fail_pcie;
+       }
+
+       ret = clk_prepare_enable(amlogic_pcie->phy_clk);
+       if (ret)
+               goto fail_pcie_phy;
+       amlogic_pcie->bus_clk = devm_clk_get(dev, "pcie_refpll");
+       if (IS_ERR(amlogic_pcie->bus_clk)) {
+               dev_err(dev, "Failed to get pcie pcie_refpll clock\n");
+               ret = PTR_ERR(amlogic_pcie->bus_clk);
+               goto fail_pcie;
+       }
+
+       ret = clk_prepare_enable(amlogic_pcie->bus_clk);
+       if (ret)
+               goto fail_pcie;
+
+       if (!amlogic_pcie->phy->reset_state) {
+               rate = clk_get_rate(amlogic_pcie->bus_clk);
+               if (rate != PCIE_PLL_RATE) {
+                       ret = -ENODEV;
+                       goto fail_pcie;
+               }
+       }
+
+       /*RESET0[6,7] = 1*/
+       if (!amlogic_pcie->phy->reset_state) {
+               if (amlogic_pcie->rst_mod == 0) {
+                       val = readl(amlogic_pcie->phy->reset_base);
+                       val |=    (0x3<<6);
+                       writel(val, amlogic_pcie->phy->reset_base);
+               } else {
+                       val = readl(amlogic_pcie->phy->reset_base);
+                       val |= ((0x1<<pcie_apb_rst_bit)|
+                                       (0x1<<pcie_phy_rst_bit));
+                       writel(val, amlogic_pcie->phy->reset_base);
+               }
+               mdelay(1);
+       }
+       amlogic_pcie->clk = devm_clk_get(dev, "pcie");
+       if (IS_ERR(amlogic_pcie->clk)) {
+               dev_err(dev, "Failed to get pcie rc clock\n");
+               ret = PTR_ERR(amlogic_pcie->clk);
+               goto fail_bus_clk;
+       }
+
+       ret = clk_prepare_enable(amlogic_pcie->clk);
+       if (ret)
+               goto fail_bus_clk;
+
+       /*RESET0[1,2] = 1*/
+       if (amlogic_pcie->pcie_num == 1) {
+               if (amlogic_pcie->rst_mod == 0) {
+                       val = readl(amlogic_pcie->phy->reset_base);
+                       val |=  (0x1<<1);
+                       writel(val, amlogic_pcie->phy->reset_base);
+               } else {
+                       val = readl(amlogic_pcie->phy->reset_base);
+                       val |=  (0x1<<pcie_ctrl_a_rst_bit);
+                       writel(val, amlogic_pcie->phy->reset_base);
+               }
+               mdelay(1);
+       } else {
+               if (amlogic_pcie->rst_mod == 0) {
+                       val = readl(amlogic_pcie->phy->reset_base);
+                       val |= (0x1<<2);
+                       writel(val, amlogic_pcie->phy->reset_base);
+               } else {
+                       val = readl(amlogic_pcie->phy->reset_base);
+                       val |= (0x1<<pcie_ctrl_a_rst_bit);
+                       writel(val, amlogic_pcie->phy->reset_base);
+               }
+               mdelay(1);
+       }
+
+       amlogic_pcie->phy->reset_state = 1;
+
+       elbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
+       amlogic_pcie->elbi_base = devm_ioremap_resource(dev, elbi_base);
+       if (IS_ERR(amlogic_pcie->elbi_base)) {
+               ret = PTR_ERR(amlogic_pcie->elbi_base);
+               goto fail_clk;
+       }
+
+       cfg_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
+       amlogic_pcie->cfg_base = devm_ioremap_resource(dev, cfg_base);
+       if (IS_ERR(amlogic_pcie->cfg_base)) {
+               ret = PTR_ERR(amlogic_pcie->cfg_base);
+               goto fail_clk;
+       }
+
+       ret = amlogic_add_pcie_port(amlogic_pcie, pdev);
+       if (ret < 0)
+               goto fail_clk;
+
+       platform_set_drvdata(pdev, amlogic_pcie);
+       device_create_file(&pdev->dev, &dev_attr_phyread_v2);
+       device_create_file(&pdev->dev, &dev_attr_phywrite_v2);
+       return 0;
+
+fail_clk:
+       clk_disable_unprepare(amlogic_pcie->clk);
+fail_bus_clk:
+       clk_disable_unprepare(amlogic_pcie->bus_clk);
+fail_pcie:
+       clk_disable_unprepare(amlogic_pcie->phy_clk);
+       port_num--;
+fail_pcie_phy:
+       return ret;
+}
+
+static int __exit amlogic_pcie_remove(struct platform_device *pdev)
+{
+       struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev);
+
+       if (amlogic_pcie->phy->power_state == 0) {
+               dev_info(&pdev->dev, "PCIE phy power off, no remove\n");
+               return 0;
+       }
+
+       device_remove_file(&pdev->dev, &dev_attr_phywrite_v2);
+       device_remove_file(&pdev->dev, &dev_attr_phyread_v2);
+
+       clk_disable_unprepare(amlogic_pcie->clk);
+       clk_disable_unprepare(amlogic_pcie->bus_clk);
+       clk_disable_unprepare(amlogic_pcie->phy_clk);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int amlogic_pcie_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev);
+       struct pcie_port *pp = &amlogic_pcie->pp;
+       u32 val;
+
+       if (!amlogic_pcie->pm_enable) {
+               dev_info(dev, "don't suspend amlogic pcie\n");
+               return 0;
+       }
+
+       if (amlogic_pcie->device_attch == 0) {
+               dev_info(dev, "controller power off, no suspend\n");
+               return 0;
+       }
+
+       dev_info(dev, "amlogic_pcie_suspend\n");
+
+       /* clear MSE */
+       val = dw_pcie_readl_rc(pp, PCI_COMMAND);
+       val &= ~PCI_COMMAND_MEMORY;
+       dw_pcie_writel_rc(pp, PCI_COMMAND, val);
+
+       return 0;
+}
+
+static int amlogic_pcie_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev);
+       struct pcie_port *pp = &amlogic_pcie->pp;
+       u32 val;
+
+       if (!amlogic_pcie->pm_enable) {
+               dev_info(dev, "don't resume amlogic pcie\n");
+               return 0;
+       }
+
+       if (amlogic_pcie->device_attch == 0) {
+               dev_info(dev, "controller power off, no resume\n");
+               return 0;
+       }
+
+       dev_info(dev, "amlogic_pcie_resume\n");
+
+       /* set MSE */
+       val = dw_pcie_readl_rc(pp, PCI_COMMAND);
+       val |= PCI_COMMAND_MEMORY;
+       dw_pcie_writel_rc(pp, PCI_COMMAND, val);
+
+       return 0;
+}
+
+static int amlogic_pcie_suspend_noirq(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev);
+
+       if (!amlogic_pcie->pm_enable) {
+               dev_info(dev, "don't noirq suspend amlogic pcie\n");
+               return 0;
+       }
+
+       if (amlogic_pcie->phy->device_attch == 0) {
+               dev_info(dev, "PCIE phy power off, no suspend noirq\n");
+               return 0;
+       }
+
+       if (amlogic_pcie->device_attch == 0) {
+               dev_info(dev, "controller power off, no suspend noirq\n");
+               if (amlogic_pcie->pcie_num == 1) {
+                       writel(0x1d, pcie_aml_regs_v2.pcie_phy_r[0]);
+                       amlogic_pcie->phy->power_state = 0;
+               }
+               return 0;
+       }
+
+       dev_info(dev, "amlogic_pcie_suspend_noirq\n");
+
+       clk_disable_unprepare(amlogic_pcie->clk);
+       clk_disable_unprepare(amlogic_pcie->bus_clk);
+       clk_disable_unprepare(amlogic_pcie->phy_clk);
+       amlogic_pcie->phy->reset_state = 0;
+
+       if (amlogic_pcie->pcie_num == 1) {
+               writel(0x1d, pcie_aml_regs_v2.pcie_phy_r[0]);
+               amlogic_pcie->phy->power_state = 0;
+       }
+
+       return 0;
+}
+
+static int amlogic_pcie_resume_noirq(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct amlogic_pcie *amlogic_pcie = platform_get_drvdata(pdev);
+       unsigned long rate = 100000000;
+
+       if (!amlogic_pcie->pm_enable) {
+               dev_info(dev, "don't noirq resume amlogic pcie\n");
+               return 0;
+       }
+
+       if (amlogic_pcie->phy->device_attch == 0) {
+               dev_info(dev, "PCIE phy power off, no resume noirq\n");
+               return 0;
+       }
+
+       if (amlogic_pcie->pcie_num == 1) {
+               writel(0x1c, pcie_aml_regs_v2.pcie_phy_r[0]);
+               amlogic_pcie->phy->power_state = 1;
+               udelay(500);
+       }
+
+       if (amlogic_pcie->device_attch == 0) {
+               dev_info(dev, "controller power off, no resume noirq\n");
+               return 0;
+       }
+
+       dev_info(dev, "amlogic_pcie_resume_noirq\n");
+       if (!amlogic_pcie->phy->reset_state)
+               clk_set_rate(amlogic_pcie->bus_clk, rate);
+
+       amlogic_pcie->phy->reset_state = 1;
+
+       clk_prepare_enable(amlogic_pcie->phy_clk);
+       clk_prepare_enable(amlogic_pcie->bus_clk);
+       clk_prepare_enable(amlogic_pcie->clk);
+       udelay(500);
+
+       return 0;
+}
+
+#else
+#define amlogic_pcie_suspend NULL
+#define amlogic_pcie_resume NULL
+#define amlogic_pcie_suspend_noirq NULL
+#define amlogic_pcie_resume_noirq NULL
+#endif
+
+static const struct dev_pm_ops amlogic_pcie_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(amlogic_pcie_suspend, amlogic_pcie_resume)
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(amlogic_pcie_suspend_noirq,
+                                     amlogic_pcie_resume_noirq)
+};
+
+
+
+static const struct of_device_id amlogic_pcie_of_match[] = {
+       { .compatible = "amlogic, amlogic-pcie-v2", },
+       {},
+};
+
+static struct platform_driver amlogic_pcie_driver = {
+       .remove         = __exit_p(amlogic_pcie_remove),
+       .driver = {
+               .name   = "amlogic-pcie-v2",
+               .of_match_table = amlogic_pcie_of_match,
+               .pm     = &amlogic_pcie_pm_ops,
+       },
+};
+
+/* AMLOGIC PCIe driver does not allow module unload */
+static int __init amlogic_pcie_init(void)
+{
+       return platform_driver_probe(&amlogic_pcie_driver, amlogic_pcie_probe);
+}
+subsys_initcall(amlogic_pcie_init);