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