armbian_build/patch/kernel/archive/spacemit-6.1/030-drivers-pci-controller-dwc.patch
2024-07-01 19:15:00 +02:00

2167 lines
60 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Patrick Yavitz <pyavitz@armbian.com>
Date: Fri, 21 Jun 2024 11:54:06 -0400
Subject: add spacemit patch set
source: https://gitee.com/bianbu-linux/linux-6.1
Signed-off-by: Patrick Yavitz <pyavitz@armbian.com>
---
drivers/pci/controller/dwc/Kconfig | 35 +
drivers/pci/controller/dwc/Makefile | 2 +
drivers/pci/controller/dwc/pcie-designware-host.c | 5 +-
drivers/pci/controller/dwc/pcie-k1x.c | 1846 ++++++++++
drivers/pci/controller/dwc/pcie-spacemit.c | 209 ++
5 files changed, 2096 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index 111111111111..222222222222 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -48,6 +48,32 @@ config PCI_DRA7XX_EP
to enable device-specific features PCI_DRA7XX_EP must be selected.
This uses the DesignWare core.
+config PCI_K1X
+ bool
+
+config PCI_K1X_HOST
+ bool "Spacemit K1X PCIe Controller - Host Mode"
+ depends on SOC_SPACEMIT_K1X
+ depends on PCI && PCI_MSI_IRQ_DOMAIN
+ depends on OF && HAS_IOMEM
+ select PCIE_DW_HOST
+ select PCI_K1X
+ default y
+ help
+ Enables support for the PCIe controller in the K1X SoC to work in
+ host mode.
+
+config PCI_K1X_EP
+ bool "Spacemit K1X PCIE Controller - Endpoint Mode"
+ depends on SOC_SPACEMIT_K1X
+ depends on PCI_ENDPOINT
+ depends on OF && HAS_IOMEM
+ select PCIE_DW_EP
+ select PCI_K1X
+ help
+ Enables support for the PCIe controller in the K1X SoC to work in
+ endpoint mode.
+
config PCIE_DW_PLAT
bool
@@ -385,4 +411,13 @@ config PCIE_FU740
Say Y here if you want PCIe controller support for the SiFive
FU740.
+config PCIE_SPACEMIT
+ bool "Spacemit PCIe controller"
+ depends on SOC_SPACEMIT || COMPILE_TEST
+ depends on PCI_MSI_IRQ_DOMAIN
+ select PCIE_DW_HOST
+ help
+ Say Y here to enable PCIe controller support on Spacemit SoCs. The
+ PCIe controller uses the DesignWare core plus Spacemit hardware wrappers.
+
endmenu
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
index 111111111111..222222222222 100644
--- a/drivers/pci/controller/dwc/Makefile
+++ b/drivers/pci/controller/dwc/Makefile
@@ -25,6 +25,8 @@ obj-$(CONFIG_PCIE_TEGRA194) += pcie-tegra194.o
obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o
+obj-$(CONFIG_PCIE_SPACEMIT) += pcie-spacemit.o
+obj-$(CONFIG_PCI_K1X) += pcie-k1x.o
# The following drivers are for devices that use the generic ACPI
# pci_root.c driver but don't support standard ECAM config access.
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 111111111111..222222222222 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -367,7 +367,10 @@ static int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)
dw_chained_msi_isr, pp);
}
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (IS_ENABLED(CONFIG_SOC_SPACEMIT_K1PRO))
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+ else
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret)
dev_warn(dev, "Failed to set DMA mask to 32-bit. Devices with only 32-bit MSI support may not work properly\n");
diff --git a/drivers/pci/controller/dwc/pcie-k1x.c b/drivers/pci/controller/dwc/pcie-k1x.c
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/drivers/pci/controller/dwc/pcie-k1x.c
@@ -0,0 +1,1846 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Spacemit k1x PCIe rc && ep driver
+ *
+ * Copyright (c) 2023, spacemit Corporation.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/resource.h>
+#include <linux/types.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "../../pci.h"
+#include "pcie-designware.h"
+
+/* PCIe controller wrapper k1x configuration registers */
+
+#define K1X_PHY_AHB_IRQ_EN 0x0000
+#define IRQ_EN BIT(0)
+#define PME_TURN_OFF BIT(5)
+
+#define K1X_PHY_AHB_IRQSTATUS_INTX 0x0008
+#define INTA BIT(6)
+#define INTB BIT(7)
+#define INTC BIT(8)
+#define INTD BIT(9)
+#define LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD)
+#define INTX_MASK GENMASK(9, 6)
+#define INTX_SHIFT 6
+
+#define K1X_PHY_AHB_IRQENABLE_SET_INTX 0x000c
+
+#define K1X_PHY_AHB_IRQSTATUS_MSI 0x0010
+#define MSI BIT(11)
+#define PCIE_REMOTE_INTERRUPT BIT(31)
+/* DMA write channel 0~7 irq*/
+#define EDMA_INT0 BIT(0)
+#define EDMA_INT1 BIT(1)
+#define EDMA_INT2 BIT(2)
+#define EDMA_INT3 BIT(3)
+#define EDMA_INT4 BIT(4)
+#define EDMA_INT5 BIT(5)
+#define EDMA_INT6 BIT(6)
+#define EDMA_INT7 BIT(7)
+/* DMA read channel 0~7 irq*/
+#define EDMA_INT8 BIT(8)
+#define EDMA_INT9 BIT(9)
+#define EDMA_INT10 BIT(10)
+#define EDMA_INT11 BIT(11)
+#define EDMA_INT12 BIT(12)
+#define EDMA_INT13 BIT(13)
+#define EDMA_INT14 BIT(14)
+#define EDMA_INT15 BIT(15)
+#define DMA_READ_INT GENMASK(11, 8)
+
+#define K1X_PHY_AHB_IRQENABLE_SET_MSI 0x0014
+
+#define PCIECTRL_K1X_CONF_DEVICE_CMD 0x0000
+#define LTSSM_EN BIT(6)
+/* Perst input value in ep mode */
+#define PCIE_PERST_IN BIT(7)
+#define PCIE_AUX_PWR_DET BIT(9)
+/* Perst GPIO en in RC mode 1: perst# low, 0: perst# high */
+#define PCIE_RC_PERST BIT(12)
+/* Wake# GPIO in EP mode 1: Wake# low, 0: Wake# high */
+#define PCIE_EP_WAKE BIT(13)
+#define APP_HOLD_PHY_RST BIT(30)
+/* BIT31 0: EP, 1: RC*/
+#define DEVICE_TYPE_RC BIT(31)
+
+#define PCIE_CTRL_LOGIC 0x0004
+#define PCIE_IGNORE_PERSTN BIT(2)
+
+#define K1X_PHY_AHB_LINK_STS 0x0004
+#define SMLH_LINK_UP BIT(1)
+#define RDLH_LINK_UP BIT(12)
+#define PCIE_CLIENT_DEBUG_LTSSM_MASK GENMASK(11, 6)
+#define PCIE_CLIENT_DEBUG_LTSSM_L1 (BIT(10) | BIT(8))
+#define PCIE_CLIENT_DEBUG_LTSSM_L2 (BIT(10) | BIT(8) | BIT(6))
+
+#define ADDR_INTR_STATUS1 0x0018
+#define ADDR_INTR_ENABLE1 0x001C
+#define MSI_INT BIT(0)
+#define MSIX_INT GENMASK(8, 1)
+
+#define ADDR_MSI_RECV_CTRL 0x0080
+#define MSI_MON_EN BIT(0)
+#define MSIX_MON_EN GENMASK(8, 1)
+#define MSIX_AFIFO_FULL BIT(30)
+#define MSIX_AFIFO_EMPTY BIT(29)
+#define ADDR_MSI_RECV_ADDR0 0x0084
+#define ADDR_MSIX_MON_MASK 0x0088
+#define ADDR_MSIX_MON_BASE0 0x008c
+
+#define ADDR_MON_FIFO_DATA0 0x00b0
+#define ADDR_MON_FIFO_DATA1 0x00b4
+#define FIFO_EMPTY 0xFFFFFFFF
+#define FIFO_LEN 32
+#define INT_VEC_MASK GENMASK(7, 0)
+
+#define EXP_CAP_ID_OFFSET 0x70
+
+#define PCIECTRL_K1X_CONF_INTX_ASSERT 0x0124
+#define PCIECTRL_K1X_CONF_INTX_DEASSERT 0x0128
+
+/*RC write config 0xD28 offset register which equal with ELBI offset 0x028 addr*/
+#define PCIE_ELBI_EP_DMA_IRQ_STATUS 0x028
+#define PC_TO_EP_INT (0x3fffffff)
+
+#define PCIE_ELBI_EP_DMA_IRQ_MASK 0x02c
+#define PC_TO_EP_INT_MASK (0x3fffffff)
+
+#define PCIE_ELBI_EP_MSI_REASON 0x018
+
+#define PCIE_LINK_IS_L2(x) \
+ (((x) & PCIE_CLIENT_DEBUG_LTSSM_MASK) == PCIE_CLIENT_DEBUG_LTSSM_L2)
+struct k1x_pcie {
+ struct dw_pcie *pci;
+ void __iomem *base; /* DT k1x_conf */
+ void __iomem *elbi_base;
+ void __iomem *dma_base;
+ void __iomem *phy_ahb; /* DT phy_ahb */
+ void __iomem *phy_addr; /* DT phy_addr */
+ void __iomem *conf0_addr; /* DT conf0_addr */
+ void __iomem *phy0_addr; /* DT phy0_addr */
+ u32 pcie_rcal;
+ int phy_count; /* DT phy-names count */
+ struct phy **phy;
+ int pcie_init_before_kernel;
+ int port_id;
+ int num_lanes;
+ int link_gen;
+ bool link_is_up;
+ struct irq_domain *irq_domain;
+ enum dw_pcie_device_mode mode;
+ struct page *msi_page;
+ struct page *msix_page;
+ dma_addr_t msix_addr;
+ struct clk *clk_pcie; /*include master slave slave_lite clk*/
+ struct clk *clk_master;
+ struct clk *clk_slave;
+ struct clk *clk_slave_lite;
+ struct reset_control *reset;
+
+ struct gpio_desc *perst_gpio; /* for PERST# in RC mode*/
+ int pwr_on_gpio;
+};
+
+struct k1x_pcie_of_data {
+ enum dw_pcie_device_mode mode;
+};
+
+#define to_k1x_pcie(x) dev_get_drvdata((x)->dev)
+
+static inline u32 k1x_pcie_readl_dma(struct k1x_pcie *pcie, u32 reg)
+{
+ return readl(pcie->dma_base + reg);
+}
+
+static inline void k1x_pcie_writel_dma(struct k1x_pcie *pcie, u32 reg, u32 val)
+{
+ writel(val, pcie->dma_base + reg);
+}
+
+static inline u32 k1x_pcie_readw_dma(struct k1x_pcie *pcie, u32 reg)
+{
+ return (u32)readw(pcie->dma_base + reg);
+}
+
+static inline void k1x_pcie_writew_dma(struct k1x_pcie *pcie, u32 reg, u32 val)
+{
+ writew((u16)val, pcie->dma_base + reg);
+}
+
+static inline u32 k1x_pcie_readl(struct k1x_pcie *pcie, u32 offset)
+{
+ return readl(pcie->base + offset);
+}
+
+static inline void k1x_pcie_writel(struct k1x_pcie *pcie, u32 offset,
+ u32 value)
+{
+ writel(value, pcie->base + offset);
+}
+
+static inline u32 k1x_pcie_readl_elbi(struct k1x_pcie *pcie, u32 reg)
+{
+ return readl(pcie->elbi_base + reg);
+}
+
+static inline void k1x_pcie_writel_elbi(struct k1x_pcie *pcie, u32 reg, u32 val)
+{
+ writel(val, pcie->elbi_base + reg);
+}
+
+static inline u32 k1x_pcie_phy_ahb_readl(struct k1x_pcie *pcie, u32 offset)
+{
+ return readl(pcie->phy_ahb + offset);
+}
+
+static inline void k1x_pcie_phy_ahb_writel(struct k1x_pcie *pcie, u32 offset,
+ u32 value)
+{
+ writel(value, pcie->phy_ahb + offset);
+}
+
+static inline u32 k1x_pcie_phy_reg_readl(struct k1x_pcie *pcie, u32 offset)
+{
+ return readl(pcie->phy_addr + offset);
+}
+
+static inline void k1x_pcie_phy_reg_writel(struct k1x_pcie *pcie, u32 offset,
+ u32 value)
+{
+ writel(value, pcie->phy_addr + offset);
+}
+
+static inline u32 k1x_pcie_conf0_reg_readl(struct k1x_pcie *pcie, u32 offset)
+{
+ return readl(pcie->conf0_addr + offset);
+}
+
+static inline void k1x_pcie_conf0_reg_writel(struct k1x_pcie *pcie, u32 offset,
+ u32 value)
+{
+ writel(value, pcie->conf0_addr + offset);
+}
+
+static inline u32 k1x_pcie_phy0_reg_readl(struct k1x_pcie *pcie, u32 offset)
+{
+ return readl(pcie->phy0_addr + offset);
+}
+
+static inline void k1x_pcie_phy0_reg_writel(struct k1x_pcie *pcie, u32 offset,
+ u32 value)
+{
+ writel(value, pcie->phy0_addr + offset);
+}
+
+#define PCIE_REF_CLK_OUTPUT
+static int porta_init_done = 0;
+// wait porta rterm done
+void porta_rterm(struct k1x_pcie *k1x)
+{
+ int rd_data, count;
+ u32 val;
+
+ //REG32(PMUA_REG_BASE + 0x3CC) = 0x4000003f;
+ val = k1x_pcie_conf0_reg_readl(k1x, 0);
+ val = 0x4000003f;
+ k1x_pcie_conf0_reg_writel(k1x, 0 , val);
+
+ //REG32(PMUA_REG_BASE + 0x3CC) &= 0xbfffffff; // clear hold phy reset
+ val = k1x_pcie_conf0_reg_readl(k1x, 0);
+ val &= 0xbfffffff;
+ k1x_pcie_conf0_reg_writel(k1x, 0 , val);
+
+ // set refclk model
+ //REG32(0xC0B10000 + (0x17 << 2)) |= (0x1 << 10);
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x17 << 2));
+ val |= (0x1 << 10);
+ k1x_pcie_phy0_reg_writel(k1x, (0x17 << 2), val);
+
+ //REG32(0xC0B10000 + (0x17 << 2)) &= ~(0x3 << 8);
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x17 << 2));
+ val &= ~(0x3 << 8);
+ k1x_pcie_phy0_reg_writel(k1x, (0x17 << 2), val);
+
+
+#ifndef PCIE_REF_CLK_OUTPUT
+ // receiver mode
+ //REG32(0xC0B10000 + (0x17 << 2)) |= 0x2 << 8;
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x17 << 2));
+ val |= 0x2 << 8;
+ k1x_pcie_phy0_reg_writel(k1x, (0x17 << 2), val);
+
+ //REG32(0xC0B10000 + (0x8 << 2)) &= ~(0x1 << 29);
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x8 << 2));
+ val &= ~(0x1 << 29);
+ k1x_pcie_phy0_reg_writel(k1x, (0x8 << 2), val);
+#ifdef PCIE_SEL_24M_REF_CLK
+ //REG32(0xC0B10000 + (0x12 << 2)) &= 0xffff0fff;
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x12 << 2));
+ val &= 0xffff0fff;
+ k1x_pcie_phy0_reg_writel(k1x, (0x12 << 2), val);
+
+ //REG32(0xC0B10000 + (0x12 << 2)) |= 0x00002000; // select 24Mhz refclock input pll_reg1[15:13]=2
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x12 << 2));
+ val |= 0x00002000;
+ k1x_pcie_phy0_reg_writel(k1x, (0x12 << 2), val);
+
+ //REG32(0xC0B10000 + (0x8 << 2)) |= 0x3 << 29; // rc_cal_reg2 0x68
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x8 << 2));
+ val |= 0x3 << 29;
+ k1x_pcie_phy0_reg_writel(k1x, (0x8 << 2), val);
+#elif PCIE_SEL_100M_REF_CLK
+ //REG32(0xC0B10000 + (0x8 << 2)) |= 0x1 << 30; // rc_cal_reg2 0x48
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x8 << 2));
+ val |= 0x1 << 30;
+ k1x_pcie_phy0_reg_writel(k1x, (0x8 << 2), val);
+#endif
+ //REG32(0xC0B10000 + (0x14 << 2)) |= (0x1 << 3); // pll_reg9[3] en_rterm,only enable in receiver mode
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x14 << 2));
+ val |= (0x1 << 3);
+ k1x_pcie_phy0_reg_writel(k1x, (0x14 << 2), val);
+#else
+ // driver mode
+ //REG32(0xC0B10000 + (0x17 << 2)) |= 0x1 << 8;
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x17 << 2));
+ val |= 0x1 << 8;
+ k1x_pcie_phy0_reg_writel(k1x, (0x17 << 2), val);
+
+ //REG32(0xC0B10000 + 0x400 + (0x17 << 2)) |= 0x1 << 8;
+ val = k1x_pcie_phy0_reg_readl(k1x, 0x400 + (0x17 << 2));
+ val |= 0x1 << 8;
+ k1x_pcie_phy0_reg_writel(k1x, 0x400 + (0x17 << 2), val);
+
+ //REG32(0xC0B10000 + (0x12 << 2)) &= 0xffff0fff;
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x12 << 2));
+ val &= 0xffff0fff;
+ k1x_pcie_phy0_reg_writel(k1x, (0x12 << 2), val);
+
+ //REG32(0xC0B10000 + (0x12 << 2)) |= 0x00002000; // select 24Mhz refclock input pll_reg1[15:13]=2
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x12 << 2));
+ val |= 0x00002000;
+ k1x_pcie_phy0_reg_writel(k1x, (0x12 << 2), val);
+
+ //REG32(0xC0B10000 + (0x13 << 2)) |= (0x1 << 4); // pll_reg5[4] of lane0, enable refclk_100_n/p 100Mhz output
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x13 << 2));
+ val |= (0x1 << 4);
+ k1x_pcie_phy0_reg_writel(k1x, (0x13 << 2), val);
+
+ //// REG32(0xC0B10000+(0x14<<2)) |= (0x1<<3);//pll_reg9[3] en_rterm,only enable in receiver mode
+#endif
+
+ //REG32(0xC0B10000 + (0x12 << 2)) &= 0xfff0ffff; // pll_reg1 of lane0, disable ssc pll_reg4[3:0]=4'h0
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x12 << 2));
+ val &= 0xfff0ffff;
+ k1x_pcie_phy0_reg_writel(k1x, (0x12 << 2), val);
+
+ //REG32(0xC0B10000 + (0x02 << 2)) = 0x00000B78; // PU_ADDR_CLK_CFG of lane0
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x02 << 2));
+ val = 0x00000B78;
+ k1x_pcie_phy0_reg_writel(k1x, (0x02 << 2), val);
+
+ //REG32(0xC0B10000 + (0x06 << 2)) = 0x00000400; // force rcv done
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x06 << 2));
+ val = 0x00000400;
+ k1x_pcie_phy0_reg_writel(k1x, (0x06 << 2), val);
+ printk("Now waiting portA resister tuning done...\n");
+
+ // force PCIE mpu_u3/pu_rx_lfps
+ //REG32(PCIE_PUPHY_REG_BASE + 0x6 * 4) |= (0x1 << 17) | (0x1 << 15);
+ val = k1x_pcie_phy_reg_readl(k1x, (0x6 * 4));
+ val |= ((0x1 << 17) | (0x1 << 15));
+ k1x_pcie_phy_reg_writel(k1x, (0x6 * 4), val);
+
+ // wait pm0 rterm done
+ count = 0;
+ do
+ {
+ //rd_data = REG32(0xC0B10000 + 0x21 * 4);
+ rd_data = k1x_pcie_phy0_reg_readl(k1x, (0x21 * 4));
+ if (count++ > 5000) {
+ printk(KERN_WARNING "read pcie0 phy rd_data time out.\n");
+ break;
+ }
+ } while (((rd_data >> 10) & 0x1) == 0); // waiting PCIe portA readonly_reg2[2] r_tune_done==1
+}
+
+// force rterm value to porta/b/c
+void rterm_force(struct k1x_pcie *k1x, u32 pcie_rcal)
+{
+ int i, lane;
+ u32 val = 0;
+
+ lane = k1x->num_lanes;
+ printk("pcie_rcal = 0x%08x\n", pcie_rcal);
+ printk("pcie port id = %d, lane num = %d\n", k1x->port_id, lane);
+
+ // 2.write pma0 rterm value LSB[3:0](read0nly1[3:0]) to lane0/1 rx_reg1
+ for (i = 0; i < lane; i++)
+ {
+ val = k1x_pcie_phy_reg_readl(k1x, ((0x14 << 2) + 0x400 * i));
+ val |= ((pcie_rcal & 0xf) << 8);
+ k1x_pcie_phy_reg_writel(k1x, ((0x14 << 2) + 0x400 * i), val);
+ }
+ // 3.set lane0/1 rx_reg4 bit5=0
+ for (i = 0; i < lane; i++)
+ {
+ val = k1x_pcie_phy_reg_readl(k1x, ((0x15 << 2) + 0x400 * i));
+ val &= ~(1 << 5);
+ k1x_pcie_phy_reg_writel(k1x, ((0x15 << 2) + 0x400 * i), val);
+ }
+
+ // 4.write pma0 rterm value MSB[7:4](readonly1[7:4]) to lane0/1 tx_reg1[7:4]
+ for (i = 0; i < lane; i++)
+ {
+ val = k1x_pcie_phy_reg_readl(k1x, ((0x19 << 2) + 0x400 * i));
+ val |= ((pcie_rcal >> 4) & 0xf) << 12;
+ k1x_pcie_phy_reg_writel(k1x, ((0x19 << 2) + 0x400 * i), val);
+ }
+
+ // 5.set lane0/1 tx_reg3 bit1=1
+ for (i = 0; i < lane; i++)
+ {
+ val = k1x_pcie_phy_reg_readl(k1x, ((0x19 << 2) + 0x400 * i));
+ val |= (1 << 25);
+ k1x_pcie_phy_reg_writel(k1x, ((0x19 << 2) + 0x400 * i), val);
+ }
+
+ // 6.adjust rc calrefclk freq
+#ifndef PCIE_REF_CLK_OUTPUT
+ //REG32(PCIE_PUPHY_REG_BASE + (0x8 << 2)) &= ~(0x1 << 29);
+ val = k1x_pcie_phy_reg_readl(k1x, (0x8 << 2));
+ val &= ~(0x1 << 29);
+ k1x_pcie_phy_reg_writel(k1x, (0x8 << 2), val);
+#ifdef PCIE_SEL_24M_REF_CLK
+ //REG32(PCIE_PUPHY_REG_BASE + (0x8 << 2)) |= 0x3 << 29; // rc_cal_reg2 0x68
+ val = k1x_pcie_phy_reg_readl(k1x, (0x8 << 2));
+ val |= 0x3 << 29;
+ k1x_pcie_phy_reg_writel(k1x, (0x8 << 2), val);
+#elif PCIE_SEL_100M_REF_CLK
+ //REG32(PCIE_PUPHY_REG_BASE + (0x8 << 2)) |= 0x1 << 30; // rc_cal_reg2 0x48
+ val = k1x_pcie_phy_reg_readl(k1x, (0x8 << 2));
+ val |= 0x1 << 30;
+ k1x_pcie_phy_reg_writel(k1x, (0x8 << 2), val);
+#endif
+#else
+ //REG32(PCIE_PUPHY_REG_BASE + (0x8 << 2)) |= 0x3 << 29;
+ val = k1x_pcie_phy_reg_readl(k1x, (0x8 << 2));
+ val |= 0x3 << 29;
+ k1x_pcie_phy_reg_writel(k1x, (0x8 << 2), val);
+#endif
+
+ // 7.set lane0/1 rc_cal_reg1[6]=1
+ for (i = 0; i < lane; i++)
+ {
+ val = k1x_pcie_phy_reg_readl(k1x, ((0x8 << 2) + 0x400 * i));
+ val &= ~(1 << 22);
+ k1x_pcie_phy_reg_writel(k1x, ((0x8 << 2) + 0x400 * i), val);
+ }
+ for (i = 0; i < lane; i++)
+ {
+ val = k1x_pcie_phy_reg_readl(k1x, ((0x8 << 2) + 0x400 * i));
+ val |= (1 << 22);
+ k1x_pcie_phy_reg_writel(k1x, ((0x8 << 2) + 0x400 * i), val);
+ }
+
+ // release forc PCIE mpu_u3/pu_rx_lfps
+ val = k1x_pcie_phy_reg_readl(k1x, 0x6 * 4);
+ val &= 0xFFFD7FFF;
+ k1x_pcie_phy_reg_writel(k1x, 0x6 * 4, val);
+}
+
+static int init_phy(struct k1x_pcie *k1x)
+{
+ u32 rd_data, pcie_rcal;
+ u32 val = 0;
+ int count;
+
+ printk("Now init Rterm...\n");
+ printk("pcie prot id = %d, porta_init_done = %d\n", k1x->port_id, porta_init_done);
+ if (k1x->port_id != 0) {
+ if (porta_init_done == 0) {
+ porta_rterm(k1x);
+ //pcie_rcal = REG32(0xC0B10000 + (0x21 << 2));
+ pcie_rcal = k1x_pcie_phy0_reg_readl(k1x, (0x21 << 2));
+
+ //REG32(PMUA_REG_BASE + 0x3CC) &= ~0x4000003f;
+ val = k1x_pcie_conf0_reg_readl(k1x, 0);
+ val &= ~0x4000003f;
+ k1x_pcie_conf0_reg_writel(k1x, 0, val);
+ } else {
+ //pcie_rcal = REG32(0xC0B10000 + (0x21 << 2));
+ pcie_rcal = k1x_pcie_phy0_reg_readl(k1x, (0x21 << 2));
+ }
+ } else {
+ count = 0;
+ do {
+ //rd_data = REG32(0xC0B10000 + 0x21 * 4);
+ rd_data = k1x_pcie_phy0_reg_readl(k1x, (0x21 * 4));
+ if (count++ > 5000) {
+ printk(KERN_WARNING "read pcie0 phy rd_data time out.\n");
+ break;
+ }
+ } while (((rd_data >> 10) & 0x1) == 0);
+ //pcie_rcal = REG32(0xC0B10000 + (0x21 << 2));
+ pcie_rcal = k1x_pcie_phy0_reg_readl(k1x, (0x21 << 2));
+ }
+
+ k1x->pcie_rcal = pcie_rcal;
+ rterm_force(k1x, pcie_rcal);
+
+ printk("Now int init_puphy...\n");
+ val = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ val &= 0xbfffffff;
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, val);
+
+ // set refclk model
+ val = k1x_pcie_phy_reg_readl(k1x, (0x17 << 2));
+ val |= (0x1 << 10);
+ k1x_pcie_phy_reg_writel(k1x, (0x17 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, (0x17 << 2));
+ val &= ~(0x3 << 8);
+ k1x_pcie_phy_reg_writel(k1x, (0x17 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, 0x400 + (0x17 << 2));
+ val |= (0x1 << 10);
+ k1x_pcie_phy_reg_writel(k1x, 0x400 + (0x17 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, 0x400 + (0x17 << 2));
+ val &= ~(0x3 << 8);
+ k1x_pcie_phy_reg_writel(k1x, 0x400+ (0x17 << 2), val);
+#ifndef PCIE_REF_CLK_OUTPUT
+ // receiver mode
+ REG32(PCIE_PUPHY_REG_BASE + (0x17 << 2)) |= 0x2 << 8;
+ REG32(PCIE_PUPHY_REG_BASE + 0x400 + (0x17 << 2)) |= 0x2 << 8;
+#ifdef PCIE_SEL_24M_REF_CLK
+ val = k1x_pcie_phy_reg_readl(k1x, (0x12 << 2));
+ val &= 0xffff0fff;
+ k1x_pcie_phy_reg_writel(k1x, (0x12 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, (0x12 << 2));
+ val |= 0x00002000;
+ k1x_pcie_phy_reg_writel(k1x, (0x12 << 2), val);
+#endif
+#else
+ // driver mode
+ val = k1x_pcie_phy_reg_readl(k1x, (0x17 << 2));
+ val |= 0x1 << 8;
+ k1x_pcie_phy_reg_writel(k1x, (0x17 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, 0x400 + (0x17 << 2));
+ val |= 0x1 << 8;
+ k1x_pcie_phy_reg_writel(k1x, 0x400 + (0x17 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, (0x12 << 2));
+ val &= 0xffff0fff;
+ k1x_pcie_phy_reg_writel(k1x, (0x12 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, (0x12 << 2));
+ val |= 0x00002000;
+ k1x_pcie_phy_reg_writel(k1x, (0x12 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, (0x13 << 2));
+ val |= (0x1 << 4);
+ k1x_pcie_phy_reg_writel(k1x, (0x13 << 2), val);
+
+ if (k1x->port_id == 0x0) {
+ //REG32(0xC0B10000+(0x14<<2)) |= (0x1<<3);//pll_reg9[3] en_rterm,only enable in receiver mode
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x14 << 2));
+ val |= (0x1 << 3);
+ k1x_pcie_phy0_reg_writel(k1x, (0x14 << 2), val);
+ }
+#endif
+
+ // pll_reg1 of lane0, disable ssc pll_reg4[3:0]=4'h0
+ val = k1x_pcie_phy_reg_readl(k1x, (0x12 << 2));
+ val &= 0xfff0ffff;
+ k1x_pcie_phy_reg_writel(k1x, (0x12 << 2), val);
+
+ // PU_ADDR_CLK_CFG of lane0
+ val = k1x_pcie_phy_reg_readl(k1x, (0x02 << 2));
+ val = 0x00000B78;
+ k1x_pcie_phy_reg_writel(k1x, (0x02 << 2), val);
+
+ // PU_ADDR_CLK_CFG of lane1
+ val = k1x_pcie_phy_reg_readl(k1x, 0x400 + (0x02 << 2));
+ val = 0x00000B78;
+ k1x_pcie_phy_reg_writel(k1x, 0x400 + (0x02 << 2), val);
+
+ // force rcv done
+ val = k1x_pcie_phy_reg_readl(k1x, (0x06 << 2));
+ val = 0x00000400;
+ k1x_pcie_phy_reg_writel(k1x, (0x06 << 2), val);
+
+ // force rcv done
+ val = k1x_pcie_phy_reg_readl(k1x, 0x400 + (0x06 << 2));
+ val = 0x00000400;
+ k1x_pcie_phy_reg_writel(k1x, 0x400 + (0x06 << 2), val);
+
+ // waiting pll lock
+ printk("waiting pll lock...\n");
+ count = 0;
+ do
+ {
+ rd_data = k1x_pcie_phy_reg_readl(k1x, 0x8);
+ if (count++ > 5000) {
+ printk(KERN_WARNING "read pcie%d phy rd_data time out.\n", k1x->port_id);
+ break;
+ }
+ } while ((rd_data & 0x1) == 0);
+
+ if (k1x->port_id == 0)
+ porta_init_done = 0x1;
+ printk("Now finish init_puphy....\n");
+ return 0;
+}
+
+int is_pcie_init = 1;
+static int __init pcie_already_init(char *str)
+{
+ is_pcie_init = 1;
+ return 0;
+}
+__setup("pcie_init", pcie_already_init);
+
+static int k1x_pcie_link_up(struct dw_pcie *pci)
+{
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+ u32 reg = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_LINK_STS);
+
+ return (reg & RDLH_LINK_UP) && (reg & SMLH_LINK_UP);
+}
+
+static void k1x_pcie_stop_link(struct dw_pcie *pci)
+{
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+ u32 reg;
+
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ reg &= ~LTSSM_EN;
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, reg);
+}
+
+static int k1x_pcie_establish_link(struct dw_pcie *pci)
+{
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+ struct device *dev = pci->dev;
+ u32 reg;
+
+ if (k1x->mode == DW_PCIE_EP_TYPE) {
+ u32 cnt =0;
+ do {
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ if((reg & (1<<7)) == (1<<7))
+ break;
+ udelay(10);
+ cnt += 1;
+ }while(cnt < 300000);
+ }
+
+ if (dw_pcie_link_up(pci)) {
+ dev_err(dev, "link is already up\n");
+ return 0;
+ }
+
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ reg |= LTSSM_EN;
+ reg &= ~APP_HOLD_PHY_RST;
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, reg);
+
+ printk("ltssm enable\n");
+ return 0;
+}
+
+/*
+ * start of a new interrupt
+ * we don't need operate the h/w register here,
+ * but we must implement a function here, handle_edge_irq will call this func
+ *
+ */
+static void k1x_irq_ack(struct irq_data *data)
+{
+ return;
+}
+
+static void k1x_pci_msi_mask_irq(struct irq_data *data)
+{
+ struct msi_desc * desc = irq_data_get_msi_desc(data);
+
+ if(desc)
+ pci_msi_mask_irq(data);
+}
+
+static void k1x_pci_msi_unmask_irq(struct irq_data *data)
+{
+ struct msi_desc * desc = irq_data_get_msi_desc(data);
+
+ if(desc)
+ pci_msi_unmask_irq(data);
+}
+
+static struct irq_chip k1x_msi_irq_chip = {
+ .name = "PCI-MSI",
+ .irq_ack = k1x_irq_ack,
+ .irq_enable = k1x_pci_msi_unmask_irq,
+ .irq_disable = k1x_pci_msi_mask_irq,
+ .irq_mask = k1x_pci_msi_mask_irq,
+ .irq_unmask = k1x_pci_msi_unmask_irq,
+};
+
+static struct msi_domain_info k1x_pcie_msi_domain_info = {
+ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_PCI_MSIX | MSI_FLAG_MULTI_PCI_MSI),
+ .chip = &k1x_msi_irq_chip,
+};
+
+/* MSI int handler */
+irqreturn_t k1x_handle_msi_irq(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+ u32 val, addr;
+ int i;
+ irqreturn_t ret = IRQ_NONE;
+
+ val = k1x_pcie_phy_ahb_readl(k1x, ADDR_MSI_RECV_CTRL);
+ if(val & MSIX_AFIFO_FULL)
+ pr_err("AXI monitor FIFO FULL.\n");
+
+ for (i = 0; i < FIFO_LEN; i++) {
+
+ addr = k1x_pcie_phy_ahb_readl(k1x, ADDR_MON_FIFO_DATA0);
+ if (addr == FIFO_EMPTY)
+ break;
+ val = k1x_pcie_phy_ahb_readl(k1x, ADDR_MON_FIFO_DATA1);
+ /* in fact, val is the hwirq which equals with msi_data + msi vector */
+ val &= INT_VEC_MASK;
+
+ ret = IRQ_HANDLED;
+ generic_handle_domain_irq(pp->irq_domain, val);
+ }
+
+ return ret;
+}
+
+static void k1x_pcie_setup_msi_msg(struct irq_data *d, struct msi_msg *msg)
+{
+ struct dw_pcie_rp *pp = irq_data_get_irq_chip_data(d);
+ u64 msi_target;
+
+ msi_target = (u64)pp->msi_data;
+
+ msg->address_lo = lower_32_bits(msi_target);
+ msg->address_hi = upper_32_bits(msi_target);
+
+ msg->data = d->hwirq;
+
+ pr_debug("msi#%d address_hi %#x address_lo %#x\n",
+ (int)d->hwirq, msg->address_hi, msg->address_lo);
+}
+
+static int k1x_pcie_msi_set_affinity(struct irq_data *d,
+ const struct cpumask *mask, bool force)
+{
+ return -EINVAL;
+}
+
+static struct irq_chip k1x_pcie_msi_bottom_irq_chip = {
+ .name = "K1X-PCI-MSI",
+ .irq_compose_msi_msg = k1x_pcie_setup_msi_msg,
+ .irq_set_affinity = k1x_pcie_msi_set_affinity,
+};
+
+static int k1x_pcie_irq_domain_alloc(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs,
+ void *args)
+{
+ struct dw_pcie_rp *pp = domain->host_data;
+ unsigned long flags;
+ u32 i;
+ int bit;
+
+ raw_spin_lock_irqsave(&pp->lock, flags);
+
+ bit = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS,
+ order_base_2(nr_irqs));
+
+ raw_spin_unlock_irqrestore(&pp->lock, flags);
+
+ if (bit < 0)
+ return -ENOSPC;
+
+ for (i = 0; i < nr_irqs; i++)
+ irq_domain_set_info(domain, virq + i, bit + i,
+ pp->msi_irq_chip,
+ pp, handle_edge_irq,
+ NULL, NULL);
+
+ return 0;
+}
+
+static void k1x_pcie_irq_domain_free(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
+ struct dw_pcie_rp *pp = domain->host_data;
+ unsigned long flags;
+
+ raw_spin_lock_irqsave(&pp->lock, flags);
+
+ bitmap_release_region(pp->msi_irq_in_use, d->hwirq,
+ order_base_2(nr_irqs));
+
+ raw_spin_unlock_irqrestore(&pp->lock, flags);
+}
+
+static const struct irq_domain_ops k1x_pcie_msi_domain_ops = {
+ .alloc = k1x_pcie_irq_domain_alloc,
+ .free = k1x_pcie_irq_domain_free,
+};
+
+int k1x_pcie_allocate_domains(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pcie = to_dw_pcie_from_pp(pp);
+ struct fwnode_handle *fwnode = of_node_to_fwnode(pcie->dev->of_node);
+
+ pp->irq_domain = irq_domain_create_linear(fwnode, MAX_MSI_IRQS,
+ &k1x_pcie_msi_domain_ops, pp);
+ if (!pp->irq_domain) {
+ dev_err(pcie->dev, "Failed to create IRQ domain\n");
+ return -ENOMEM;
+ }
+
+ irq_domain_update_bus_token(pp->irq_domain, DOMAIN_BUS_NEXUS);
+ pp->msi_domain = pci_msi_create_irq_domain(fwnode,
+ &k1x_pcie_msi_domain_info,
+ pp->irq_domain);
+ if (!pp->msi_domain) {
+ dev_err(pcie->dev, "Failed to create MSI domain\n");
+ irq_domain_remove(pp->irq_domain);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void k1x_pcie_msix_addr_alloc(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+ struct device *dev = pci->dev;
+ u64 msi_target;
+ u32 reg;
+
+ k1x->msix_page = alloc_page(GFP_KERNEL);
+ k1x->msix_addr = dma_map_page(dev, k1x->msix_page, 0, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, k1x->msix_addr)) {
+ dev_err(dev, "Failed to map MSIX address\n");
+ __free_page(k1x->msix_page);
+ k1x->msix_page = NULL;
+ return;
+ }
+ msi_target = (u64)k1x->msix_addr;
+
+ pr_info("(u64)pp->msix_addr =%llx\n", (u64)k1x->msix_addr);
+ reg = k1x_pcie_phy_ahb_readl(k1x, ADDR_MSI_RECV_CTRL);
+ reg |= MSIX_MON_EN;
+ k1x_pcie_phy_ahb_writel(k1x, ADDR_MSI_RECV_CTRL, reg);
+ reg = k1x_pcie_phy_ahb_readl(k1x, ADDR_MSIX_MON_MASK);
+ reg |= 0xA;
+ k1x_pcie_phy_ahb_writel(k1x, ADDR_MSIX_MON_MASK, reg);
+ k1x_pcie_phy_ahb_writel(k1x, ADDR_MSIX_MON_BASE0, (lower_32_bits(msi_target) >> 2));
+}
+
+void k1x_pcie_msi_addr_alloc(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+ struct device *dev = pci->dev;
+ u64 msi_target;
+ u32 reg;
+
+ k1x->msi_page = alloc_page(GFP_KERNEL);
+ pp->msi_data = dma_map_page(dev, k1x->msi_page, 0, PAGE_SIZE,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, pp->msi_data)) {
+ dev_err(dev, "Failed to map MSI data\n");
+ __free_page(k1x->msi_page);
+ k1x->msi_page = NULL;
+ return;
+ }
+ msi_target = (u64)pp->msi_data;
+
+ pr_info("(u64)pp->msi_data =%llx\n", (u64)pp->msi_data);
+ /* Program the msi_data */
+ dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_LO, lower_32_bits(msi_target));
+ dw_pcie_writel_dbi(pci, PCIE_MSI_ADDR_HI, upper_32_bits(msi_target));
+
+ reg = k1x_pcie_phy_ahb_readl(k1x, ADDR_MSI_RECV_CTRL);
+ reg |= MSI_MON_EN;
+ k1x_pcie_phy_ahb_writel(k1x, ADDR_MSI_RECV_CTRL, reg);
+ k1x_pcie_phy_ahb_writel(k1x, ADDR_MSI_RECV_ADDR0, (lower_32_bits(msi_target) >> 2));
+}
+
+static void k1x_pcie_enable_msi_interrupts(struct k1x_pcie *k1x)
+{
+ u32 reg;
+
+ reg = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_IRQENABLE_SET_MSI);
+ reg |= MSI;
+ k1x_pcie_phy_ahb_writel(k1x, K1X_PHY_AHB_IRQENABLE_SET_MSI, reg);
+
+ reg = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_IRQENABLE_SET_INTX);
+ reg |= LEG_EP_INTERRUPTS;
+ k1x_pcie_phy_ahb_writel(k1x, K1X_PHY_AHB_IRQENABLE_SET_INTX, reg);
+
+ reg = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_IRQ_EN);
+ reg |= IRQ_EN;
+ k1x_pcie_phy_ahb_writel(k1x, K1X_PHY_AHB_IRQ_EN, reg);
+
+ reg = k1x_pcie_phy_ahb_readl(k1x, ADDR_INTR_ENABLE1);
+ reg |= (MSI_INT | MSIX_INT);
+ k1x_pcie_phy_ahb_writel(k1x, ADDR_INTR_ENABLE1, reg);
+}
+
+static void k1x_pcie_enable_wrapper_interrupts(struct k1x_pcie *k1x)
+{
+ u32 reg;
+
+ reg = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_IRQENABLE_SET_MSI);
+ reg |= PCIE_REMOTE_INTERRUPT | DMA_READ_INT;
+ k1x_pcie_phy_ahb_writel(k1x, K1X_PHY_AHB_IRQENABLE_SET_MSI, reg);
+
+ reg = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_IRQ_EN);
+ reg |= IRQ_EN;
+ k1x_pcie_phy_ahb_writel(k1x, K1X_PHY_AHB_IRQ_EN, reg);
+
+ reg = k1x_pcie_readl_elbi(k1x, PCIE_ELBI_EP_DMA_IRQ_MASK);
+ reg |= PC_TO_EP_INT_MASK;
+ k1x_pcie_writel_elbi(k1x, PCIE_ELBI_EP_DMA_IRQ_MASK, reg);
+}
+
+int k1x_pcie_enable_clocks(struct k1x_pcie *k1x)
+{
+ struct device *dev = k1x->pci->dev;
+ int err;
+
+ err = clk_prepare_enable(k1x->clk_master);
+ if (err) {
+ dev_err(dev, "unable to enable k1x->clk_master clock\n");
+ return err;
+ }
+
+ err = clk_prepare_enable(k1x->clk_slave);
+ if (err) {
+ dev_err(dev, "unable to enable k1x->clk_slave clock\n");
+ goto err_clk_master_pcie;
+ }
+
+ err = clk_prepare_enable(k1x->clk_slave_lite);
+ if (err) {
+ dev_err(dev, "unable to enable k1x->clk_slave_lite clock\n");
+ goto err_clk_slave_pcie;
+ }
+
+ return 0;
+
+ err_clk_slave_pcie:
+ clk_disable_unprepare(k1x->clk_slave);
+ err_clk_master_pcie:
+ clk_disable_unprepare(k1x->clk_master);
+ return err;
+}
+EXPORT_SYMBOL_GPL(k1x_pcie_enable_clocks);
+
+void k1x_pcie_disable_clocks(struct k1x_pcie *k1x)
+{
+
+ clk_disable_unprepare(k1x->clk_slave_lite);
+ clk_disable_unprepare(k1x->clk_slave);
+ clk_disable_unprepare(k1x->clk_master);
+}
+EXPORT_SYMBOL_GPL(k1x_pcie_disable_clocks);
+
+int k1x_pcie_wait_for_speed_change(struct dw_pcie *pci)
+{
+ struct device *dev = pci->dev;
+ u32 tmp;
+ unsigned int retries;
+
+ for (retries = 0; retries < 200; retries++) {
+ tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+ /* Test if the speed change finished. */
+ if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
+ return 0;
+ usleep_range(100, 1000);
+ }
+
+ dev_err(dev, "Speed change timeout\n");
+ return -ETIMEDOUT;
+}
+
+
+static int k1x_pcie_host_init(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+ u32 reg;
+
+ mdelay(100);
+ /* set Perst# gpio high state*/
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ reg &= ~PCIE_RC_PERST;
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, reg);
+
+ /* read the link status register, get the current speed */
+ reg = dw_pcie_readw_dbi(pci, EXP_CAP_ID_OFFSET + PCI_EXP_LNKSTA);
+ pr_info("Link up, Gen%i\n", reg & PCI_EXP_LNKSTA_CLS);
+
+ k1x_pcie_enable_msi_interrupts(k1x);
+
+ return 0;
+}
+
+static int k1x_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
+ irq_set_chip_data(irq, domain->host_data);
+
+ return 0;
+}
+
+static const struct irq_domain_ops intx_domain_ops = {
+ .map = k1x_pcie_intx_map,
+ .xlate = pci_irqd_intx_xlate,
+};
+
+static int k1x_pcie_init_irq_domain(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct device *dev = pci->dev;
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+ struct device_node *node = dev->of_node;
+ struct device_node *pcie_intc_node = of_get_next_child(node, NULL);
+
+ if (!pcie_intc_node) {
+ dev_err(dev, "No PCIe Intc node found\n");
+ return -ENODEV;
+ }
+
+ k1x->irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
+ &intx_domain_ops, pp);
+ if (!k1x->irq_domain) {
+ dev_err(dev, "Failed to get a INTx IRQ domain\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void k1x_pcie_msi_irq_handler(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct k1x_pcie *k1x;
+ struct dw_pcie *pci;
+ struct dw_pcie_rp *pp;
+ u32 reg;
+ u32 hwirq;
+ u32 virq;
+
+ chained_irq_enter(chip, desc);
+
+ pp = irq_desc_get_handler_data(desc);
+ pci = to_dw_pcie_from_pp(pp);
+ k1x = to_k1x_pcie(pci);
+
+ reg = k1x_pcie_phy_ahb_readl(k1x, ADDR_INTR_STATUS1);
+ k1x_pcie_phy_ahb_writel(k1x, ADDR_INTR_STATUS1, reg);
+ if ((reg & MSI_INT) | (reg & MSIX_INT)) {
+ k1x_handle_msi_irq(pp);
+ }
+
+ /* legacy intx*/
+ reg = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_IRQSTATUS_INTX);
+ k1x_pcie_phy_ahb_writel(k1x, K1X_PHY_AHB_IRQSTATUS_INTX, reg);
+ reg = (reg & INTX_MASK) >> INTX_SHIFT;
+ if(reg)
+ pr_debug("legacy INTx interrupt received\n");
+
+ while(reg) {
+ hwirq = ffs(reg) - 1;
+ reg &= ~BIT(hwirq);
+ virq = irq_find_mapping(k1x->irq_domain, hwirq);
+ if(virq)
+ generic_handle_irq(virq);
+ else
+ pr_err("unexpected IRQ,INT%d\n", hwirq);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+int k1x_pcie_msi_host_init(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct device *dev = pci->dev;
+ int ret;
+
+ if (!pci_msi_enabled())
+ return -EINVAL;
+
+ pp->msi_irq_chip = &k1x_pcie_msi_bottom_irq_chip;
+
+ ret = k1x_pcie_allocate_domains(pp);
+ if (ret) {
+ dev_err(dev, "irq domain init failed\n");
+ return ret;
+ }
+
+ irq_set_chained_handler_and_data(pp->irq, k1x_pcie_msi_irq_handler, pp);
+ k1x_pcie_msi_addr_alloc(pp);
+ k1x_pcie_msix_addr_alloc(pp);
+
+ return ret;
+}
+
+static const struct dw_pcie_host_ops k1x_pcie_host_ops = {
+ .host_init = k1x_pcie_host_init,
+ .msi_host_init = k1x_pcie_msi_host_init,
+};
+
+static void (*k1x_pcie_irq_callback)(int);
+
+void k1x_pcie_set_irq_callback(void (*fn)(int))
+{
+ k1x_pcie_irq_callback = fn;
+}
+
+/* local cpu interrupt, vendor specific*/
+static irqreturn_t k1x_pcie_irq_handler(int irq, void *arg)
+{
+ struct k1x_pcie *k1x = arg;
+ int num;
+ u32 reg, reg_ahb;
+ __maybe_unused u8 chan;
+#if 0
+ DMA_DIRC dirc = DMA_READ;
+ DMA_INT_TYPE type;
+ BOOLEAN hasErr;
+ DMA_ERR errType;
+#endif
+
+ reg = k1x_pcie_readl_elbi(k1x, PCIE_ELBI_EP_DMA_IRQ_STATUS);
+ /* write 0 to clear the irq*/
+ k1x_pcie_writel_elbi(k1x, PCIE_ELBI_EP_DMA_IRQ_STATUS, 0);
+ reg_ahb = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_IRQSTATUS_MSI);
+
+repeat:
+ if (reg & PC_TO_EP_INT) {
+ pr_debug( "%s: irq = %d, reg=%x\n", __func__, irq, reg);
+ num = (reg & PC_TO_EP_INT);
+ if (k1x_pcie_irq_callback)
+ k1x_pcie_irq_callback(num);
+ }
+ if(reg_ahb & DMA_READ_INT) {
+ pr_debug( "dma read done irq reg=%x\n", reg);
+#if 0
+ /* chan: read channel 0/1/2/3 */
+ while (DmaQueryReadIntSrc(pci, &chan, &type)) {
+ BOOLEAN over = false;
+
+ /* Abort and Done interrupts are handled differently. */
+ if (type == DMA_INT_ABORT) {
+ /* Read error status registers to see what's the source of error. */
+ hasErr = DmaQueryErrSrc(pci, dirc, chan,
+ &errType);
+ DmaClrInt(pci, dirc, chan, DMA_INT_ABORT);
+ DmaClrInt(pci, dirc, chan, DMA_INT_DONE);
+
+ if (hasErr) {
+ printk("Xfer on %s channel %d encounterred hardware error: %d",
+ dirc == DMA_WRITE ? "write" : "read", chan, errType);
+
+ if (errType == DMA_ERR_WR || errType == DMA_ERR_RD){
+ /* Fatal errors. Can't recover. */
+ over = true;
+ printk("pcie DMA fatel error occurred...\n");
+ } else {
+ /* Try to request the DMA to continue processing. */
+ printk("Resuming DMA transfer from non-fatal error...");
+ }
+ } else {
+ printk("Xfer on %s channel %d aborted but no HW error found!",
+ dirc == DMA_WRITE ? "write" : "read", chan);
+ over = true;
+ }
+ } else {
+ pr_debug("dirc=%d chan=%d \n", dirc, chan);
+ DmaClrInt(pci, dirc, chan, DMA_INT_DONE);
+ over = true;
+ }
+
+ /* Xfer is aborted/done, invoking callback if desired */
+ if (over && k1x_pcie_irq_callback)
+ k1x_pcie_irq_callback(chan);
+ }
+#endif
+ }
+
+ reg = k1x_pcie_readl_elbi(k1x, PCIE_ELBI_EP_DMA_IRQ_STATUS);
+ if (reg & PC_TO_EP_INT) {
+ k1x_pcie_writel_elbi(k1x, PCIE_ELBI_EP_DMA_IRQ_STATUS, 0);
+ goto repeat;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void k1x_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+ enum pci_barno bar;
+
+ if (0) {
+ for (bar = BAR_0; bar <= BAR_5; bar++)
+ dw_pcie_ep_reset_bar(pci, bar);
+ }
+
+ k1x_pcie_enable_wrapper_interrupts(k1x);
+}
+
+__maybe_unused static void k1x_pcie_ep_enable_irq(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+
+ k1x_pcie_enable_wrapper_interrupts(k1x);
+}
+
+__maybe_unused static void k1x_pcie_ep_disable_irq(struct dw_pcie_ep *ep)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct k1x_pcie *k1x = to_k1x_pcie(pci);
+ u32 reg;
+
+ reg = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_IRQ_EN);
+ reg &= ~IRQ_EN;
+ k1x_pcie_phy_ahb_writel(k1x, K1X_PHY_AHB_IRQ_EN, reg);
+
+}
+
+static int k1x_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
+ enum pci_epc_irq_type type, u16 interrupt_num)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ //struct k1x_pcie *k1x = to_k1x_pcie(pci);
+
+ switch (type) {
+ case PCI_EPC_IRQ_LEGACY:
+ dev_err(pci->dev, "UNKNOWN IRQ type\n");
+ //k1x_pcie_raise_legacy_irq(k1x);
+ return -EINVAL;
+ case PCI_EPC_IRQ_MSI:
+ dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
+ break;
+ case PCI_EPC_IRQ_MSIX:
+ dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
+ break;
+ default:
+ dev_err(pci->dev, "UNKNOWN IRQ type\n");
+ }
+
+ return 0;
+}
+
+static const struct pci_epc_features k1x_pcie_epc_features = {
+ .linkup_notifier = false,
+ .msi_capable = true,
+ .msix_capable = true,
+};
+
+static const struct pci_epc_features*
+k1x_pcie_get_features(struct dw_pcie_ep *ep)
+{
+ return &k1x_pcie_epc_features;
+}
+
+static struct dw_pcie_ep_ops pcie_ep_ops = {
+ .ep_init = k1x_pcie_ep_init,
+ .raise_irq = k1x_pcie_raise_irq,
+ //.enable_irq = k1x_pcie_ep_enable_irq,
+ //.disable_irq = k1x_pcie_ep_disable_irq,
+ .get_features = k1x_pcie_get_features,
+};
+
+static int __init k1x_add_pcie_ep(struct k1x_pcie *k1x,
+ struct platform_device *pdev)
+{
+ int ret;
+ struct dw_pcie_ep *ep;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct dw_pcie *pci = k1x->pci;
+
+ ep = &pci->ep;
+ ep->ops = &pcie_ep_ops;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
+ k1x->elbi_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!k1x->elbi_base)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma");
+ k1x->dma_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!k1x->dma_base)
+ return -ENOMEM;
+
+ ret = dw_pcie_ep_init(ep);
+ if (ret) {
+ dev_err(dev, "failed to initialize endpoint\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init k1x_add_pcie_port(struct k1x_pcie *k1x,
+ struct platform_device *pdev)
+{
+ int ret;
+ struct dw_pcie *pci = k1x->pci;
+ struct dw_pcie_rp *pp = &pci->pp;
+ struct device *dev = pci->dev;
+ struct resource *res;
+ u32 reg;
+
+ /* set Perst# (fundamental reset) gpio low state*/
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ reg |= PCIE_RC_PERST;
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, reg);
+
+ pp->irq = platform_get_irq(pdev, 0);
+ if (pp->irq < 0) {
+ dev_err(dev, "missing IRQ resource\n");
+ return pp->irq;
+ }
+
+ ret = k1x_pcie_init_irq_domain(pp);
+ if (ret < 0)
+ return ret;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
+ pci->dbi_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!pci->dbi_base)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu");
+ pci->atu_base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!pci->atu_base)
+ return -ENOMEM;
+
+ pp->ops = &k1x_pcie_host_ops;
+
+ pp->num_vectors = MAX_MSI_IRQS;
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .start_link = k1x_pcie_establish_link,
+ .stop_link = k1x_pcie_stop_link,
+ .link_up = k1x_pcie_link_up,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static void k1x_pcie_disable_phy(struct k1x_pcie *k1x)
+{
+ u32 reg;
+
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ reg &= ~(0x3f);
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, reg);
+}
+
+static int k1x_pcie_enable_phy(struct k1x_pcie *k1x)
+{
+ u32 reg, val, rd_data;
+ int count;
+
+ printk("Now k1x_pcie_enable_phy in resume...\n");
+
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ reg |= 0x3f;
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, reg);
+
+ rterm_force(k1x, k1x->pcie_rcal);
+
+ val = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ val &= 0xbfffffff;
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, val);
+
+ // set refclk model
+ val = k1x_pcie_phy_reg_readl(k1x, (0x17 << 2));
+ val |= (0x1 << 10);
+ k1x_pcie_phy_reg_writel(k1x, (0x17 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, (0x17 << 2));
+ val &= ~(0x3 << 8);
+ k1x_pcie_phy_reg_writel(k1x, (0x17 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, 0x400 + (0x17 << 2));
+ val |= (0x1 << 10);
+ k1x_pcie_phy_reg_writel(k1x, 0x400 + (0x17 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, 0x400 + (0x17 << 2));
+ val &= ~(0x3 << 8);
+ k1x_pcie_phy_reg_writel(k1x, 0x400+ (0x17 << 2), val);
+#ifndef PCIE_REF_CLK_OUTPUT
+ // receiver mode
+ REG32(PCIE_PUPHY_REG_BASE + (0x17 << 2)) |= 0x2 << 8;
+ REG32(PCIE_PUPHY_REG_BASE + 0x400 + (0x17 << 2)) |= 0x2 << 8;
+#ifdef PCIE_SEL_24M_REF_CLK
+ val = k1x_pcie_phy_reg_readl(k1x, (0x12 << 2));
+ val &= 0xffff0fff;
+ k1x_pcie_phy_reg_writel(k1x, (0x12 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, (0x12 << 2));
+ val |= 0x00002000;
+ k1x_pcie_phy_reg_writel(k1x, (0x12 << 2), val);
+#endif
+#else
+ // driver mode
+ val = k1x_pcie_phy_reg_readl(k1x, (0x17 << 2));
+ val |= 0x1 << 8;
+ k1x_pcie_phy_reg_writel(k1x, (0x17 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, 0x400 + (0x17 << 2));
+ val |= 0x1 << 8;
+ k1x_pcie_phy_reg_writel(k1x, 0x400 + (0x17 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, (0x12 << 2));
+ val &= 0xffff0fff;
+ k1x_pcie_phy_reg_writel(k1x, (0x12 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, (0x12 << 2));
+ val |= 0x00002000;
+ k1x_pcie_phy_reg_writel(k1x, (0x12 << 2), val);
+
+ val = k1x_pcie_phy_reg_readl(k1x, (0x13 << 2));
+ val |= (0x1 << 4);
+ k1x_pcie_phy_reg_writel(k1x, (0x13 << 2), val);
+
+ if (k1x->port_id == 0x0) {
+ //REG32(0xC0B10000+(0x14<<2)) |= (0x1<<3);//pll_reg9[3] en_rterm,only enable in receiver mode
+ val = k1x_pcie_phy0_reg_readl(k1x, (0x14 << 2));
+ val |= (0x1 << 3);
+ k1x_pcie_phy0_reg_writel(k1x, (0x14 << 2), val);
+ }
+#endif
+
+ // pll_reg1 of lane0, disable ssc pll_reg4[3:0]=4'h0
+ val = k1x_pcie_phy_reg_readl(k1x, (0x12 << 2));
+ val &= 0xfff0ffff;
+ k1x_pcie_phy_reg_writel(k1x, (0x12 << 2), val);
+
+ // PU_ADDR_CLK_CFG of lane0
+ val = k1x_pcie_phy_reg_readl(k1x, (0x02 << 2));
+ val = 0x00000B78;
+ k1x_pcie_phy_reg_writel(k1x, (0x02 << 2), val);
+
+ // PU_ADDR_CLK_CFG of lane1
+ val = k1x_pcie_phy_reg_readl(k1x, 0x400 + (0x02 << 2));
+ val = 0x00000B78;
+ k1x_pcie_phy_reg_writel(k1x, 0x400 + (0x02 << 2), val);
+
+ // force rcv done
+ val = k1x_pcie_phy_reg_readl(k1x, (0x06 << 2));
+ val = 0x00000400;
+ k1x_pcie_phy_reg_writel(k1x, (0x06 << 2), val);
+
+ // force rcv done
+ val = k1x_pcie_phy_reg_readl(k1x, 0x400 + (0x06 << 2));
+ val = 0x00000400;
+ k1x_pcie_phy_reg_writel(k1x, 0x400 + (0x06 << 2), val);
+
+ // waiting pll lock
+ printk("waiting pll lock...\n");
+ count = 0;
+ do
+ {
+ rd_data = k1x_pcie_phy_reg_readl(k1x, 0x8);
+ if (count++ > 5000) {
+ printk(KERN_WARNING "read pcie%d phy rd_data time out.\n", k1x->port_id);
+ break;
+ }
+ } while ((rd_data & 0x1) == 0);
+
+ printk("Now finish enable phy....\n");
+
+ return 0;
+}
+#endif
+
+static const struct k1x_pcie_of_data k1x_pcie_rc_of_data = {
+ .mode = DW_PCIE_RC_TYPE,
+};
+
+static const struct k1x_pcie_of_data k1x_pcie_ep_of_data = {
+ .mode = DW_PCIE_EP_TYPE,
+};
+
+static const struct of_device_id of_k1x_pcie_match[] = {
+ {
+ .compatible = "k1x,dwc-pcie",
+ .data = &k1x_pcie_rc_of_data,
+ },
+ {
+ .compatible = "k1x,dwc-pcie-ep",
+ .data = &k1x_pcie_ep_of_data,
+ },
+ {},
+};
+
+static int k1x_power_on(struct k1x_pcie *k1x, int on)
+{
+ bool status = on ? 1 : 0;
+
+ if (k1x->pwr_on_gpio <= 0) {
+ return 0;
+ }
+
+ dev_info(k1x->pci->dev, "set power on gpio %d to %d\n", k1x->pwr_on_gpio, status);
+ gpio_direction_output(k1x->pwr_on_gpio, status);
+ return 0;
+}
+
+static int __init k1x_pcie_probe(struct platform_device *pdev)
+{
+ u32 reg;
+ int ret;
+ int irq;
+ void __iomem *base;
+ struct resource *res;
+ struct dw_pcie *pci;
+ struct k1x_pcie *k1x;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ const struct of_device_id *match;
+ const struct k1x_pcie_of_data *data;
+ enum dw_pcie_device_mode mode;
+
+ match = of_match_device(of_match_ptr(of_k1x_pcie_match), dev);
+ if (!match)
+ return -EINVAL;
+
+ data = (struct k1x_pcie_of_data *)match->data;
+ mode = (enum dw_pcie_device_mode)data->mode;
+
+ k1x = devm_kzalloc(dev, sizeof(*k1x), GFP_KERNEL);
+ if (!k1x)
+ return -ENOMEM;
+
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+ pci->ops = &dw_pcie_ops;
+
+ irq = platform_get_irq(pdev, 1);
+ if (irq < 0) {
+ dev_err(dev, "missing IRQ resource: %d\n", irq);
+ return irq;
+ }
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "k1x_conf");
+ base = devm_ioremap(dev, res->start, resource_size(res));
+ if (!base)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ahb");
+ k1x->phy_ahb = devm_ioremap(dev, res->start, resource_size(res));
+ if (!k1x->phy_ahb)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_addr");
+ k1x->phy_addr = devm_ioremap(dev, res->start, resource_size(res));
+ if (!k1x->phy_addr)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf0_addr");
+ k1x->conf0_addr = devm_ioremap(dev, res->start, resource_size(res));
+ if (!k1x->conf0_addr)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy0_addr");
+ k1x->phy0_addr = devm_ioremap(dev, res->start, resource_size(res));
+ if (!k1x->phy0_addr)
+ return -ENOMEM;
+
+ if (of_property_read_u32(np, "k1x,pcie-port", &k1x->port_id)) {
+ dev_err(dev, "Failed to get pcie's port id\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(np, "num-lanes", &k1x->num_lanes)) {
+ dev_warn(dev, "Failed to get pcie's port num-lanes.\n");
+ k1x->num_lanes = 1;
+ }
+ if((k1x->num_lanes < 1) || (k1x->num_lanes > 2)) {
+ dev_warn(dev, "configuration of num-lanes is invalid.\n");
+ k1x->num_lanes = 1;
+ }
+
+ k1x->pwr_on_gpio = of_get_named_gpio(np, "k1x,pwr_on", 0);
+ if (k1x->pwr_on_gpio <= 0) {
+ dev_info(dev, "has no power on gpio.\n");
+ }
+
+ /* pcie0 and usb use combo phy and reset */
+ if (k1x->port_id == 0) {
+ k1x->reset = devm_reset_control_array_get_shared(dev);
+ } else {
+ k1x->reset = devm_reset_control_get_optional(dev, NULL);
+ }
+ if (IS_ERR(k1x->reset)) {
+ dev_err(dev, "Failed to get pcie%d's resets\n", k1x->port_id);
+ return PTR_ERR(k1x->reset);
+ }
+
+ k1x->base = base;
+ k1x->pci = pci;
+ platform_set_drvdata(pdev, k1x);
+
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+
+ reset_control_deassert(k1x->reset);
+
+ init_phy(k1x);
+
+ k1x->pcie_init_before_kernel = is_pcie_init;
+ if (is_pcie_init == 0) {
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ reg &= ~LTSSM_EN;
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, reg);
+ }
+ k1x->link_gen = of_pci_get_max_link_speed(np);
+ if (k1x->link_gen < 0 || k1x->link_gen > 3)
+ k1x->link_gen = 3;
+
+ k1x->mode = mode;
+ switch (mode) {
+ case DW_PCIE_RC_TYPE:
+ if(!IS_ENABLED(CONFIG_PCI_K1X_HOST))
+ return -ENODEV;
+
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ reg |= (DEVICE_TYPE_RC | PCIE_AUX_PWR_DET);
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, reg);
+
+ reg = k1x_pcie_readl(k1x, PCIE_CTRL_LOGIC);
+ reg |= PCIE_IGNORE_PERSTN;
+ k1x_pcie_writel(k1x, PCIE_CTRL_LOGIC, reg);
+
+ /* power on the interface */
+ k1x_power_on(k1x, 1);
+
+ ret = k1x_add_pcie_port(k1x, pdev);
+ if (ret < 0)
+ goto err_clk;
+ break;
+ case DW_PCIE_EP_TYPE:
+ if(!IS_ENABLED(CONFIG_PCI_K1X_EP))
+ return -ENODEV;
+
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ reg &= ~DEVICE_TYPE_RC;
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, reg);
+
+ ret = k1x_add_pcie_ep(k1x, pdev);
+ if (ret < 0)
+ goto err_clk;
+ break;
+ default:
+ dev_err(dev, "INVALID device type %d\n", mode);
+ }
+
+ ret = devm_request_irq(dev, irq, k1x_pcie_irq_handler,
+ IRQF_SHARED, "k1x-pcie", k1x);
+ if (ret) {
+ dev_err(dev, "failed to request k1x-pcie irq\n");
+ goto err_clk;
+ }
+
+ return 0;
+
+err_clk:
+ k1x_pcie_disable_clocks(k1x);
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int k1x_pcie_wait_l2(struct k1x_pcie *k1x)
+{
+ u32 value;
+ u32 reg;
+ int err;
+
+ reg = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_IRQ_EN);
+ reg |= PME_TURN_OFF;
+ k1x_pcie_phy_ahb_writel(k1x, K1X_PHY_AHB_IRQ_EN, reg);
+ udelay(1);
+ reg = k1x_pcie_phy_ahb_readl(k1x, K1X_PHY_AHB_IRQ_EN);
+ reg &= ~PME_TURN_OFF;
+ k1x_pcie_phy_ahb_writel(k1x, K1X_PHY_AHB_IRQ_EN, reg);
+
+ err = readl_poll_timeout(k1x->phy_ahb + K1X_PHY_AHB_LINK_STS,
+ value, PCIE_LINK_IS_L2(value), 20,
+ jiffies_to_usecs(5 * HZ));
+ if (err) {
+ pr_err("PCIe link enter L2 timeout!\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int k1x_pcie_suspend(struct device *dev)
+{
+ struct k1x_pcie *k1x = dev_get_drvdata(dev);
+ struct dw_pcie *pci = k1x->pci;
+ u32 val;
+
+ if (k1x->mode != DW_PCIE_RC_TYPE)
+ return 0;
+
+ /* clear MSE */
+ val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
+ val &= ~PCI_COMMAND_MEMORY;
+ dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
+
+ return 0;
+}
+
+static int k1x_pcie_resume(struct device *dev)
+{
+ struct k1x_pcie *k1x = dev_get_drvdata(dev);
+ struct dw_pcie *pci = k1x->pci;
+ u32 val;
+
+ if (k1x->mode != DW_PCIE_RC_TYPE)
+ return 0;
+
+ /* set MSE */
+ val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
+ val |= PCI_COMMAND_MEMORY;
+ dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
+
+ return 0;
+}
+
+static int k1x_pcie_suspend_noirq(struct device *dev)
+{
+ struct k1x_pcie *k1x = dev_get_drvdata(dev);
+ struct dw_pcie *pci = k1x->pci;
+ u32 reg;
+
+ k1x->link_is_up = dw_pcie_link_up(pci);
+ dev_info(dev, "link is %s\n", k1x->link_is_up ? "up" : "down");
+
+ if (k1x->link_is_up)
+ k1x_pcie_wait_l2(k1x);
+
+ /* set Perst# (fundamental reset) gpio low state*/
+ reg = k1x_pcie_readl(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD);
+ reg |= PCIE_RC_PERST;
+ k1x_pcie_writel(k1x, PCIECTRL_K1X_CONF_DEVICE_CMD, reg);
+
+ k1x_pcie_stop_link(pci);
+ k1x_pcie_disable_phy(k1x);
+
+ /* power off the interface */
+ k1x_power_on(k1x, 0);
+
+ /* soft reset */
+ reg = k1x_pcie_readl(k1x, PCIE_CTRL_LOGIC);
+ reg |= (1 << 0);
+ k1x_pcie_writel(k1x, PCIE_CTRL_LOGIC, reg);
+
+ return 0;
+}
+
+static int k1x_pcie_resume_noirq(struct device *dev)
+{
+ struct k1x_pcie *k1x = dev_get_drvdata(dev);
+ struct dw_pcie *pci = k1x->pci;
+ struct dw_pcie_rp *pp = &pci->pp;
+ u32 reg;
+
+ /* soft no reset */
+ reg = k1x_pcie_readl(k1x, PCIE_CTRL_LOGIC);
+ reg &= ~(1 << 0);
+ k1x_pcie_writel(k1x, PCIE_CTRL_LOGIC, reg);
+
+ /* power on the interface */
+ k1x_power_on(k1x, 1);
+
+ k1x_pcie_enable_phy(k1x);
+ k1x_pcie_host_init(pp);
+ dw_pcie_setup_rc(pp);
+
+ if (k1x->link_is_up) {
+ k1x_pcie_establish_link(pci);
+ dw_pcie_wait_for_link(pci);
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops k1x_pcie_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(k1x_pcie_suspend, k1x_pcie_resume)
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(k1x_pcie_suspend_noirq,
+ k1x_pcie_resume_noirq)
+};
+
+static struct platform_driver k1x_pcie_driver = {
+ .driver = {
+ .name = "k1x-dwc-pcie",
+ .of_match_table = of_k1x_pcie_match,
+ .suppress_bind_attrs = true,
+ .pm = &k1x_pcie_pm_ops,
+ },
+};
+builtin_platform_driver_probe(k1x_pcie_driver, k1x_pcie_probe);
diff --git a/drivers/pci/controller/dwc/pcie-spacemit.c b/drivers/pci/controller/dwc/pcie-spacemit.c
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/drivers/pci/controller/dwc/pcie-spacemit.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Spacemit PCIe root complex driver
+ *
+ * Copyright (c) 2023, spacemit Corporation.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/pci.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/reset.h>
+
+#include "pcie-designware.h"
+
+#define PCIE_APP_TYPE 0x000
+#define DEVICE_TYPE_RC 0x4
+
+#define PCIE_APP_CTL 0x004
+#define CTL_LTSSM_ENABLE BIT(0)
+
+#define PCIE_APP_INTEN 0x100
+#define PCIE_APP_INTSTA 0x104
+#define PCIE_APP_STATE 0x200
+#define LTSSM_STATE_MASK GENMASK(5, 0)
+
+struct spacemit_pcie {
+ struct dw_pcie *pci;
+ void __iomem *app_base;
+ struct phy *phy;
+ struct clk *clk;
+ struct reset_control *reset;
+};
+
+#define to_spacemit_pcie(x) dev_get_drvdata((x)->dev)
+
+static int spacemit_pcie_start_link(struct dw_pcie *pci)
+{
+ struct spacemit_pcie *spacemit_pcie = to_spacemit_pcie(pci);
+ u64 ctl_reg = spacemit_pcie->app_base + PCIE_APP_CTL;
+
+ writel(readl(ctl_reg) | CTL_LTSSM_ENABLE, ctl_reg);
+ return 0;
+}
+
+static int spacemit_pcie_link_up(struct dw_pcie *pci)
+{
+ volatile u32 ltssm_state = 0;
+ struct spacemit_pcie *spacemit_pcie = to_spacemit_pcie(pci);
+ u64 state_reg = spacemit_pcie->app_base + PCIE_APP_STATE;
+
+ ltssm_state = readl(state_reg) & LTSSM_STATE_MASK;
+ return !!ltssm_state;
+}
+
+static irqreturn_t spacemit_pcie_irq_handler(int irq, void *arg)
+{
+ struct spacemit_pcie *spacemit_pcie = arg;
+ struct dw_pcie *pci = spacemit_pcie->pci;
+ struct dw_pcie_rp *pp = &pci->pp;
+ u64 intsta_reg = 0;
+ unsigned int status;
+
+ intsta_reg = spacemit_pcie->app_base + PCIE_APP_INTSTA;
+ status = readl(intsta_reg);
+
+ if (status & 3) {
+ BUG_ON(!IS_ENABLED(CONFIG_PCI_MSI));
+ dw_handle_msi_irq(pp);
+ }
+
+ writel(status, intsta_reg);
+
+ return IRQ_HANDLED;
+}
+
+static void spacemit_pcie_enable_interrupts(struct spacemit_pcie *spacemit_pcie)
+{
+ u64 inten_reg = spacemit_pcie->app_base + PCIE_APP_INTEN;
+}
+
+static int spacemit_pcie_host_init(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ struct spacemit_pcie *spacemit_pcie = to_spacemit_pcie(pci);
+ int ret;
+
+ if (!IS_ERR(spacemit_pcie->clk)) {
+ ret = clk_prepare_enable(spacemit_pcie->clk);
+ if (ret) {
+ printk(KERN_ERR "couldn't enable clk for pcie\n");
+ return ret;
+ }
+ }
+
+ reset_control_deassert(spacemit_pcie->reset);
+
+ //set rc mode
+ writel(DEVICE_TYPE_RC, spacemit_pcie->app_base + PCIE_APP_TYPE);
+ return 0;
+}
+
+static const struct dw_pcie_host_ops spacemit_pcie_host_ops = {
+ .host_init = spacemit_pcie_host_init,
+};
+
+static const struct dw_pcie_ops dw_pcie_ops = {
+ .link_up = spacemit_pcie_link_up,
+ .start_link = spacemit_pcie_start_link,
+};
+
+static int spacemit_pcie_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dw_pcie_rp *pp;
+ struct dw_pcie *pci;
+ struct spacemit_pcie *spacemit_pcie;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ spacemit_pcie = devm_kzalloc(dev, sizeof(*spacemit_pcie), GFP_KERNEL);
+ if (!spacemit_pcie)
+ return -ENOMEM;
+
+ pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+ if (!pci)
+ return -ENOMEM;
+
+ pci->dev = dev;
+ pci->ops = &dw_pcie_ops;
+ pp = &pci->pp;
+
+ spacemit_pcie->pci = pci;
+
+ pm_runtime_enable(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ goto err_pm_runtime_put;
+
+ spacemit_pcie->app_base = devm_platform_ioremap_resource_byname(pdev, "app");
+ if (IS_ERR(spacemit_pcie->app_base)) {
+ ret = PTR_ERR(spacemit_pcie->app_base);
+ goto err_pm_runtime_put;
+ }
+
+ spacemit_pcie->phy = devm_phy_optional_get(dev, "pciephy");
+ if (IS_ERR(spacemit_pcie->phy)) {
+ ret = PTR_ERR(spacemit_pcie->phy);
+ goto err_pm_runtime_put;
+ }
+
+ ret = phy_init(spacemit_pcie->phy);
+ if (ret)
+ goto err_pm_runtime_put;
+
+ spacemit_pcie->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(spacemit_pcie->clk)) {
+ dev_err(dev, "pcie clk not exist, skipped it\n");
+ }
+
+ spacemit_pcie->reset = devm_reset_control_array_get_optional_exclusive(&pdev->dev);
+ if (IS_ERR(spacemit_pcie->reset)) {
+ dev_err(dev, "Failed to get pcie's resets\n");
+ ret = PTR_ERR(spacemit_pcie->reset);
+ goto err_phy_exit;
+ }
+
+ platform_set_drvdata(pdev, spacemit_pcie);
+
+ pp->ops = &spacemit_pcie_host_ops;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret < 0) {
+ dev_err(dev, "failed to initialize host\n");
+ goto err_phy_exit;
+ }
+
+ return 0;
+
+err_phy_exit:
+ phy_exit(spacemit_pcie->phy);
+err_pm_runtime_put:
+ pm_runtime_put(dev);
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+
+static const struct of_device_id spacemit_pcie_of_match[] = {
+ { .compatible = "spacemit,k1-pro-pcie"},
+ {},
+};
+
+static struct platform_driver spacemit_pcie_driver = {
+ .probe = spacemit_pcie_probe,
+ .driver = {
+ .name = "spacemit-pcie",
+ .of_match_table = spacemit_pcie_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+builtin_platform_driver(spacemit_pcie_driver);
--
Armbian