162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * FU740 DesignWare PCIe Controller integration 462306a36Sopenharmony_ci * Copyright (C) 2019-2021 SiFive, Inc. 562306a36Sopenharmony_ci * Paul Walmsley 662306a36Sopenharmony_ci * Greentime Hu 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based in part on the i.MX6 PCIe host controller shim which is: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Copyright (C) 2013 Kosagi 1162306a36Sopenharmony_ci * https://www.kosagi.com 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/clk.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/gpio.h> 1762306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/pci.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/resource.h> 2362306a36Sopenharmony_ci#include <linux/types.h> 2462306a36Sopenharmony_ci#include <linux/interrupt.h> 2562306a36Sopenharmony_ci#include <linux/iopoll.h> 2662306a36Sopenharmony_ci#include <linux/reset.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include "pcie-designware.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define to_fu740_pcie(x) dev_get_drvdata((x)->dev) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct fu740_pcie { 3362306a36Sopenharmony_ci struct dw_pcie pci; 3462306a36Sopenharmony_ci void __iomem *mgmt_base; 3562306a36Sopenharmony_ci struct gpio_desc *reset; 3662306a36Sopenharmony_ci struct gpio_desc *pwren; 3762306a36Sopenharmony_ci struct clk *pcie_aux; 3862306a36Sopenharmony_ci struct reset_control *rst; 3962306a36Sopenharmony_ci}; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define SIFIVE_DEVICESRESETREG 0x28 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define PCIEX8MGMT_PERST_N 0x0 4462306a36Sopenharmony_ci#define PCIEX8MGMT_APP_LTSSM_ENABLE 0x10 4562306a36Sopenharmony_ci#define PCIEX8MGMT_APP_HOLD_PHY_RST 0x18 4662306a36Sopenharmony_ci#define PCIEX8MGMT_DEVICE_TYPE 0x708 4762306a36Sopenharmony_ci#define PCIEX8MGMT_PHY0_CR_PARA_ADDR 0x860 4862306a36Sopenharmony_ci#define PCIEX8MGMT_PHY0_CR_PARA_RD_EN 0x870 4962306a36Sopenharmony_ci#define PCIEX8MGMT_PHY0_CR_PARA_RD_DATA 0x878 5062306a36Sopenharmony_ci#define PCIEX8MGMT_PHY0_CR_PARA_SEL 0x880 5162306a36Sopenharmony_ci#define PCIEX8MGMT_PHY0_CR_PARA_WR_DATA 0x888 5262306a36Sopenharmony_ci#define PCIEX8MGMT_PHY0_CR_PARA_WR_EN 0x890 5362306a36Sopenharmony_ci#define PCIEX8MGMT_PHY0_CR_PARA_ACK 0x898 5462306a36Sopenharmony_ci#define PCIEX8MGMT_PHY1_CR_PARA_ADDR 0x8a0 5562306a36Sopenharmony_ci#define PCIEX8MGMT_PHY1_CR_PARA_RD_EN 0x8b0 5662306a36Sopenharmony_ci#define PCIEX8MGMT_PHY1_CR_PARA_RD_DATA 0x8b8 5762306a36Sopenharmony_ci#define PCIEX8MGMT_PHY1_CR_PARA_SEL 0x8c0 5862306a36Sopenharmony_ci#define PCIEX8MGMT_PHY1_CR_PARA_WR_DATA 0x8c8 5962306a36Sopenharmony_ci#define PCIEX8MGMT_PHY1_CR_PARA_WR_EN 0x8d0 6062306a36Sopenharmony_ci#define PCIEX8MGMT_PHY1_CR_PARA_ACK 0x8d8 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_CDR_TRACK_EN BIT(0) 6362306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_LOS_THRSHLD BIT(5) 6462306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_TERM_EN BIT(9) 6562306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_TERM_ACDC BIT(10) 6662306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_EN BIT(11) 6762306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_INIT_VAL (PCIEX8MGMT_PHY_CDR_TRACK_EN|\ 6862306a36Sopenharmony_ci PCIEX8MGMT_PHY_LOS_THRSHLD|\ 6962306a36Sopenharmony_ci PCIEX8MGMT_PHY_TERM_EN|\ 7062306a36Sopenharmony_ci PCIEX8MGMT_PHY_TERM_ACDC|\ 7162306a36Sopenharmony_ci PCIEX8MGMT_PHY_EN) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 0x1008 7462306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_LANE_OFF 0x100 7562306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_LANE0_BASE (PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 0) 7662306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_LANE1_BASE (PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 1) 7762306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_LANE2_BASE (PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 2) 7862306a36Sopenharmony_ci#define PCIEX8MGMT_PHY_LANE3_BASE (PCIEX8MGMT_PHY_LANEN_DIG_ASIC_RX_OVRD_IN_3 + 0x100 * 3) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic void fu740_pcie_assert_reset(struct fu740_pcie *afp) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci /* Assert PERST_N GPIO */ 8362306a36Sopenharmony_ci gpiod_set_value_cansleep(afp->reset, 0); 8462306a36Sopenharmony_ci /* Assert controller PERST_N */ 8562306a36Sopenharmony_ci writel_relaxed(0x0, afp->mgmt_base + PCIEX8MGMT_PERST_N); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void fu740_pcie_deassert_reset(struct fu740_pcie *afp) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci /* Deassert controller PERST_N */ 9162306a36Sopenharmony_ci writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_PERST_N); 9262306a36Sopenharmony_ci /* Deassert PERST_N GPIO */ 9362306a36Sopenharmony_ci gpiod_set_value_cansleep(afp->reset, 1); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic void fu740_pcie_power_on(struct fu740_pcie *afp) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci gpiod_set_value_cansleep(afp->pwren, 1); 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * Ensure that PERST has been asserted for at least 100 ms. 10162306a36Sopenharmony_ci * Section 2.2 of PCI Express Card Electromechanical Specification 10262306a36Sopenharmony_ci * Revision 3.0 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci msleep(100); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic void fu740_pcie_drive_reset(struct fu740_pcie *afp) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci fu740_pcie_assert_reset(afp); 11062306a36Sopenharmony_ci fu740_pcie_power_on(afp); 11162306a36Sopenharmony_ci fu740_pcie_deassert_reset(afp); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void fu740_phyregwrite(const uint8_t phy, const uint16_t addr, 11562306a36Sopenharmony_ci const uint16_t wrdata, struct fu740_pcie *afp) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct device *dev = afp->pci.dev; 11862306a36Sopenharmony_ci void __iomem *phy_cr_para_addr; 11962306a36Sopenharmony_ci void __iomem *phy_cr_para_wr_data; 12062306a36Sopenharmony_ci void __iomem *phy_cr_para_wr_en; 12162306a36Sopenharmony_ci void __iomem *phy_cr_para_ack; 12262306a36Sopenharmony_ci int ret, val; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Setup */ 12562306a36Sopenharmony_ci if (phy) { 12662306a36Sopenharmony_ci phy_cr_para_addr = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_ADDR; 12762306a36Sopenharmony_ci phy_cr_para_wr_data = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_WR_DATA; 12862306a36Sopenharmony_ci phy_cr_para_wr_en = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_WR_EN; 12962306a36Sopenharmony_ci phy_cr_para_ack = afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_ACK; 13062306a36Sopenharmony_ci } else { 13162306a36Sopenharmony_ci phy_cr_para_addr = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_ADDR; 13262306a36Sopenharmony_ci phy_cr_para_wr_data = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_WR_DATA; 13362306a36Sopenharmony_ci phy_cr_para_wr_en = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_WR_EN; 13462306a36Sopenharmony_ci phy_cr_para_ack = afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_ACK; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci writel_relaxed(addr, phy_cr_para_addr); 13862306a36Sopenharmony_ci writel_relaxed(wrdata, phy_cr_para_wr_data); 13962306a36Sopenharmony_ci writel_relaxed(1, phy_cr_para_wr_en); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Wait for wait_idle */ 14262306a36Sopenharmony_ci ret = readl_poll_timeout(phy_cr_para_ack, val, val, 10, 5000); 14362306a36Sopenharmony_ci if (ret) 14462306a36Sopenharmony_ci dev_warn(dev, "Wait for wait_idle state failed!\n"); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* Clear */ 14762306a36Sopenharmony_ci writel_relaxed(0, phy_cr_para_wr_en); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* Wait for ~wait_idle */ 15062306a36Sopenharmony_ci ret = readl_poll_timeout(phy_cr_para_ack, val, !val, 10, 5000); 15162306a36Sopenharmony_ci if (ret) 15262306a36Sopenharmony_ci dev_warn(dev, "Wait for !wait_idle state failed!\n"); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void fu740_pcie_init_phy(struct fu740_pcie *afp) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci /* Enable phy cr_para_sel interfaces */ 15862306a36Sopenharmony_ci writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_PHY0_CR_PARA_SEL); 15962306a36Sopenharmony_ci writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_PHY1_CR_PARA_SEL); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * Wait 10 cr_para cycles to guarantee that the registers are ready 16362306a36Sopenharmony_ci * to be edited. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci ndelay(10); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Set PHY AC termination mode */ 16862306a36Sopenharmony_ci fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE0_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); 16962306a36Sopenharmony_ci fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE1_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); 17062306a36Sopenharmony_ci fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE2_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); 17162306a36Sopenharmony_ci fu740_phyregwrite(0, PCIEX8MGMT_PHY_LANE3_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); 17262306a36Sopenharmony_ci fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE0_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); 17362306a36Sopenharmony_ci fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE1_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); 17462306a36Sopenharmony_ci fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE2_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); 17562306a36Sopenharmony_ci fu740_phyregwrite(1, PCIEX8MGMT_PHY_LANE3_BASE, PCIEX8MGMT_PHY_INIT_VAL, afp); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic int fu740_pcie_start_link(struct dw_pcie *pci) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct device *dev = pci->dev; 18162306a36Sopenharmony_ci struct fu740_pcie *afp = dev_get_drvdata(dev); 18262306a36Sopenharmony_ci u8 cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP); 18362306a36Sopenharmony_ci int ret; 18462306a36Sopenharmony_ci u32 orig, tmp; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* 18762306a36Sopenharmony_ci * Force 2.5GT/s when starting the link, due to some devices not 18862306a36Sopenharmony_ci * probing at higher speeds. This happens with the PCIe switch 18962306a36Sopenharmony_ci * on the Unmatched board when U-Boot has not initialised the PCIe. 19062306a36Sopenharmony_ci * The fix in U-Boot is to force 2.5GT/s, which then gets cleared 19162306a36Sopenharmony_ci * by the soft reset done by this driver. 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ci dev_dbg(dev, "cap_exp at %x\n", cap_exp); 19462306a36Sopenharmony_ci dw_pcie_dbi_ro_wr_en(pci); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci tmp = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCAP); 19762306a36Sopenharmony_ci orig = tmp & PCI_EXP_LNKCAP_SLS; 19862306a36Sopenharmony_ci tmp &= ~PCI_EXP_LNKCAP_SLS; 19962306a36Sopenharmony_ci tmp |= PCI_EXP_LNKCAP_SLS_2_5GB; 20062306a36Sopenharmony_ci dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCAP, tmp); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Enable LTSSM */ 20362306a36Sopenharmony_ci writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_APP_LTSSM_ENABLE); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ret = dw_pcie_wait_for_link(pci); 20662306a36Sopenharmony_ci if (ret) { 20762306a36Sopenharmony_ci dev_err(dev, "error: link did not start\n"); 20862306a36Sopenharmony_ci goto err; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci tmp = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCAP); 21262306a36Sopenharmony_ci if ((tmp & PCI_EXP_LNKCAP_SLS) != orig) { 21362306a36Sopenharmony_ci dev_dbg(dev, "changing speed back to original\n"); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci tmp &= ~PCI_EXP_LNKCAP_SLS; 21662306a36Sopenharmony_ci tmp |= orig; 21762306a36Sopenharmony_ci dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCAP, tmp); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); 22062306a36Sopenharmony_ci tmp |= PORT_LOGIC_SPEED_CHANGE; 22162306a36Sopenharmony_ci dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = dw_pcie_wait_for_link(pci); 22462306a36Sopenharmony_ci if (ret) { 22562306a36Sopenharmony_ci dev_err(dev, "error: link did not start at new speed\n"); 22662306a36Sopenharmony_ci goto err; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ret = 0; 23162306a36Sopenharmony_cierr: 23262306a36Sopenharmony_ci WARN_ON(ret); /* we assume that errors will be very rare */ 23362306a36Sopenharmony_ci dw_pcie_dbi_ro_wr_dis(pci); 23462306a36Sopenharmony_ci return ret; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int fu740_pcie_host_init(struct dw_pcie_rp *pp) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct dw_pcie *pci = to_dw_pcie_from_pp(pp); 24062306a36Sopenharmony_ci struct fu740_pcie *afp = to_fu740_pcie(pci); 24162306a36Sopenharmony_ci struct device *dev = pci->dev; 24262306a36Sopenharmony_ci int ret; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* Power on reset */ 24562306a36Sopenharmony_ci fu740_pcie_drive_reset(afp); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci /* Enable pcieauxclk */ 24862306a36Sopenharmony_ci ret = clk_prepare_enable(afp->pcie_aux); 24962306a36Sopenharmony_ci if (ret) { 25062306a36Sopenharmony_ci dev_err(dev, "unable to enable pcie_aux clock\n"); 25162306a36Sopenharmony_ci return ret; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * Assert hold_phy_rst (hold the controller LTSSM in reset after 25662306a36Sopenharmony_ci * power_up_rst_n for register programming with cr_para) 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci writel_relaxed(0x1, afp->mgmt_base + PCIEX8MGMT_APP_HOLD_PHY_RST); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Deassert power_up_rst_n */ 26162306a36Sopenharmony_ci ret = reset_control_deassert(afp->rst); 26262306a36Sopenharmony_ci if (ret) { 26362306a36Sopenharmony_ci dev_err(dev, "unable to deassert pcie_power_up_rst_n\n"); 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci fu740_pcie_init_phy(afp); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Disable pcieauxclk */ 27062306a36Sopenharmony_ci clk_disable_unprepare(afp->pcie_aux); 27162306a36Sopenharmony_ci /* Clear hold_phy_rst */ 27262306a36Sopenharmony_ci writel_relaxed(0x0, afp->mgmt_base + PCIEX8MGMT_APP_HOLD_PHY_RST); 27362306a36Sopenharmony_ci /* Enable pcieauxclk */ 27462306a36Sopenharmony_ci clk_prepare_enable(afp->pcie_aux); 27562306a36Sopenharmony_ci /* Set RC mode */ 27662306a36Sopenharmony_ci writel_relaxed(0x4, afp->mgmt_base + PCIEX8MGMT_DEVICE_TYPE); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic const struct dw_pcie_host_ops fu740_pcie_host_ops = { 28262306a36Sopenharmony_ci .host_init = fu740_pcie_host_init, 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic const struct dw_pcie_ops dw_pcie_ops = { 28662306a36Sopenharmony_ci .start_link = fu740_pcie_start_link, 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int fu740_pcie_probe(struct platform_device *pdev) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct device *dev = &pdev->dev; 29262306a36Sopenharmony_ci struct dw_pcie *pci; 29362306a36Sopenharmony_ci struct fu740_pcie *afp; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci afp = devm_kzalloc(dev, sizeof(*afp), GFP_KERNEL); 29662306a36Sopenharmony_ci if (!afp) 29762306a36Sopenharmony_ci return -ENOMEM; 29862306a36Sopenharmony_ci pci = &afp->pci; 29962306a36Sopenharmony_ci pci->dev = dev; 30062306a36Sopenharmony_ci pci->ops = &dw_pcie_ops; 30162306a36Sopenharmony_ci pci->pp.ops = &fu740_pcie_host_ops; 30262306a36Sopenharmony_ci pci->pp.num_vectors = MAX_MSI_IRQS; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* SiFive specific region: mgmt */ 30562306a36Sopenharmony_ci afp->mgmt_base = devm_platform_ioremap_resource_byname(pdev, "mgmt"); 30662306a36Sopenharmony_ci if (IS_ERR(afp->mgmt_base)) 30762306a36Sopenharmony_ci return PTR_ERR(afp->mgmt_base); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* Fetch GPIOs */ 31062306a36Sopenharmony_ci afp->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); 31162306a36Sopenharmony_ci if (IS_ERR(afp->reset)) 31262306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(afp->reset), "unable to get reset-gpios\n"); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci afp->pwren = devm_gpiod_get_optional(dev, "pwren", GPIOD_OUT_LOW); 31562306a36Sopenharmony_ci if (IS_ERR(afp->pwren)) 31662306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(afp->pwren), "unable to get pwren-gpios\n"); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Fetch clocks */ 31962306a36Sopenharmony_ci afp->pcie_aux = devm_clk_get(dev, "pcie_aux"); 32062306a36Sopenharmony_ci if (IS_ERR(afp->pcie_aux)) 32162306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(afp->pcie_aux), 32262306a36Sopenharmony_ci "pcie_aux clock source missing or invalid\n"); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Fetch reset */ 32562306a36Sopenharmony_ci afp->rst = devm_reset_control_get_exclusive(dev, NULL); 32662306a36Sopenharmony_ci if (IS_ERR(afp->rst)) 32762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(afp->rst), "unable to get reset\n"); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci platform_set_drvdata(pdev, afp); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return dw_pcie_host_init(&pci->pp); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic void fu740_pcie_shutdown(struct platform_device *pdev) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct fu740_pcie *afp = platform_get_drvdata(pdev); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Bring down link, so bootloader gets clean state in case of reboot */ 33962306a36Sopenharmony_ci fu740_pcie_assert_reset(afp); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic const struct of_device_id fu740_pcie_of_match[] = { 34362306a36Sopenharmony_ci { .compatible = "sifive,fu740-pcie", }, 34462306a36Sopenharmony_ci {}, 34562306a36Sopenharmony_ci}; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic struct platform_driver fu740_pcie_driver = { 34862306a36Sopenharmony_ci .driver = { 34962306a36Sopenharmony_ci .name = "fu740-pcie", 35062306a36Sopenharmony_ci .of_match_table = fu740_pcie_of_match, 35162306a36Sopenharmony_ci .suppress_bind_attrs = true, 35262306a36Sopenharmony_ci }, 35362306a36Sopenharmony_ci .probe = fu740_pcie_probe, 35462306a36Sopenharmony_ci .shutdown = fu740_pcie_shutdown, 35562306a36Sopenharmony_ci}; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cibuiltin_platform_driver(fu740_pcie_driver); 358