[PATCH v4 17/17] PCI: dwc: artpec6: Add support for the ARTPEC-7 SoC

2017-11-03 Thread Niklas Cassel
Add support for the ARTPEC-7 SoC in the artpec6 driver.
The ARTPEC-6 SoC and the ARTPEC-7 SoC are very similar.
Unfortunately, some fields in the PCIECFG and PCIESTAT
register have changed.

Signed-off-by: Niklas Cassel 
---
 drivers/pci/dwc/pcie-artpec6.c | 162 -
 1 file changed, 159 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c
index e5030477099c..d27f1b42cee6 100644
--- a/drivers/pci/dwc/pcie-artpec6.c
+++ b/drivers/pci/dwc/pcie-artpec6.c
@@ -27,14 +27,21 @@
 
 #define to_artpec6_pcie(x) dev_get_drvdata((x)->dev)
 
+enum artpec_pcie_variants {
+   ARTPEC6,
+   ARTPEC7,
+};
+
 struct artpec6_pcie {
struct dw_pcie  *pci;
struct regmap   *regmap;/* DT axis,syscon-pcie */
void __iomem*phy_base;  /* DT phy */
+   enum artpec_pcie_variants variant;
enum dw_pcie_device_mode mode;
 };
 
 struct artpec_pcie_of_data {
+   enum artpec_pcie_variants variant;
enum dw_pcie_device_mode mode;
 };
 
@@ -45,6 +52,13 @@ static const struct of_device_id artpec6_pcie_of_match[];
 #define PCIE_PHY_DEBUG_R0  (PL_OFFSET + 0x28)
 #define PCIE_PHY_DEBUG_R1  (PL_OFFSET + 0x2c)
 
+#define ACK_F_ASPM_CTRL_OFF(PL_OFFSET + 0xc)
+#define ACK_N_FTS_MASK GENMASK(15, 8)
+#define ACK_N_FTS(x)   (((x) << 8) & ACK_N_FTS_MASK)
+
+#define FAST_TRAINING_SEQ_MASK GENMASK(7, 0)
+#define FAST_TRAINING_SEQ(x)   (((x) << 0) & FAST_TRAINING_SEQ_MASK)
+
 /* ARTPEC-6 specific registers */
 #define PCIECFG0x18
 #define  PCIECFG_DBG_OEN   BIT(24)
@@ -59,6 +73,13 @@ static const struct of_device_id artpec6_pcie_of_match[];
 #define  PCIECFG_MODE_TX_DRV_ENBIT(3)
 #define  PCIECFG_CISRREN   BIT(2)
 #define  PCIECFG_MACRO_ENABLE  BIT(0)
+/* ARTPEC-7 specific fields */
+#define  PCIECFG_REFCLKSEL BIT(23)
+#define  PCIECFG_NOC_RESET BIT(3)
+
+#define PCIESTAT   0x1c
+/* ARTPEC-7 specific fields */
+#define  PCIESTAT_EXTREFCLKBIT(3)
 
 #define NOCCFG 0x40
 #define  NOCCFG_ENABLE_CLK_PCIEBIT(4)
@@ -69,6 +90,12 @@ static const struct of_device_id artpec6_pcie_of_match[];
 #define PHY_STATUS 0x118
 #define  PHY_COSPLLLOCKBIT(0)
 
+#define PHY_TX_ASIC_OUT0x1014
+#define  PHY_TX_ASIC_OUT_TX_ACKBIT(0)
+
+#define PHY_RX_ASIC_OUT0x101b
+#define  PHY_RX_ASIC_OUT_ACK   BIT(0)
+
 static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset)
 {
u32 val;
@@ -127,7 +154,7 @@ static const struct dw_pcie_ops dw_pcie_ops = {
.stop_link = artpec6_pcie_stop_link,
 };
 
-static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie)
+static void artpec6_pcie_init_phy_a6(struct artpec6_pcie *artpec6_pcie)
 {
u32 val;
unsigned int retries;
@@ -173,12 +200,109 @@ static void artpec6_pcie_init_phy(struct artpec6_pcie 
*artpec6_pcie)
} while (retries && !(val & PHY_COSPLLLOCK));
 }
 
