162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PCIe controller driver for Intel Keem Bay
462306a36Sopenharmony_ci * Copyright (C) 2020 Intel Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/bitfield.h>
862306a36Sopenharmony_ci#include <linux/bits.h>
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/iopoll.h>
1562306a36Sopenharmony_ci#include <linux/irqchip/chained_irq.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1862306a36Sopenharmony_ci#include <linux/pci.h>
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/property.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "pcie-designware.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* PCIE_REGS_APB_SLV Registers */
2562306a36Sopenharmony_ci#define PCIE_REGS_PCIE_CFG		0x0004
2662306a36Sopenharmony_ci#define  PCIE_DEVICE_TYPE		BIT(8)
2762306a36Sopenharmony_ci#define  PCIE_RSTN			BIT(0)
2862306a36Sopenharmony_ci#define PCIE_REGS_PCIE_APP_CNTRL	0x0008
2962306a36Sopenharmony_ci#define  APP_LTSSM_ENABLE		BIT(0)
3062306a36Sopenharmony_ci#define PCIE_REGS_INTERRUPT_ENABLE	0x0028
3162306a36Sopenharmony_ci#define  MSI_CTRL_INT_EN		BIT(8)
3262306a36Sopenharmony_ci#define  EDMA_INT_EN			GENMASK(7, 0)
3362306a36Sopenharmony_ci#define PCIE_REGS_INTERRUPT_STATUS	0x002c
3462306a36Sopenharmony_ci#define  MSI_CTRL_INT			BIT(8)
3562306a36Sopenharmony_ci#define PCIE_REGS_PCIE_SII_PM_STATE	0x00b0
3662306a36Sopenharmony_ci#define  SMLH_LINK_UP			BIT(19)
3762306a36Sopenharmony_ci#define  RDLH_LINK_UP			BIT(8)
3862306a36Sopenharmony_ci#define  PCIE_REGS_PCIE_SII_LINK_UP	(SMLH_LINK_UP | RDLH_LINK_UP)
3962306a36Sopenharmony_ci#define PCIE_REGS_PCIE_PHY_CNTL		0x0164
4062306a36Sopenharmony_ci#define  PHY0_SRAM_BYPASS		BIT(8)
4162306a36Sopenharmony_ci#define PCIE_REGS_PCIE_PHY_STAT		0x0168
4262306a36Sopenharmony_ci#define  PHY0_MPLLA_STATE		BIT(1)
4362306a36Sopenharmony_ci#define PCIE_REGS_LJPLL_STA		0x016c
4462306a36Sopenharmony_ci#define  LJPLL_LOCK			BIT(0)
4562306a36Sopenharmony_ci#define PCIE_REGS_LJPLL_CNTRL_0		0x0170
4662306a36Sopenharmony_ci#define  LJPLL_EN			BIT(29)
4762306a36Sopenharmony_ci#define  LJPLL_FOUT_EN			GENMASK(24, 21)
4862306a36Sopenharmony_ci#define PCIE_REGS_LJPLL_CNTRL_2		0x0178
4962306a36Sopenharmony_ci#define  LJPLL_REF_DIV			GENMASK(17, 12)
5062306a36Sopenharmony_ci#define  LJPLL_FB_DIV			GENMASK(11, 0)
5162306a36Sopenharmony_ci#define PCIE_REGS_LJPLL_CNTRL_3		0x017c
5262306a36Sopenharmony_ci#define  LJPLL_POST_DIV3A		GENMASK(24, 22)
5362306a36Sopenharmony_ci#define  LJPLL_POST_DIV2A		GENMASK(18, 16)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define PERST_DELAY_US		1000
5662306a36Sopenharmony_ci#define AUX_CLK_RATE_HZ		24000000
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistruct keembay_pcie {
5962306a36Sopenharmony_ci	struct dw_pcie		pci;
6062306a36Sopenharmony_ci	void __iomem		*apb_base;
6162306a36Sopenharmony_ci	enum dw_pcie_device_mode mode;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	struct clk		*clk_master;
6462306a36Sopenharmony_ci	struct clk		*clk_aux;
6562306a36Sopenharmony_ci	struct gpio_desc	*reset;
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct keembay_pcie_of_data {
6962306a36Sopenharmony_ci	enum dw_pcie_device_mode mode;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic void keembay_ep_reset_assert(struct keembay_pcie *pcie)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	gpiod_set_value_cansleep(pcie->reset, 1);
7562306a36Sopenharmony_ci	usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void keembay_ep_reset_deassert(struct keembay_pcie *pcie)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	/*
8162306a36Sopenharmony_ci	 * Ensure that PERST# is asserted for a minimum of 100ms.
8262306a36Sopenharmony_ci	 *
8362306a36Sopenharmony_ci	 * For more details, refer to PCI Express Card Electromechanical
8462306a36Sopenharmony_ci	 * Specification Revision 1.1, Table-2.4.
8562306a36Sopenharmony_ci	 */
8662306a36Sopenharmony_ci	msleep(100);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	gpiod_set_value_cansleep(pcie->reset, 0);
8962306a36Sopenharmony_ci	usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic void keembay_pcie_ltssm_set(struct keembay_pcie *pcie, bool enable)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	u32 val;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	val = readl(pcie->apb_base + PCIE_REGS_PCIE_APP_CNTRL);
9762306a36Sopenharmony_ci	if (enable)
9862306a36Sopenharmony_ci		val |= APP_LTSSM_ENABLE;
9962306a36Sopenharmony_ci	else
10062306a36Sopenharmony_ci		val &= ~APP_LTSSM_ENABLE;
10162306a36Sopenharmony_ci	writel(val, pcie->apb_base + PCIE_REGS_PCIE_APP_CNTRL);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int keembay_pcie_link_up(struct dw_pcie *pci)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct keembay_pcie *pcie = dev_get_drvdata(pci->dev);
10762306a36Sopenharmony_ci	u32 val;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	val = readl(pcie->apb_base + PCIE_REGS_PCIE_SII_PM_STATE);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	return (val & PCIE_REGS_PCIE_SII_LINK_UP) == PCIE_REGS_PCIE_SII_LINK_UP;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic int keembay_pcie_start_link(struct dw_pcie *pci)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct keembay_pcie *pcie = dev_get_drvdata(pci->dev);
11762306a36Sopenharmony_ci	u32 val;
11862306a36Sopenharmony_ci	int ret;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (pcie->mode == DW_PCIE_EP_TYPE)
12162306a36Sopenharmony_ci		return 0;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	keembay_pcie_ltssm_set(pcie, false);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	ret = readl_poll_timeout(pcie->apb_base + PCIE_REGS_PCIE_PHY_STAT,
12662306a36Sopenharmony_ci				 val, val & PHY0_MPLLA_STATE, 20,
12762306a36Sopenharmony_ci				 500 * USEC_PER_MSEC);
12862306a36Sopenharmony_ci	if (ret) {
12962306a36Sopenharmony_ci		dev_err(pci->dev, "MPLLA is not locked\n");
13062306a36Sopenharmony_ci		return ret;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	keembay_pcie_ltssm_set(pcie, true);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return 0;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void keembay_pcie_stop_link(struct dw_pcie *pci)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct keembay_pcie *pcie = dev_get_drvdata(pci->dev);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	keembay_pcie_ltssm_set(pcie, false);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic const struct dw_pcie_ops keembay_pcie_ops = {
14662306a36Sopenharmony_ci	.link_up	= keembay_pcie_link_up,
14762306a36Sopenharmony_ci	.start_link	= keembay_pcie_start_link,
14862306a36Sopenharmony_ci	.stop_link	= keembay_pcie_stop_link,
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic inline void keembay_pcie_disable_clock(void *data)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct clk *clk = data;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	clk_disable_unprepare(clk);
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic inline struct clk *keembay_pcie_probe_clock(struct device *dev,
15962306a36Sopenharmony_ci						   const char *id, u64 rate)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	struct clk *clk;
16262306a36Sopenharmony_ci	int ret;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	clk = devm_clk_get(dev, id);
16562306a36Sopenharmony_ci	if (IS_ERR(clk))
16662306a36Sopenharmony_ci		return clk;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (rate) {
16962306a36Sopenharmony_ci		ret = clk_set_rate(clk, rate);
17062306a36Sopenharmony_ci		if (ret)
17162306a36Sopenharmony_ci			return ERR_PTR(ret);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	ret = clk_prepare_enable(clk);
17562306a36Sopenharmony_ci	if (ret)
17662306a36Sopenharmony_ci		return ERR_PTR(ret);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	ret = devm_add_action_or_reset(dev, keembay_pcie_disable_clock, clk);
17962306a36Sopenharmony_ci	if (ret)
18062306a36Sopenharmony_ci		return ERR_PTR(ret);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return clk;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int keembay_pcie_probe_clocks(struct keembay_pcie *pcie)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct dw_pcie *pci = &pcie->pci;
18862306a36Sopenharmony_ci	struct device *dev = pci->dev;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	pcie->clk_master = keembay_pcie_probe_clock(dev, "master", 0);
19162306a36Sopenharmony_ci	if (IS_ERR(pcie->clk_master))
19262306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(pcie->clk_master),
19362306a36Sopenharmony_ci				     "Failed to enable master clock");
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	pcie->clk_aux = keembay_pcie_probe_clock(dev, "aux", AUX_CLK_RATE_HZ);
19662306a36Sopenharmony_ci	if (IS_ERR(pcie->clk_aux))
19762306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(pcie->clk_aux),
19862306a36Sopenharmony_ci				     "Failed to enable auxiliary clock");
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	return 0;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/*
20462306a36Sopenharmony_ci * Initialize the internal PCIe PLL in Host mode.
20562306a36Sopenharmony_ci * See the following sections in Keem Bay data book,
20662306a36Sopenharmony_ci * (1) 6.4.6.1 PCIe Subsystem Example Initialization,
20762306a36Sopenharmony_ci * (2) 6.8 PCIe Low Jitter PLL for Ref Clk Generation.
20862306a36Sopenharmony_ci */
20962306a36Sopenharmony_cistatic int keembay_pcie_pll_init(struct keembay_pcie *pcie)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct dw_pcie *pci = &pcie->pci;
21262306a36Sopenharmony_ci	u32 val;
21362306a36Sopenharmony_ci	int ret;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	val = FIELD_PREP(LJPLL_REF_DIV, 0) | FIELD_PREP(LJPLL_FB_DIV, 0x32);
21662306a36Sopenharmony_ci	writel(val, pcie->apb_base + PCIE_REGS_LJPLL_CNTRL_2);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	val = FIELD_PREP(LJPLL_POST_DIV3A, 0x2) |
21962306a36Sopenharmony_ci		FIELD_PREP(LJPLL_POST_DIV2A, 0x2);
22062306a36Sopenharmony_ci	writel(val, pcie->apb_base + PCIE_REGS_LJPLL_CNTRL_3);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	val = FIELD_PREP(LJPLL_EN, 0x1) | FIELD_PREP(LJPLL_FOUT_EN, 0xc);
22362306a36Sopenharmony_ci	writel(val, pcie->apb_base + PCIE_REGS_LJPLL_CNTRL_0);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	ret = readl_poll_timeout(pcie->apb_base + PCIE_REGS_LJPLL_STA,
22662306a36Sopenharmony_ci				 val, val & LJPLL_LOCK, 20,
22762306a36Sopenharmony_ci				 500 * USEC_PER_MSEC);
22862306a36Sopenharmony_ci	if (ret)
22962306a36Sopenharmony_ci		dev_err(pci->dev, "Low jitter PLL is not locked\n");
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return ret;
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic void keembay_pcie_msi_irq_handler(struct irq_desc *desc)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct keembay_pcie *pcie = irq_desc_get_handler_data(desc);
23762306a36Sopenharmony_ci	struct irq_chip *chip = irq_desc_get_chip(desc);
23862306a36Sopenharmony_ci	u32 val, mask, status;
23962306a36Sopenharmony_ci	struct dw_pcie_rp *pp;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/*
24262306a36Sopenharmony_ci	 * Keem Bay PCIe Controller provides an additional IP logic on top of
24362306a36Sopenharmony_ci	 * standard DWC IP to clear MSI IRQ by writing '1' to the respective
24462306a36Sopenharmony_ci	 * bit of the status register.
24562306a36Sopenharmony_ci	 *
24662306a36Sopenharmony_ci	 * So, a chained irq handler is defined to handle this additional
24762306a36Sopenharmony_ci	 * IP logic.
24862306a36Sopenharmony_ci	 */
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	chained_irq_enter(chip, desc);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	pp = &pcie->pci.pp;
25362306a36Sopenharmony_ci	val = readl(pcie->apb_base + PCIE_REGS_INTERRUPT_STATUS);
25462306a36Sopenharmony_ci	mask = readl(pcie->apb_base + PCIE_REGS_INTERRUPT_ENABLE);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	status = val & mask;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (status & MSI_CTRL_INT) {
25962306a36Sopenharmony_ci		dw_handle_msi_irq(pp);
26062306a36Sopenharmony_ci		writel(status, pcie->apb_base + PCIE_REGS_INTERRUPT_STATUS);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	chained_irq_exit(chip, desc);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cistatic int keembay_pcie_setup_msi_irq(struct keembay_pcie *pcie)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	struct dw_pcie *pci = &pcie->pci;
26962306a36Sopenharmony_ci	struct device *dev = pci->dev;
27062306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
27162306a36Sopenharmony_ci	int irq;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	irq = platform_get_irq_byname(pdev, "pcie");
27462306a36Sopenharmony_ci	if (irq < 0)
27562306a36Sopenharmony_ci		return irq;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	irq_set_chained_handler_and_data(irq, keembay_pcie_msi_irq_handler,
27862306a36Sopenharmony_ci					 pcie);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return 0;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic void keembay_pcie_ep_init(struct dw_pcie_ep *ep)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
28662306a36Sopenharmony_ci	struct keembay_pcie *pcie = dev_get_drvdata(pci->dev);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	writel(EDMA_INT_EN, pcie->apb_base + PCIE_REGS_INTERRUPT_ENABLE);
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
29262306a36Sopenharmony_ci				     enum pci_epc_irq_type type,
29362306a36Sopenharmony_ci				     u16 interrupt_num)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	switch (type) {
29862306a36Sopenharmony_ci	case PCI_EPC_IRQ_LEGACY:
29962306a36Sopenharmony_ci		/* Legacy interrupts are not supported in Keem Bay */
30062306a36Sopenharmony_ci		dev_err(pci->dev, "Legacy IRQ is not supported\n");
30162306a36Sopenharmony_ci		return -EINVAL;
30262306a36Sopenharmony_ci	case PCI_EPC_IRQ_MSI:
30362306a36Sopenharmony_ci		return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
30462306a36Sopenharmony_ci	case PCI_EPC_IRQ_MSIX:
30562306a36Sopenharmony_ci		return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
30662306a36Sopenharmony_ci	default:
30762306a36Sopenharmony_ci		dev_err(pci->dev, "Unknown IRQ type %d\n", type);
30862306a36Sopenharmony_ci		return -EINVAL;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic const struct pci_epc_features keembay_pcie_epc_features = {
31362306a36Sopenharmony_ci	.linkup_notifier	= false,
31462306a36Sopenharmony_ci	.msi_capable		= true,
31562306a36Sopenharmony_ci	.msix_capable		= true,
31662306a36Sopenharmony_ci	.reserved_bar		= BIT(BAR_1) | BIT(BAR_3) | BIT(BAR_5),
31762306a36Sopenharmony_ci	.bar_fixed_64bit	= BIT(BAR_0) | BIT(BAR_2) | BIT(BAR_4),
31862306a36Sopenharmony_ci	.align			= SZ_16K,
31962306a36Sopenharmony_ci};
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic const struct pci_epc_features *
32262306a36Sopenharmony_cikeembay_pcie_get_features(struct dw_pcie_ep *ep)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	return &keembay_pcie_epc_features;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic const struct dw_pcie_ep_ops keembay_pcie_ep_ops = {
32862306a36Sopenharmony_ci	.ep_init	= keembay_pcie_ep_init,
32962306a36Sopenharmony_ci	.raise_irq	= keembay_pcie_ep_raise_irq,
33062306a36Sopenharmony_ci	.get_features	= keembay_pcie_get_features,
33162306a36Sopenharmony_ci};
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic const struct dw_pcie_host_ops keembay_pcie_host_ops = {
33462306a36Sopenharmony_ci};
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic int keembay_pcie_add_pcie_port(struct keembay_pcie *pcie,
33762306a36Sopenharmony_ci				      struct platform_device *pdev)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct dw_pcie *pci = &pcie->pci;
34062306a36Sopenharmony_ci	struct dw_pcie_rp *pp = &pci->pp;
34162306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
34262306a36Sopenharmony_ci	u32 val;
34362306a36Sopenharmony_ci	int ret;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	pp->ops = &keembay_pcie_host_ops;
34662306a36Sopenharmony_ci	pp->msi_irq[0] = -ENODEV;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	ret = keembay_pcie_setup_msi_irq(pcie);
34962306a36Sopenharmony_ci	if (ret)
35062306a36Sopenharmony_ci		return ret;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	pcie->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
35362306a36Sopenharmony_ci	if (IS_ERR(pcie->reset))
35462306a36Sopenharmony_ci		return PTR_ERR(pcie->reset);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	ret = keembay_pcie_probe_clocks(pcie);
35762306a36Sopenharmony_ci	if (ret)
35862306a36Sopenharmony_ci		return ret;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	val = readl(pcie->apb_base + PCIE_REGS_PCIE_PHY_CNTL);
36162306a36Sopenharmony_ci	val |= PHY0_SRAM_BYPASS;
36262306a36Sopenharmony_ci	writel(val, pcie->apb_base + PCIE_REGS_PCIE_PHY_CNTL);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	writel(PCIE_DEVICE_TYPE, pcie->apb_base + PCIE_REGS_PCIE_CFG);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	ret = keembay_pcie_pll_init(pcie);
36762306a36Sopenharmony_ci	if (ret)
36862306a36Sopenharmony_ci		return ret;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	val = readl(pcie->apb_base + PCIE_REGS_PCIE_CFG);
37162306a36Sopenharmony_ci	writel(val | PCIE_RSTN, pcie->apb_base + PCIE_REGS_PCIE_CFG);
37262306a36Sopenharmony_ci	keembay_ep_reset_deassert(pcie);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	ret = dw_pcie_host_init(pp);
37562306a36Sopenharmony_ci	if (ret) {
37662306a36Sopenharmony_ci		keembay_ep_reset_assert(pcie);
37762306a36Sopenharmony_ci		dev_err(dev, "Failed to initialize host: %d\n", ret);
37862306a36Sopenharmony_ci		return ret;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	val = readl(pcie->apb_base + PCIE_REGS_INTERRUPT_ENABLE);
38262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_PCI_MSI))
38362306a36Sopenharmony_ci		val |= MSI_CTRL_INT_EN;
38462306a36Sopenharmony_ci	writel(val, pcie->apb_base + PCIE_REGS_INTERRUPT_ENABLE);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return 0;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int keembay_pcie_probe(struct platform_device *pdev)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	const struct keembay_pcie_of_data *data;
39262306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
39362306a36Sopenharmony_ci	struct keembay_pcie *pcie;
39462306a36Sopenharmony_ci	struct dw_pcie *pci;
39562306a36Sopenharmony_ci	enum dw_pcie_device_mode mode;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	data = device_get_match_data(dev);
39862306a36Sopenharmony_ci	if (!data)
39962306a36Sopenharmony_ci		return -ENODEV;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	mode = (enum dw_pcie_device_mode)data->mode;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
40462306a36Sopenharmony_ci	if (!pcie)
40562306a36Sopenharmony_ci		return -ENOMEM;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	pci = &pcie->pci;
40862306a36Sopenharmony_ci	pci->dev = dev;
40962306a36Sopenharmony_ci	pci->ops = &keembay_pcie_ops;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	pcie->mode = mode;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	pcie->apb_base = devm_platform_ioremap_resource_byname(pdev, "apb");
41462306a36Sopenharmony_ci	if (IS_ERR(pcie->apb_base))
41562306a36Sopenharmony_ci		return PTR_ERR(pcie->apb_base);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	platform_set_drvdata(pdev, pcie);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	switch (pcie->mode) {
42062306a36Sopenharmony_ci	case DW_PCIE_RC_TYPE:
42162306a36Sopenharmony_ci		if (!IS_ENABLED(CONFIG_PCIE_KEEMBAY_HOST))
42262306a36Sopenharmony_ci			return -ENODEV;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		return keembay_pcie_add_pcie_port(pcie, pdev);
42562306a36Sopenharmony_ci	case DW_PCIE_EP_TYPE:
42662306a36Sopenharmony_ci		if (!IS_ENABLED(CONFIG_PCIE_KEEMBAY_EP))
42762306a36Sopenharmony_ci			return -ENODEV;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci		pci->ep.ops = &keembay_pcie_ep_ops;
43062306a36Sopenharmony_ci		return dw_pcie_ep_init(&pci->ep);
43162306a36Sopenharmony_ci	default:
43262306a36Sopenharmony_ci		dev_err(dev, "Invalid device type %d\n", pcie->mode);
43362306a36Sopenharmony_ci		return -ENODEV;
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic const struct keembay_pcie_of_data keembay_pcie_rc_of_data = {
43862306a36Sopenharmony_ci	.mode = DW_PCIE_RC_TYPE,
43962306a36Sopenharmony_ci};
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic const struct keembay_pcie_of_data keembay_pcie_ep_of_data = {
44262306a36Sopenharmony_ci	.mode = DW_PCIE_EP_TYPE,
44362306a36Sopenharmony_ci};
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic const struct of_device_id keembay_pcie_of_match[] = {
44662306a36Sopenharmony_ci	{
44762306a36Sopenharmony_ci		.compatible = "intel,keembay-pcie",
44862306a36Sopenharmony_ci		.data = &keembay_pcie_rc_of_data,
44962306a36Sopenharmony_ci	},
45062306a36Sopenharmony_ci	{
45162306a36Sopenharmony_ci		.compatible = "intel,keembay-pcie-ep",
45262306a36Sopenharmony_ci		.data = &keembay_pcie_ep_of_data,
45362306a36Sopenharmony_ci	},
45462306a36Sopenharmony_ci	{}
45562306a36Sopenharmony_ci};
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_cistatic struct platform_driver keembay_pcie_driver = {
45862306a36Sopenharmony_ci	.driver = {
45962306a36Sopenharmony_ci		.name = "keembay-pcie",
46062306a36Sopenharmony_ci		.of_match_table = keembay_pcie_of_match,
46162306a36Sopenharmony_ci		.suppress_bind_attrs = true,
46262306a36Sopenharmony_ci	},
46362306a36Sopenharmony_ci	.probe  = keembay_pcie_probe,
46462306a36Sopenharmony_ci};
46562306a36Sopenharmony_cibuiltin_platform_driver(keembay_pcie_driver);
466