18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PCIe host controller driver for Axis ARTPEC-6 SoC
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Author: Niklas Cassel <niklas.cassel@axis.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on work done by Phil Edworthy <phil@edworthys.org>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/of_device.h>
148c2ecf20Sopenharmony_ci#include <linux/pci.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
168c2ecf20Sopenharmony_ci#include <linux/resource.h>
178c2ecf20Sopenharmony_ci#include <linux/signal.h>
188c2ecf20Sopenharmony_ci#include <linux/types.h>
198c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
208c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
218c2ecf20Sopenharmony_ci#include <linux/regmap.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "pcie-designware.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define to_artpec6_pcie(x)	dev_get_drvdata((x)->dev)
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cienum artpec_pcie_variants {
288c2ecf20Sopenharmony_ci	ARTPEC6,
298c2ecf20Sopenharmony_ci	ARTPEC7,
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct artpec6_pcie {
338c2ecf20Sopenharmony_ci	struct dw_pcie		*pci;
348c2ecf20Sopenharmony_ci	struct regmap		*regmap;	/* DT axis,syscon-pcie */
358c2ecf20Sopenharmony_ci	void __iomem		*phy_base;	/* DT phy */
368c2ecf20Sopenharmony_ci	enum artpec_pcie_variants variant;
378c2ecf20Sopenharmony_ci	enum dw_pcie_device_mode mode;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct artpec_pcie_of_data {
418c2ecf20Sopenharmony_ci	enum artpec_pcie_variants variant;
428c2ecf20Sopenharmony_ci	enum dw_pcie_device_mode mode;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic const struct of_device_id artpec6_pcie_of_match[];
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* ARTPEC-6 specific registers */
488c2ecf20Sopenharmony_ci#define PCIECFG				0x18
498c2ecf20Sopenharmony_ci#define  PCIECFG_DBG_OEN		BIT(24)
508c2ecf20Sopenharmony_ci#define  PCIECFG_CORE_RESET_REQ		BIT(21)
518c2ecf20Sopenharmony_ci#define  PCIECFG_LTSSM_ENABLE		BIT(20)
528c2ecf20Sopenharmony_ci#define  PCIECFG_DEVICE_TYPE_MASK	GENMASK(19, 16)
538c2ecf20Sopenharmony_ci#define  PCIECFG_CLKREQ_B		BIT(11)
548c2ecf20Sopenharmony_ci#define  PCIECFG_REFCLK_ENABLE		BIT(10)
558c2ecf20Sopenharmony_ci#define  PCIECFG_PLL_ENABLE		BIT(9)
568c2ecf20Sopenharmony_ci#define  PCIECFG_PCLK_ENABLE		BIT(8)
578c2ecf20Sopenharmony_ci#define  PCIECFG_RISRCREN		BIT(4)
588c2ecf20Sopenharmony_ci#define  PCIECFG_MODE_TX_DRV_EN		BIT(3)
598c2ecf20Sopenharmony_ci#define  PCIECFG_CISRREN		BIT(2)
608c2ecf20Sopenharmony_ci#define  PCIECFG_MACRO_ENABLE		BIT(0)
618c2ecf20Sopenharmony_ci/* ARTPEC-7 specific fields */
628c2ecf20Sopenharmony_ci#define  PCIECFG_REFCLKSEL		BIT(23)
638c2ecf20Sopenharmony_ci#define  PCIECFG_NOC_RESET		BIT(3)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define PCIESTAT			0x1c
668c2ecf20Sopenharmony_ci/* ARTPEC-7 specific fields */
678c2ecf20Sopenharmony_ci#define  PCIESTAT_EXTREFCLK		BIT(3)
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci#define NOCCFG				0x40
708c2ecf20Sopenharmony_ci#define  NOCCFG_ENABLE_CLK_PCIE		BIT(4)
718c2ecf20Sopenharmony_ci#define  NOCCFG_POWER_PCIE_IDLEACK	BIT(3)
728c2ecf20Sopenharmony_ci#define  NOCCFG_POWER_PCIE_IDLE		BIT(2)
738c2ecf20Sopenharmony_ci#define  NOCCFG_POWER_PCIE_IDLEREQ	BIT(1)
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#define PHY_STATUS			0x118
768c2ecf20Sopenharmony_ci#define  PHY_COSPLLLOCK			BIT(0)
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci#define PHY_TX_ASIC_OUT			0x4040
798c2ecf20Sopenharmony_ci#define  PHY_TX_ASIC_OUT_TX_ACK		BIT(0)
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#define PHY_RX_ASIC_OUT			0x405c
828c2ecf20Sopenharmony_ci#define  PHY_RX_ASIC_OUT_ACK		BIT(0)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	u32 val;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	regmap_read(artpec6_pcie->regmap, offset, &val);
898c2ecf20Sopenharmony_ci	return val;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u32 val)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	regmap_write(artpec6_pcie->regmap, offset, val);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic u64 artpec6_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 pci_addr)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
1008c2ecf20Sopenharmony_ci	struct pcie_port *pp = &pci->pp;
1018c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep = &pci->ep;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	switch (artpec6_pcie->mode) {
1048c2ecf20Sopenharmony_ci	case DW_PCIE_RC_TYPE:
1058c2ecf20Sopenharmony_ci		return pci_addr - pp->cfg0_base;
1068c2ecf20Sopenharmony_ci	case DW_PCIE_EP_TYPE:
1078c2ecf20Sopenharmony_ci		return pci_addr - ep->phys_base;
1088c2ecf20Sopenharmony_ci	default:
1098c2ecf20Sopenharmony_ci		dev_err(pci->dev, "UNKNOWN device type\n");
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci	return pci_addr;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic int artpec6_pcie_establish_link(struct dw_pcie *pci)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
1178c2ecf20Sopenharmony_ci	u32 val;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
1208c2ecf20Sopenharmony_ci	val |= PCIECFG_LTSSM_ENABLE;
1218c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic void artpec6_pcie_stop_link(struct dw_pcie *pci)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
1298c2ecf20Sopenharmony_ci	u32 val;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
1328c2ecf20Sopenharmony_ci	val &= ~PCIECFG_LTSSM_ENABLE;
1338c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic const struct dw_pcie_ops dw_pcie_ops = {
1378c2ecf20Sopenharmony_ci	.cpu_addr_fixup = artpec6_pcie_cpu_addr_fixup,
1388c2ecf20Sopenharmony_ci	.start_link = artpec6_pcie_establish_link,
1398c2ecf20Sopenharmony_ci	.stop_link = artpec6_pcie_stop_link,
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic void artpec6_pcie_wait_for_phy_a6(struct artpec6_pcie *artpec6_pcie)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct dw_pcie *pci = artpec6_pcie->pci;
1458c2ecf20Sopenharmony_ci	struct device *dev = pci->dev;
1468c2ecf20Sopenharmony_ci	u32 val;
1478c2ecf20Sopenharmony_ci	unsigned int retries;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	retries = 50;
1508c2ecf20Sopenharmony_ci	do {
1518c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
1528c2ecf20Sopenharmony_ci		val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
1538c2ecf20Sopenharmony_ci		retries--;
1548c2ecf20Sopenharmony_ci	} while (retries &&
1558c2ecf20Sopenharmony_ci		(val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE)));
1568c2ecf20Sopenharmony_ci	if (!retries)
1578c2ecf20Sopenharmony_ci		dev_err(dev, "PCIe clock manager did not leave idle state\n");
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	retries = 50;
1608c2ecf20Sopenharmony_ci	do {
1618c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
1628c2ecf20Sopenharmony_ci		val = readl(artpec6_pcie->phy_base + PHY_STATUS);
1638c2ecf20Sopenharmony_ci		retries--;
1648c2ecf20Sopenharmony_ci	} while (retries && !(val & PHY_COSPLLLOCK));
1658c2ecf20Sopenharmony_ci	if (!retries)
1668c2ecf20Sopenharmony_ci		dev_err(dev, "PHY PLL did not lock\n");
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void artpec6_pcie_wait_for_phy_a7(struct artpec6_pcie *artpec6_pcie)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct dw_pcie *pci = artpec6_pcie->pci;
1728c2ecf20Sopenharmony_ci	struct device *dev = pci->dev;
1738c2ecf20Sopenharmony_ci	u32 val;
1748c2ecf20Sopenharmony_ci	u16 phy_status_tx, phy_status_rx;
1758c2ecf20Sopenharmony_ci	unsigned int retries;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	retries = 50;
1788c2ecf20Sopenharmony_ci	do {
1798c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
1808c2ecf20Sopenharmony_ci		val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
1818c2ecf20Sopenharmony_ci		retries--;
1828c2ecf20Sopenharmony_ci	} while (retries &&
1838c2ecf20Sopenharmony_ci		(val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE)));
1848c2ecf20Sopenharmony_ci	if (!retries)
1858c2ecf20Sopenharmony_ci		dev_err(dev, "PCIe clock manager did not leave idle state\n");
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	retries = 50;
1888c2ecf20Sopenharmony_ci	do {
1898c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
1908c2ecf20Sopenharmony_ci		phy_status_tx = readw(artpec6_pcie->phy_base + PHY_TX_ASIC_OUT);
1918c2ecf20Sopenharmony_ci		phy_status_rx = readw(artpec6_pcie->phy_base + PHY_RX_ASIC_OUT);
1928c2ecf20Sopenharmony_ci		retries--;
1938c2ecf20Sopenharmony_ci	} while (retries && ((phy_status_tx & PHY_TX_ASIC_OUT_TX_ACK) ||
1948c2ecf20Sopenharmony_ci				(phy_status_rx & PHY_RX_ASIC_OUT_ACK)));
1958c2ecf20Sopenharmony_ci	if (!retries)
1968c2ecf20Sopenharmony_ci		dev_err(dev, "PHY did not enter Pn state\n");
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic void artpec6_pcie_wait_for_phy(struct artpec6_pcie *artpec6_pcie)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	switch (artpec6_pcie->variant) {
2028c2ecf20Sopenharmony_ci	case ARTPEC6:
2038c2ecf20Sopenharmony_ci		artpec6_pcie_wait_for_phy_a6(artpec6_pcie);
2048c2ecf20Sopenharmony_ci		break;
2058c2ecf20Sopenharmony_ci	case ARTPEC7:
2068c2ecf20Sopenharmony_ci		artpec6_pcie_wait_for_phy_a7(artpec6_pcie);
2078c2ecf20Sopenharmony_ci		break;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic void artpec6_pcie_init_phy_a6(struct artpec6_pcie *artpec6_pcie)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	u32 val;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
2168c2ecf20Sopenharmony_ci	val |=  PCIECFG_RISRCREN |	/* Receiver term. 50 Ohm */
2178c2ecf20Sopenharmony_ci		PCIECFG_MODE_TX_DRV_EN |
2188c2ecf20Sopenharmony_ci		PCIECFG_CISRREN |	/* Reference clock term. 100 Ohm */
2198c2ecf20Sopenharmony_ci		PCIECFG_MACRO_ENABLE;
2208c2ecf20Sopenharmony_ci	val |= PCIECFG_REFCLK_ENABLE;
2218c2ecf20Sopenharmony_ci	val &= ~PCIECFG_DBG_OEN;
2228c2ecf20Sopenharmony_ci	val &= ~PCIECFG_CLKREQ_B;
2238c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
2248c2ecf20Sopenharmony_ci	usleep_range(5000, 6000);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
2278c2ecf20Sopenharmony_ci	val |= NOCCFG_ENABLE_CLK_PCIE;
2288c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
2298c2ecf20Sopenharmony_ci	usleep_range(20, 30);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
2328c2ecf20Sopenharmony_ci	val |= PCIECFG_PCLK_ENABLE | PCIECFG_PLL_ENABLE;
2338c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
2348c2ecf20Sopenharmony_ci	usleep_range(6000, 7000);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
2378c2ecf20Sopenharmony_ci	val &= ~NOCCFG_POWER_PCIE_IDLEREQ;
2388c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic void artpec6_pcie_init_phy_a7(struct artpec6_pcie *artpec6_pcie)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct dw_pcie *pci = artpec6_pcie->pci;
2448c2ecf20Sopenharmony_ci	u32 val;
2458c2ecf20Sopenharmony_ci	bool extrefclk;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	/* Check if external reference clock is connected */
2488c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, PCIESTAT);
2498c2ecf20Sopenharmony_ci	extrefclk = !!(val & PCIESTAT_EXTREFCLK);
2508c2ecf20Sopenharmony_ci	dev_dbg(pci->dev, "Using reference clock: %s\n",
2518c2ecf20Sopenharmony_ci		extrefclk ? "external" : "internal");
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
2548c2ecf20Sopenharmony_ci	val |=  PCIECFG_RISRCREN |	/* Receiver term. 50 Ohm */
2558c2ecf20Sopenharmony_ci		PCIECFG_PCLK_ENABLE;
2568c2ecf20Sopenharmony_ci	if (extrefclk)
2578c2ecf20Sopenharmony_ci		val |= PCIECFG_REFCLKSEL;
2588c2ecf20Sopenharmony_ci	else
2598c2ecf20Sopenharmony_ci		val &= ~PCIECFG_REFCLKSEL;
2608c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
2618c2ecf20Sopenharmony_ci	usleep_range(10, 20);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
2648c2ecf20Sopenharmony_ci	val |= NOCCFG_ENABLE_CLK_PCIE;
2658c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
2668c2ecf20Sopenharmony_ci	usleep_range(20, 30);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
2698c2ecf20Sopenharmony_ci	val &= ~NOCCFG_POWER_PCIE_IDLEREQ;
2708c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void artpec6_pcie_init_phy(struct artpec6_pcie *artpec6_pcie)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	switch (artpec6_pcie->variant) {
2768c2ecf20Sopenharmony_ci	case ARTPEC6:
2778c2ecf20Sopenharmony_ci		artpec6_pcie_init_phy_a6(artpec6_pcie);
2788c2ecf20Sopenharmony_ci		break;
2798c2ecf20Sopenharmony_ci	case ARTPEC7:
2808c2ecf20Sopenharmony_ci		artpec6_pcie_init_phy_a7(artpec6_pcie);
2818c2ecf20Sopenharmony_ci		break;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void artpec6_pcie_assert_core_reset(struct artpec6_pcie *artpec6_pcie)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	u32 val;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
2908c2ecf20Sopenharmony_ci	switch (artpec6_pcie->variant) {
2918c2ecf20Sopenharmony_ci	case ARTPEC6:
2928c2ecf20Sopenharmony_ci		val |= PCIECFG_CORE_RESET_REQ;
2938c2ecf20Sopenharmony_ci		break;
2948c2ecf20Sopenharmony_ci	case ARTPEC7:
2958c2ecf20Sopenharmony_ci		val &= ~PCIECFG_NOC_RESET;
2968c2ecf20Sopenharmony_ci		break;
2978c2ecf20Sopenharmony_ci	}
2988c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void artpec6_pcie_deassert_core_reset(struct artpec6_pcie *artpec6_pcie)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	u32 val;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
3068c2ecf20Sopenharmony_ci	switch (artpec6_pcie->variant) {
3078c2ecf20Sopenharmony_ci	case ARTPEC6:
3088c2ecf20Sopenharmony_ci		val &= ~PCIECFG_CORE_RESET_REQ;
3098c2ecf20Sopenharmony_ci		break;
3108c2ecf20Sopenharmony_ci	case ARTPEC7:
3118c2ecf20Sopenharmony_ci		val |= PCIECFG_NOC_RESET;
3128c2ecf20Sopenharmony_ci		break;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci	artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
3158c2ecf20Sopenharmony_ci	usleep_range(100, 200);
3168c2ecf20Sopenharmony_ci}
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic int artpec6_pcie_host_init(struct pcie_port *pp)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
3218c2ecf20Sopenharmony_ci	struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	if (artpec6_pcie->variant == ARTPEC7) {
3248c2ecf20Sopenharmony_ci		pci->n_fts[0] = 180;
3258c2ecf20Sopenharmony_ci		pci->n_fts[1] = 180;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci	artpec6_pcie_assert_core_reset(artpec6_pcie);
3288c2ecf20Sopenharmony_ci	artpec6_pcie_init_phy(artpec6_pcie);
3298c2ecf20Sopenharmony_ci	artpec6_pcie_deassert_core_reset(artpec6_pcie);
3308c2ecf20Sopenharmony_ci	artpec6_pcie_wait_for_phy(artpec6_pcie);
3318c2ecf20Sopenharmony_ci	dw_pcie_setup_rc(pp);
3328c2ecf20Sopenharmony_ci	artpec6_pcie_establish_link(pci);
3338c2ecf20Sopenharmony_ci	dw_pcie_wait_for_link(pci);
3348c2ecf20Sopenharmony_ci	dw_pcie_msi_init(pp);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	return 0;
3378c2ecf20Sopenharmony_ci}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic const struct dw_pcie_host_ops artpec6_pcie_host_ops = {
3408c2ecf20Sopenharmony_ci	.host_init = artpec6_pcie_host_init,
3418c2ecf20Sopenharmony_ci};
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
3448c2ecf20Sopenharmony_ci				 struct platform_device *pdev)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct dw_pcie *pci = artpec6_pcie->pci;
3478c2ecf20Sopenharmony_ci	struct pcie_port *pp = &pci->pp;
3488c2ecf20Sopenharmony_ci	struct device *dev = pci->dev;
3498c2ecf20Sopenharmony_ci	int ret;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_PCI_MSI)) {
3528c2ecf20Sopenharmony_ci		pp->msi_irq = platform_get_irq_byname(pdev, "msi");
3538c2ecf20Sopenharmony_ci		if (pp->msi_irq < 0)
3548c2ecf20Sopenharmony_ci			return pp->msi_irq;
3558c2ecf20Sopenharmony_ci	}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	pp->ops = &artpec6_pcie_host_ops;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	ret = dw_pcie_host_init(pp);
3608c2ecf20Sopenharmony_ci	if (ret) {
3618c2ecf20Sopenharmony_ci		dev_err(dev, "failed to initialize host\n");
3628c2ecf20Sopenharmony_ci		return ret;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	return 0;
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic void artpec6_pcie_ep_init(struct dw_pcie_ep *ep)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3718c2ecf20Sopenharmony_ci	struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pci);
3728c2ecf20Sopenharmony_ci	enum pci_barno bar;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	artpec6_pcie_assert_core_reset(artpec6_pcie);
3758c2ecf20Sopenharmony_ci	artpec6_pcie_init_phy(artpec6_pcie);
3768c2ecf20Sopenharmony_ci	artpec6_pcie_deassert_core_reset(artpec6_pcie);
3778c2ecf20Sopenharmony_ci	artpec6_pcie_wait_for_phy(artpec6_pcie);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	for (bar = 0; bar < PCI_STD_NUM_BARS; bar++)
3808c2ecf20Sopenharmony_ci		dw_pcie_ep_reset_bar(pci, bar);
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
3848c2ecf20Sopenharmony_ci				  enum pci_epc_irq_type type, u16 interrupt_num)
3858c2ecf20Sopenharmony_ci{
3868c2ecf20Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	switch (type) {
3898c2ecf20Sopenharmony_ci	case PCI_EPC_IRQ_LEGACY:
3908c2ecf20Sopenharmony_ci		dev_err(pci->dev, "EP cannot trigger legacy IRQs\n");
3918c2ecf20Sopenharmony_ci		return -EINVAL;
3928c2ecf20Sopenharmony_ci	case PCI_EPC_IRQ_MSI:
3938c2ecf20Sopenharmony_ci		return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
3948c2ecf20Sopenharmony_ci	default:
3958c2ecf20Sopenharmony_ci		dev_err(pci->dev, "UNKNOWN IRQ type\n");
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	return 0;
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic const struct dw_pcie_ep_ops pcie_ep_ops = {
4028c2ecf20Sopenharmony_ci	.ep_init = artpec6_pcie_ep_init,
4038c2ecf20Sopenharmony_ci	.raise_irq = artpec6_pcie_raise_irq,
4048c2ecf20Sopenharmony_ci};
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic int artpec6_add_pcie_ep(struct artpec6_pcie *artpec6_pcie,
4078c2ecf20Sopenharmony_ci			       struct platform_device *pdev)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	int ret;
4108c2ecf20Sopenharmony_ci	struct dw_pcie_ep *ep;
4118c2ecf20Sopenharmony_ci	struct resource *res;
4128c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
4138c2ecf20Sopenharmony_ci	struct dw_pcie *pci = artpec6_pcie->pci;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	ep = &pci->ep;
4168c2ecf20Sopenharmony_ci	ep->ops = &pcie_ep_ops;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	pci->dbi_base2 = devm_platform_ioremap_resource_byname(pdev, "dbi2");
4198c2ecf20Sopenharmony_ci	if (IS_ERR(pci->dbi_base2))
4208c2ecf20Sopenharmony_ci		return PTR_ERR(pci->dbi_base2);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
4238c2ecf20Sopenharmony_ci	if (!res)
4248c2ecf20Sopenharmony_ci		return -EINVAL;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	ep->phys_base = res->start;
4278c2ecf20Sopenharmony_ci	ep->addr_size = resource_size(res);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	ret = dw_pcie_ep_init(ep);
4308c2ecf20Sopenharmony_ci	if (ret) {
4318c2ecf20Sopenharmony_ci		dev_err(dev, "failed to initialize endpoint\n");
4328c2ecf20Sopenharmony_ci		return ret;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	return 0;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic int artpec6_pcie_probe(struct platform_device *pdev)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
4418c2ecf20Sopenharmony_ci	struct dw_pcie *pci;
4428c2ecf20Sopenharmony_ci	struct artpec6_pcie *artpec6_pcie;
4438c2ecf20Sopenharmony_ci	int ret;
4448c2ecf20Sopenharmony_ci	const struct of_device_id *match;
4458c2ecf20Sopenharmony_ci	const struct artpec_pcie_of_data *data;
4468c2ecf20Sopenharmony_ci	enum artpec_pcie_variants variant;
4478c2ecf20Sopenharmony_ci	enum dw_pcie_device_mode mode;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	match = of_match_device(artpec6_pcie_of_match, dev);
4508c2ecf20Sopenharmony_ci	if (!match)
4518c2ecf20Sopenharmony_ci		return -EINVAL;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	data = (struct artpec_pcie_of_data *)match->data;
4548c2ecf20Sopenharmony_ci	variant = (enum artpec_pcie_variants)data->variant;
4558c2ecf20Sopenharmony_ci	mode = (enum dw_pcie_device_mode)data->mode;
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	artpec6_pcie = devm_kzalloc(dev, sizeof(*artpec6_pcie), GFP_KERNEL);
4588c2ecf20Sopenharmony_ci	if (!artpec6_pcie)
4598c2ecf20Sopenharmony_ci		return -ENOMEM;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
4628c2ecf20Sopenharmony_ci	if (!pci)
4638c2ecf20Sopenharmony_ci		return -ENOMEM;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	pci->dev = dev;
4668c2ecf20Sopenharmony_ci	pci->ops = &dw_pcie_ops;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	artpec6_pcie->pci = pci;
4698c2ecf20Sopenharmony_ci	artpec6_pcie->variant = variant;
4708c2ecf20Sopenharmony_ci	artpec6_pcie->mode = mode;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	pci->dbi_base = devm_platform_ioremap_resource_byname(pdev, "dbi");
4738c2ecf20Sopenharmony_ci	if (IS_ERR(pci->dbi_base))
4748c2ecf20Sopenharmony_ci		return PTR_ERR(pci->dbi_base);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	artpec6_pcie->phy_base =
4778c2ecf20Sopenharmony_ci		devm_platform_ioremap_resource_byname(pdev, "phy");
4788c2ecf20Sopenharmony_ci	if (IS_ERR(artpec6_pcie->phy_base))
4798c2ecf20Sopenharmony_ci		return PTR_ERR(artpec6_pcie->phy_base);
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	artpec6_pcie->regmap =
4828c2ecf20Sopenharmony_ci		syscon_regmap_lookup_by_phandle(dev->of_node,
4838c2ecf20Sopenharmony_ci						"axis,syscon-pcie");
4848c2ecf20Sopenharmony_ci	if (IS_ERR(artpec6_pcie->regmap))
4858c2ecf20Sopenharmony_ci		return PTR_ERR(artpec6_pcie->regmap);
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, artpec6_pcie);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	switch (artpec6_pcie->mode) {
4908c2ecf20Sopenharmony_ci	case DW_PCIE_RC_TYPE:
4918c2ecf20Sopenharmony_ci		if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_HOST))
4928c2ecf20Sopenharmony_ci			return -ENODEV;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		ret = artpec6_add_pcie_port(artpec6_pcie, pdev);
4958c2ecf20Sopenharmony_ci		if (ret < 0)
4968c2ecf20Sopenharmony_ci			return ret;
4978c2ecf20Sopenharmony_ci		break;
4988c2ecf20Sopenharmony_ci	case DW_PCIE_EP_TYPE: {
4998c2ecf20Sopenharmony_ci		u32 val;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci		if (!IS_ENABLED(CONFIG_PCIE_ARTPEC6_EP))
5028c2ecf20Sopenharmony_ci			return -ENODEV;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci		val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
5058c2ecf20Sopenharmony_ci		val &= ~PCIECFG_DEVICE_TYPE_MASK;
5068c2ecf20Sopenharmony_ci		artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
5078c2ecf20Sopenharmony_ci		ret = artpec6_add_pcie_ep(artpec6_pcie, pdev);
5088c2ecf20Sopenharmony_ci		if (ret < 0)
5098c2ecf20Sopenharmony_ci			return ret;
5108c2ecf20Sopenharmony_ci		break;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci	default:
5138c2ecf20Sopenharmony_ci		dev_err(dev, "INVALID device type %d\n", artpec6_pcie->mode);
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	return 0;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic const struct artpec_pcie_of_data artpec6_pcie_rc_of_data = {
5208c2ecf20Sopenharmony_ci	.variant = ARTPEC6,
5218c2ecf20Sopenharmony_ci	.mode = DW_PCIE_RC_TYPE,
5228c2ecf20Sopenharmony_ci};
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_cistatic const struct artpec_pcie_of_data artpec6_pcie_ep_of_data = {
5258c2ecf20Sopenharmony_ci	.variant = ARTPEC6,
5268c2ecf20Sopenharmony_ci	.mode = DW_PCIE_EP_TYPE,
5278c2ecf20Sopenharmony_ci};
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic const struct artpec_pcie_of_data artpec7_pcie_rc_of_data = {
5308c2ecf20Sopenharmony_ci	.variant = ARTPEC7,
5318c2ecf20Sopenharmony_ci	.mode = DW_PCIE_RC_TYPE,
5328c2ecf20Sopenharmony_ci};
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cistatic const struct artpec_pcie_of_data artpec7_pcie_ep_of_data = {
5358c2ecf20Sopenharmony_ci	.variant = ARTPEC7,
5368c2ecf20Sopenharmony_ci	.mode = DW_PCIE_EP_TYPE,
5378c2ecf20Sopenharmony_ci};
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic const struct of_device_id artpec6_pcie_of_match[] = {
5408c2ecf20Sopenharmony_ci	{
5418c2ecf20Sopenharmony_ci		.compatible = "axis,artpec6-pcie",
5428c2ecf20Sopenharmony_ci		.data = &artpec6_pcie_rc_of_data,
5438c2ecf20Sopenharmony_ci	},
5448c2ecf20Sopenharmony_ci	{
5458c2ecf20Sopenharmony_ci		.compatible = "axis,artpec6-pcie-ep",
5468c2ecf20Sopenharmony_ci		.data = &artpec6_pcie_ep_of_data,
5478c2ecf20Sopenharmony_ci	},
5488c2ecf20Sopenharmony_ci	{
5498c2ecf20Sopenharmony_ci		.compatible = "axis,artpec7-pcie",
5508c2ecf20Sopenharmony_ci		.data = &artpec7_pcie_rc_of_data,
5518c2ecf20Sopenharmony_ci	},
5528c2ecf20Sopenharmony_ci	{
5538c2ecf20Sopenharmony_ci		.compatible = "axis,artpec7-pcie-ep",
5548c2ecf20Sopenharmony_ci		.data = &artpec7_pcie_ep_of_data,
5558c2ecf20Sopenharmony_ci	},
5568c2ecf20Sopenharmony_ci	{},
5578c2ecf20Sopenharmony_ci};
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_cistatic struct platform_driver artpec6_pcie_driver = {
5608c2ecf20Sopenharmony_ci	.probe = artpec6_pcie_probe,
5618c2ecf20Sopenharmony_ci	.driver = {
5628c2ecf20Sopenharmony_ci		.name	= "artpec6-pcie",
5638c2ecf20Sopenharmony_ci		.of_match_table = artpec6_pcie_of_match,
5648c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
5658c2ecf20Sopenharmony_ci	},
5668c2ecf20Sopenharmony_ci};
5678c2ecf20Sopenharmony_cibuiltin_platform_driver(artpec6_pcie_driver);
568