From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Patrick Yavitz 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 --- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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