+static void artpec6_pcie_init_phy_a7(struct artpec6_pcie *artpec6_pcie)
+{
+   struct dw_pcie *pci = artpec6_pcie->pci;
+   u32 val;
+   u16 phy_status_tx, phy_status_rx;
+   unsigned int retries;
+   bool extrefclk;
+
+   /* Check if external reference clock is connected */
+   val = artpec6_pcie_readl(artpec6_pcie, PCIESTAT);
+   extrefclk = !!(val & PCIESTAT_EXTREFCLK);
+   dev_dbg(pci->dev, "Using reference clock: %s\n",
+   extrefclk ? "external" : "internal");
+
+   val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
+   val |=  PCIECFG_RISRCREN |  /* Receiver term. 50 Ohm */
+   PCIECFG_PCLK_ENABLE;
+   if (extrefclk)
+   val |= PCIECFG_REFCLKSEL;
+   else
+   val &= ~PCIECFG_REFCLKSEL;
+   artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
+   usleep_range(10, 20);
+
+   val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
+   val |= NOCCFG_ENABLE_CLK_PCIE;
+   artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
+   usleep_range(20, 30);
+
+   val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
+   val &= ~NOCCFG_POWER_PCIE_IDLEREQ;
+   artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
+
+   retries = 50;
+   do {
+   usleep_range(1000, 2000);
+   val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
+   retries--;
+   } while (retries &&
+   (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE)));
+
+   retries = 50;
+   do {
+   usleep_range(1000, 2000);
+   phy_status_tx = readw(artpec6_pcie->phy_base + PHY_TX_ASIC_OUT);
+ 

[PATCH v4 17/17] PCI: dwc: artpec6: Add support for the ARTPEC-7 SoC

2017-11-03 Thread Niklas Cassel
Add support for the ARTPEC-7 SoC in the artpec6 driver.
The ARTPEC-6 SoC and the ARTPEC-7 SoC are very similar.
Unfortunately, some fields in the PCIECFG and PCIESTAT
register have changed.

Signed-off-by: Niklas Cassel 
---
 drivers/pci/dwc/pcie-artpec6.c | 162 -
 1 file changed, 159 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/dwc/pcie-artpec6.c b/drivers/pci/dwc/pcie-artpec6.c
index e5030477099c..d27f1b42cee6 100644
--- a/drivers/pci/dwc/pcie-artpec6.c
+++ b/drivers/pci/dwc/pcie-artpec6.c
@@ -27,14 +27,21 @@
 
 #define to_artpec6_pcie(x) dev_get_drvdata((x)->dev)
 
+enum artpec_pcie_variants {
+   ARTPEC6,
+   ARTPEC7,
+};
+
 struct artpec6_pcie {
struct dw_pcie  *pci;
struct regmap   *regmap;/* DT axis,syscon-pcie */
void __iomem*phy_base;  /* DT phy */
+   enum artpec_pcie_variants variant;
enum dw_pcie_device_mode mode;
 };
 
 struct artpec_pcie_of_data {
+   enum artpec_pcie_variants variant;
enum dw_pcie_device_mode mode;
 };
 
@@ -45,6 +52,13 @@ static const struct of_device_id artpec6_pcie_of_match[];
 #define PCIE_PHY_DEBUG_R0  (PL_OFFSET + 0x28)
 #define PCIE_PHY_DEBUG_R1  (PL_OFFSET + 0x2c)
 
+#define ACK_F_ASPM_CTRL_OFF(PL_OFFSET + 0xc)
+#define ACK_N_FTS_MASK GENMASK(15, 8)
+#define ACK_N_FTS(x)   (((x) << 8) & ACK_N_FTS_MASK)
+
+#define FAST_TRAINING_SEQ_MASK GENMASK(7, 0)
+#define FAST_TRAINING_SEQ(x)   (((x) << 0) & FAST_TRAINING_SEQ_MASK)
+
 /* ARTPEC-6 specific registers */
 #define PCIECFG0x18
 #define  PCIECFG_DBG_OEN   BIT(24)
@@ -59,6 +73,13 @@ static const struct of_device_id artpec6_pcie_of_match[];
 #define  PCIECFG_MODE_TX_DRV_ENBIT(3)
 #define  PCIECFG_CISRREN   BIT(2)
 #define  PCIECFG_MACRO_ENABLE  BIT(0)
+/* ARTPEC-7 specific fields */
+#define  PCIECFG_REFCLKSEL BIT(23)
+#define  PCIECFG_NOC_RESET BIT(3)
+
+#define PCIESTAT   0x1c
+/* ARTPEC-7 specific fields */
+#define  PCIESTAT_EXTREFCLKBIT(3)
 
 #define NOCCFG 0x40
 #define  NOCCFG_ENABLE_CLK_PCIEBIT(4)
@@ -69,6 +90,12 @@ static const struct of_device_id artpec6_pcie_of_match[];
 #define PHY_STATUS 0x118
 #define  PHY_COSPLLLOCKBIT(0)
 
+#define PHY_TX_ASIC_OUT0x1014
+#define  PHY_TX_ASIC_OUT_TX_ACKBIT(0)
+
+#define PHY_RX_ASIC_OUT0x101b
+#define  PHY_RX_ASIC_OUT_ACK   BIT(0)
+
 static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset)
 {
u32 val;
@@ -127,7 +154,7 @@ static const struct dw_pcie_ops dw_pcie_ops = {
.stop_link = artpec6_pcie_stop_link,
 };
 
-static void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie)
+static void artpec6_pcie_init_phy_a6(struct artpec6_pcie *artpec6_pcie)
 {
u32 val;
unsigned int retries;
@@ -173,12 +200,109 @@ static void artpec6_pcie_init_phy(struct artpec6_pcie 
*artpec6_pcie)
} while (retries && !(val & PHY_COSPLLLOCK));
 }
 
+static void artpec6_pcie_init_phy_a7(struct artpec6_pcie *artpec6_pcie)
+{
+   struct dw_pcie *pci = artpec6_pcie->pci;
+   u32 val;
+   u16 phy_status_tx, phy_status_rx;
+   unsigned int retries;
+   bool extrefclk;
+
+   /* Check if external reference clock is connected */
+   val = artpec6_pcie_readl(artpec6_pcie, PCIESTAT);
+   extrefclk = !!(val & PCIESTAT_EXTREFCLK);
+   dev_dbg(pci->dev, "Using reference clock: %s\n",
+   extrefclk ? "external" : "internal");
+
+   val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
+   val |=  PCIECFG_RISRCREN |  /* Receiver term. 50 Ohm */
+   PCIECFG_PCLK_ENABLE;
+   if (extrefclk)
+   val |= PCIECFG_REFCLKSEL;
+   else
+   val &= ~PCIECFG_REFCLKSEL;
+   artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
+   usleep_range(10, 20);
+
+   val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
+   val |= NOCCFG_ENABLE_CLK_PCIE;
+   artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
+   usleep_range(20, 30);
+
+   val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
+   val &= ~NOCCFG_POWER_PCIE_IDLEREQ;
+   artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
+
+   retries = 50;
+   do {
+   usleep_range(1000, 2000);
+   val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
+   retries--;
+   } while (retries &&
+   (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE)));
+
+   retries = 50;
+   do {
+   usleep_range(1000, 2000);
+   phy_status_tx = readw(artpec6_pcie->phy_base + PHY_TX_ASIC_OUT);
+   phy_status_rx =