[PATCH 0/2] pci: Add support for Qualcomm PCIe controller

Add support for the DWC PCIe controllers found on the Qualcomm SoCs, it requires introducing pcie_dw_find_capability() to properly configure the Host controller capabilities.
Signed-off-by: Neil Armstrong neil.armstrong@linaro.org --- Neil Armstrong (2): pci: pcie_dw_common: introduce pcie_dw_find_capability() pci: Add support for Qualcomm PCIe controller
drivers/pci/Kconfig | 8 + drivers/pci/Makefile | 1 + drivers/pci/pcie_dw_common.c | 42 ++++ drivers/pci/pcie_dw_common.h | 2 + drivers/pci/pcie_dw_qcom.c | 571 +++++++++++++++++++++++++++++++++++++++++++ include/pci.h | 4 + 6 files changed, 628 insertions(+) --- base-commit: 7fe55182d9263a62e18b450c97bdf0b8031e5667 change-id: 20241125-topic-pcie-controller-cf11210bf5b3
Best regards,

Add PCIe config space capability search function specific for the host controller, which are bridges *to* PCI devices but are not PCI devices themselves.
Signed-off-by: Neil Armstrong neil.armstrong@linaro.org --- drivers/pci/pcie_dw_common.c | 42 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pcie_dw_common.h | 2 ++ 2 files changed, 44 insertions(+)
diff --git a/drivers/pci/pcie_dw_common.c b/drivers/pci/pcie_dw_common.c index 0673e516c6fee6c01e5a5d23592e0ef55d49d823..78961271a8eef8e4b6991675ee9de2bb1968b8da 100644 --- a/drivers/pci/pcie_dw_common.c +++ b/drivers/pci/pcie_dw_common.c @@ -267,6 +267,48 @@ int pcie_dw_write_config(struct udevice *bus, pci_dev_t bdf, pcie->io.bus_start, pcie->io.size); }
+/* + * These interfaces resemble the pci_find_*capability() interfaces, but these + * are for configuring host controllers, which are bridges *to* PCI devices but + * are not PCI devices themselves. + */ +static u8 pcie_dw_find_next_cap(struct pcie_dw *pci, u8 cap_ptr, u8 cap) +{ + u8 cap_id, next_cap_ptr; + u32 val; + u16 reg; + + if (!cap_ptr) + return 0; + + val = readl(pci->dbi_base + (cap_ptr & ~0x3)); + reg = pci_conv_32_to_size(val, cap_ptr, 2); + cap_id = (reg & 0x00ff); + + if (cap_id > PCI_CAP_ID_MAX) + return 0; + + if (cap_id == cap) + return cap_ptr; + + next_cap_ptr = (reg & 0xff00) >> 8; + return pcie_dw_find_next_cap(pci, next_cap_ptr, cap); +} + +u8 pcie_dw_find_capability(struct pcie_dw *pci, u8 cap) +{ + u8 next_cap_ptr; + u32 val; + u16 reg; + + val = readl(pci->dbi_base + (PCI_CAPABILITY_LIST & ~0x3)); + reg = pci_conv_32_to_size(val, PCI_CAPABILITY_LIST, 2); + + next_cap_ptr = (reg & 0x00ff); + + return pcie_dw_find_next_cap(pci, next_cap_ptr, cap); +} + /** * pcie_dw_setup_host() - Setup the PCIe controller for RC opertaion * diff --git a/drivers/pci/pcie_dw_common.h b/drivers/pci/pcie_dw_common.h index e0f7796f2a873f91ef74bc6ce84966a4215c22f0..8cb99a12ea1a5fd3687f980cc1ff99839d06dbc2 100644 --- a/drivers/pci/pcie_dw_common.h +++ b/drivers/pci/pcie_dw_common.h @@ -139,6 +139,8 @@ int pcie_dw_read_config(const struct udevice *bus, pci_dev_t bdf, uint offset, u int pcie_dw_write_config(struct udevice *bus, pci_dev_t bdf, uint offset, ulong value, enum pci_size_t size);
+u8 pcie_dw_find_capability(struct pcie_dw *pci, u8 cap); + static inline void dw_pcie_dbi_write_enable(struct pcie_dw *pci, bool en) { u32 val;

Add support for the PCIe busses on Qualcomm platforms, by using the pcie_dw_common infrastructure.
The driver is based on the Linux driver but only supporting the "1_9_0" and compatible platforms like: - sa8540p - sc7280 - sc8180x - sc8280xp - sdm845 - sdx55 - sm8150 - sm8250 - sm8350 - sm8450 - sm8550 - sm8650 - x1e80100
But it has only been tested on: - sc7280 - sm8550 - sm8650 - x1e80100
It supports setting the IOMMU SID table for supported platforms.
Signed-off-by: Neil Armstrong neil.armstrong@linaro.org --- drivers/pci/Kconfig | 8 + drivers/pci/Makefile | 1 + drivers/pci/pcie_dw_qcom.c | 571 +++++++++++++++++++++++++++++++++++++++++++++ include/pci.h | 4 + 4 files changed, 584 insertions(+)
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 876a5fa57eed2be1bd1ebbc5466007d9764f5eff..8c94aae04dc8194c89cf0eb02053f8b9b47404ee 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -371,6 +371,14 @@ config PCIE_DW_MESON Say Y here if you want to enable DW PCIe controller support on Amlogic SoCs.
+config PCIE_DW_QCOM + bool "Qualcomm DesignWare based PCIe controller" + depends on ARCH_SNAPDRAGON + select PCIE_DW_COMMON + help + Say Y here if you want to enable DW PCIe controller support on + Qualcomm SoCs. + config PCIE_ROCKCHIP bool "Enable Rockchip PCIe driver" depends on ARCH_ROCKCHIP diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index bf361cd0fbaa4346caaba3831ebdde926b20bc3a..ba53f5949639ce2ecf2509bba2e357a94926b011 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_PCIE_MEDIATEK_GEN3) += pcie_mediatek_gen3.o obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o obj-$(CONFIG_PCIE_DW_ROCKCHIP) += pcie_dw_rockchip.o obj-$(CONFIG_PCIE_DW_MESON) += pcie_dw_meson.o +obj-$(CONFIG_PCIE_DW_QCOM) += pcie_dw_qcom.o obj-$(CONFIG_PCI_BRCMSTB) += pcie_brcmstb.o obj-$(CONFIG_PCI_OCTEONTX) += pci_octeontx.o obj-$(CONFIG_PCIE_OCTEON) += pcie_octeon.o diff --git a/drivers/pci/pcie_dw_qcom.c b/drivers/pci/pcie_dw_qcom.c new file mode 100644 index 0000000000000000000000000000000000000000..39b4cd4efe29bdd8e8c48a193985242878f2b6f0 --- /dev/null +++ b/drivers/pci/pcie_dw_qcom.c @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <clk.h> +#include <dm.h> +#include <generic-phy.h> +#include <pci.h> +#include <u-boot/crc.h> +#include <power-domain.h> +#include <reset.h> +#include <syscon.h> +#include <malloc.h> +#include <power/regulator.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <asm-generic/gpio.h> +#include <dm/device_compat.h> +#include <linux/iopoll.h> +#include <linux/delay.h> +#include <linux/log2.h> +#include <linux/bitfield.h> + +#include "pcie_dw_common.h" + +DECLARE_GLOBAL_DATA_PTR; + +struct qcom_pcie; + +struct qcom_pcie_ops { + int (*config_sid)(struct qcom_pcie *priv); +}; + +#define NUM_SUPPLIES 2 + +struct qcom_pcie { + /* Must be first member of the struct */ + struct pcie_dw dw; + void *parf; + struct phy phy; + struct reset_ctl_bulk rsts; + struct clk_bulk clks; + struct gpio_desc rst_gpio; + struct qcom_pcie_ops *ops; + struct udevice *vregs[NUM_SUPPLIES]; +}; + +/* PARF registers */ +#define PARF_SYS_CTRL 0x00 +#define PARF_PM_CTRL 0x20 +#define PARF_PCS_DEEMPH 0x34 +#define PARF_PCS_SWING 0x38 +#define PARF_PHY_CTRL 0x40 +#define PARF_PHY_REFCLK 0x4c +#define PARF_CONFIG_BITS 0x50 +#define PARF_DBI_BASE_ADDR 0x168 +#define PARF_MHI_CLOCK_RESET_CTRL 0x174 +#define PARF_AXI_MSTR_WR_ADDR_HALT 0x178 +#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8 +#define PARF_Q2A_FLUSH 0x1ac +#define PARF_LTSSM 0x1b0 +#define PARF_SID_OFFSET 0x234 +#define PARF_BDF_TRANSLATE_CFG 0x24c +#define PARF_SLV_ADDR_SPACE_SIZE 0x358 +#define PARF_DEVICE_TYPE 0x1000 +#define PARF_BDF_TO_SID_TABLE_N 0x2000 + +/* ELBI registers */ +#define ELBI_SYS_CTRL 0x04 + +/* DBI registers */ +#define AXI_MSTR_RESP_COMP_CTRL0 0x818 +#define AXI_MSTR_RESP_COMP_CTRL1 0x81c +#define MISC_CONTROL_1_REG 0x8bc + +/* MHI registers */ +#define PARF_DEBUG_CNT_PM_LINKST_IN_L2 0xc04 +#define PARF_DEBUG_CNT_PM_LINKST_IN_L1 0xc0c +#define PARF_DEBUG_CNT_PM_LINKST_IN_L0S 0xc10 +#define PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L1 0xc84 +#define PARF_DEBUG_CNT_AUX_CLK_IN_L1SUB_L2 0xc88 + +/* PARF_SYS_CTRL register fields */ +#define MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN BIT(29) +#define MST_WAKEUP_EN BIT(13) +#define SLV_WAKEUP_EN BIT(12) +#define MSTR_ACLK_CGC_DIS BIT(10) +#define SLV_ACLK_CGC_DIS BIT(9) +#define CORE_CLK_CGC_DIS BIT(6) +#define AUX_PWR_DET BIT(4) +#define L23_CLK_RMV_DIS BIT(2) +#define L1_CLK_RMV_DIS BIT(1) + +/* PARF_PM_CTRL register fields */ +#define REQ_NOT_ENTR_L1 BIT(5) + +/* PARF_PCS_DEEMPH register fields */ +#define PCS_DEEMPH_TX_DEEMPH_GEN1(x) FIELD_PREP(GENMASK(21, 16), x) +#define PCS_DEEMPH_TX_DEEMPH_GEN2_3_5DB(x) FIELD_PREP(GENMASK(13, 8), x) +#define PCS_DEEMPH_TX_DEEMPH_GEN2_6DB(x) FIELD_PREP(GENMASK(5, 0), x) + +/* PARF_PCS_SWING register fields */ +#define PCS_SWING_TX_SWING_FULL(x) FIELD_PREP(GENMASK(14, 8), x) +#define PCS_SWING_TX_SWING_LOW(x) FIELD_PREP(GENMASK(6, 0), x) + +/* PARF_PHY_CTRL register fields */ +#define PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK GENMASK(20, 16) +#define PHY_CTRL_PHY_TX0_TERM_OFFSET(x) FIELD_PREP(PHY_CTRL_PHY_TX0_TERM_OFFSET_MASK, x) +#define PHY_TEST_PWR_DOWN BIT(0) + +/* PARF_PHY_REFCLK register fields */ +#define PHY_REFCLK_SSP_EN BIT(16) +#define PHY_REFCLK_USE_PAD BIT(12) + +/* PARF_CONFIG_BITS register fields */ +#define PHY_RX0_EQ(x) FIELD_PREP(GENMASK(26, 24), x) + +/* PARF_SLV_ADDR_SPACE_SIZE register value */ +#define SLV_ADDR_SPACE_SZ 0x10000000 + +/* PARF_MHI_CLOCK_RESET_CTRL register fields */ +#define AHB_CLK_EN BIT(0) +#define MSTR_AXI_CLK_EN BIT(1) +#define BYPASS BIT(4) + +/* PARF_AXI_MSTR_WR_ADDR_HALT register fields */ +#define EN BIT(31) + +/* PARF_LTSSM register fields */ +#define LTSSM_EN BIT(8) + +/* PARF_DEVICE_TYPE register fields */ +#define DEVICE_TYPE_RC 0x4 + +/* ELBI_SYS_CTRL register fields */ +#define ELBI_SYS_CTRL_LT_ENABLE BIT(0) + +/* AXI_MSTR_RESP_COMP_CTRL0 register fields */ +#define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_2K 0x4 +#define CFG_REMOTE_RD_REQ_BRIDGE_SIZE_4K 0x5 + +/* AXI_MSTR_RESP_COMP_CTRL1 register fields */ +#define CFG_BRIDGE_SB_INIT BIT(0) + +/* MISC_CONTROL_1_REG register fields */ +#define DBI_RO_WR_EN 1 + +/* PCI_EXP_SLTCAP register fields */ +#define PCIE_CAP_SLOT_POWER_LIMIT_VAL FIELD_PREP(PCI_EXP_SLTCAP_SPLV, 250) +#define PCIE_CAP_SLOT_POWER_LIMIT_SCALE FIELD_PREP(PCI_EXP_SLTCAP_SPLS, 1) +#define PCIE_CAP_SLOT_VAL (PCI_EXP_SLTCAP_ABP | \ + PCI_EXP_SLTCAP_PCP | \ + PCI_EXP_SLTCAP_MRLSP | \ + PCI_EXP_SLTCAP_AIP | \ + PCI_EXP_SLTCAP_PIP | \ + PCI_EXP_SLTCAP_HPS | \ + PCI_EXP_SLTCAP_HPC | \ + PCI_EXP_SLTCAP_EIP | \ + PCIE_CAP_SLOT_POWER_LIMIT_VAL | \ + PCIE_CAP_SLOT_POWER_LIMIT_SCALE) + +#define PERST_DELAY_US 1000 + +#define LINK_WAIT_MAX_RETRIES 10 +#define LINK_WAIT_USLEEP 100000 + +#define QCOM_PCIE_CRC8_POLYNOMIAL (BIT(2) | BIT(1) | BIT(0)) + +#define CRC8_TABLE_SIZE 256 + +static bool qcom_pcie_wait_link_up(struct qcom_pcie *priv) +{ + u8 offset = pcie_dw_find_capability(&priv->dw, PCI_CAP_ID_EXP); + unsigned int cnt = 0; + u16 val; + + do { + val = readw(priv->dw.dbi_base + offset + PCI_EXP_LNKSTA); + + if ((val & PCI_EXP_LNKSTA_DLLLA)) + return true; + cnt++; + + udelay(LINK_WAIT_USLEEP); + } while (cnt < LINK_WAIT_MAX_RETRIES); + + return false; +} + +static void qcom_pcie_clear_aspm_l0s(struct qcom_pcie *priv) +{ + u8 offset = pcie_dw_find_capability(&priv->dw, PCI_CAP_ID_EXP); + u32 val; + + dw_pcie_dbi_write_enable(&priv->dw, true); + + val = readl(priv->dw.dbi_base + offset + PCI_EXP_LNKCAP); + val &= ~PCI_EXP_LNKCAP_ASPM_L0S; + writel(val, priv->dw.dbi_base + offset + PCI_EXP_LNKCAP); + + dw_pcie_dbi_write_enable(&priv->dw, false); +} + +static void qcom_pcie_clear_hpc(struct qcom_pcie *priv) +{ + u8 offset = pcie_dw_find_capability(&priv->dw, PCI_CAP_ID_EXP); + u32 val; + + dw_pcie_dbi_write_enable(&priv->dw, true); + + val = readl(priv->dw.dbi_base + offset + PCI_EXP_SLTCAP); + val &= ~PCI_EXP_SLTCAP_HPC; + writel(val, priv->dw.dbi_base + offset + PCI_EXP_SLTCAP); + + dw_pcie_dbi_write_enable(&priv->dw, false); +} + +static void qcom_pcie_set_lanes(struct qcom_pcie *priv, unsigned int lanes) +{ + u8 offset = pcie_dw_find_capability(&priv->dw, PCI_CAP_ID_EXP); + u32 val; + + val = readl(priv->dw.dbi_base + offset + PCI_EXP_LNKCAP); + val &= ~PCI_EXP_LNKCAP_MLW; + val |= FIELD_PREP(PCI_EXP_LNKCAP_MLW, lanes); + writel(val, priv->dw.dbi_base + offset + PCI_EXP_LNKCAP); +} + +static int qcom_pcie_config_sid_1_9_0(struct qcom_pcie *priv) +{ + /* iommu map structure */ + struct { + u32 bdf; + u32 phandle; + u32 smmu_sid; + u32 smmu_sid_len; + } *map; + void *bdf_to_sid_base = priv->parf + PARF_BDF_TO_SID_TABLE_N; + int i, nr_map, size = 0; + u32 smmu_sid_base; + + dev_read_prop(priv->dw.dev, "iommu-map", &size); + if (!size) + return 0; + + map = malloc(size); + if (!map) + return -ENOMEM; + + dev_read_u32_array(priv->dw.dev, "iommu-map", (u32 *)map, size / sizeof(u32)); + + nr_map = size / (sizeof(*map)); + + /* Registers need to be zero out first */ + memset_io(bdf_to_sid_base, 0, CRC8_TABLE_SIZE * sizeof(u32)); + + /* Extract the SMMU SID base from the first entry of iommu-map */ + smmu_sid_base = map[0].smmu_sid; + + /* Look for an available entry to hold the mapping */ + for (i = 0; i < nr_map; i++) { + __be16 bdf_be = cpu_to_be16(map[i].bdf); + u32 val; + u8 hash; + + hash = crc8(QCOM_PCIE_CRC8_POLYNOMIAL, (u8 *)&bdf_be, sizeof(bdf_be)); + + val = readl(bdf_to_sid_base + hash * sizeof(u32)); + + /* If the register is already populated, look for next available entry */ + while (val) { + u8 current_hash = hash++; + u8 next_mask = 0xff; + + /* If NEXT field is NULL then update it with next hash */ + if (!(val & next_mask)) { + val |= (u32)hash; + writel(val, bdf_to_sid_base + current_hash * sizeof(u32)); + } + + val = readl(bdf_to_sid_base + hash * sizeof(u32)); + } + + /* BDF [31:16] | SID [15:8] | NEXT [7:0] */ + val = map[i].bdf << 16 | (map[i].smmu_sid - smmu_sid_base) << 8 | 0; + writel(val, bdf_to_sid_base + hash * sizeof(u32)); + } + + free(map); + + return 0; +} + +static void qcom_pcie_configure(struct qcom_pcie *priv) +{ + u32 val; + + dw_pcie_dbi_write_enable(&priv->dw, true); + + val = readl(priv->dw.dbi_base + PCIE_PORT_LINK_CONTROL); + val &= ~PORT_LINK_FAST_LINK_MODE; + val |= PORT_LINK_DLL_LINK_EN; + val &= ~PORT_LINK_MODE_MASK; + val |= PORT_LINK_MODE_2_LANES; + writel(val, priv->dw.dbi_base + PCIE_PORT_LINK_CONTROL); + + val = readl(priv->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_LINK_WIDTH_MASK; + val |= PORT_LOGIC_LINK_WIDTH_2_LANES; + writel(val, priv->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + + qcom_pcie_set_lanes(priv, 2); + + dw_pcie_dbi_write_enable(&priv->dw, false); +} + +static int qcom_pcie_init_port(struct udevice *dev) +{ + struct qcom_pcie *priv = dev_get_priv(dev); + int vreg, ret; + u32 val; + + dm_gpio_set_value(&priv->rst_gpio, 1); + udelay(PERST_DELAY_US); + + ret = generic_phy_init(&priv->phy); + if (ret) { + dev_err(dev, "failed to init phy (%d)\n", ret); + return ret; + } + + udelay(PERST_DELAY_US); + + for (vreg = 0; vreg < NUM_SUPPLIES; ++vreg) { + ret = regulator_set_enable(priv->vregs[vreg], true); + if (ret && ret != -ENOSYS) + dev_warn(dev, "failed to enable regulator %d (%d)\n", vreg, ret); + } + + ret = clk_enable_bulk(&priv->clks); + if (ret) { + dev_err(dev, "failed to enable clocks (%d)\n", ret); + goto err_power_off_phy; + } + + ret = reset_assert_bulk(&priv->rsts); + if (ret) { + dev_err(dev, "failed to assert resets (%d)\n", ret); + goto err_disable_clks; + } + + udelay(PERST_DELAY_US); + + ret = reset_deassert_bulk(&priv->rsts); + if (ret) { + dev_err(dev, "failed to deassert resets (%d)\n", ret); + goto err_power_off_phy; + } + + udelay(PERST_DELAY_US); + + /* configure PCIe to RC mode */ + writel(DEVICE_TYPE_RC, priv->parf + PARF_DEVICE_TYPE); + + /* enable PCIe clocks and resets */ + val = readl(priv->parf + PARF_PHY_CTRL); + val &= ~PHY_TEST_PWR_DOWN; + writel(val, priv->parf + PARF_PHY_CTRL); + + /* change DBI base address */ + writel(0, priv->parf + PARF_DBI_BASE_ADDR); + + /* MAC PHY_POWERDOWN MUX DISABLE */ + val = readl(priv->parf + PARF_SYS_CTRL); + val &= ~MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN; + writel(val, priv->parf + PARF_SYS_CTRL); + + val = readl(priv->parf + PARF_MHI_CLOCK_RESET_CTRL); + val |= BYPASS; + writel(val, priv->parf + PARF_MHI_CLOCK_RESET_CTRL); + + /* Enable L1 and L1SS */ + val = readl(priv->parf + PARF_PM_CTRL); + val &= ~REQ_NOT_ENTR_L1; + writel(val, priv->parf + PARF_PM_CTRL); + + val = readl(priv->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); + val |= EN; + writel(val, priv->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2); + + ret = generic_phy_power_on(&priv->phy); + if (ret) { + dev_err(dev, "failed to power on phy (%d)\n", ret); + goto err_exit_phy; + } + + qcom_pcie_clear_aspm_l0s(priv); + qcom_pcie_clear_hpc(priv); + + mdelay(100); + dm_gpio_set_value(&priv->rst_gpio, 0); + udelay(PERST_DELAY_US); + + if (priv->ops && priv->ops->config_sid) { + ret = priv->ops->config_sid(priv); + if (ret) + goto err_deassert_bulk; + } + + qcom_pcie_configure(priv); + + pcie_dw_setup_host(&priv->dw); + + /* enable link training */ + val = readl(priv->parf + PARF_LTSSM); + val |= LTSSM_EN; + writel(val, priv->parf + PARF_LTSSM); + + return 0; +err_deassert_bulk: + reset_assert_bulk(&priv->rsts); +err_disable_clks: + clk_disable_bulk(&priv->clks); +err_power_off_phy: + generic_phy_power_off(&priv->phy); +err_exit_phy: + generic_phy_exit(&priv->phy); + + return ret; +} + +static const char *qcom_pcie_vregs[NUM_SUPPLIES] = { + "vdda-supply", + "vddpe-3v3-supply", +}; + +static int qcom_pcie_parse_dt(struct udevice *dev) +{ + struct qcom_pcie *priv = dev_get_priv(dev); + int vreg, ret; + + priv->dw.dbi_base = dev_read_addr_name_ptr(dev, "dbi"); + if (!priv->dw.dbi_base) + return -EINVAL; + + dev_dbg(dev, "DBI address is 0x%p\n", priv->dw.dbi_base); + + priv->dw.atu_base = dev_read_addr_name_ptr(dev, "atu"); + if (!priv->dw.atu_base) + return -EINVAL; + + dev_dbg(dev, "ATU address is 0x%p\n", priv->dw.atu_base); + + priv->parf = dev_read_addr_name_ptr(dev, "parf"); + if (!priv->parf) + return -EINVAL; + + dev_dbg(dev, "PARF address is 0x%p\n", priv->parf); + + ret = gpio_request_by_name(dev, "perst-gpios", 0, + &priv->rst_gpio, GPIOD_IS_OUT); + if (ret) { + dev_err(dev, "failed to find reset-gpios property\n"); + return ret; + } + + ret = reset_get_bulk(dev, &priv->rsts); + if (ret) { + dev_err(dev, "failed to get resets (%d)\n", ret); + return ret; + } + + ret = clk_get_bulk(dev, &priv->clks); + if (ret) { + dev_err(dev, "failed to get clocks (%d)\n", ret); + return ret; + } + + ret = generic_phy_get_by_index(dev, 0, &priv->phy); + if (ret) { + dev_err(dev, "failed to get pcie phy (%d)\n", ret); + return ret; + } + + for (vreg = 0; vreg < NUM_SUPPLIES; ++vreg) { + ret = device_get_supply_regulator(dev, qcom_pcie_vregs[vreg], &priv->vregs[vreg]); + if (ret) + dev_warn(dev, "failed to get regulator %d (%d)\n", vreg, ret); + } + + return 0; +} + +/** + * qcom_pcie_probe() - Probe the PCIe bus for active link + * + * @dev: A pointer to the device being operated on + * + * Probe for an active link on the PCIe bus and configure the controller + * to enable this port. + * + * Return: 0 on success, else -ENODEV + */ +static int qcom_pcie_probe(struct udevice *dev) +{ + struct qcom_pcie *priv = dev_get_priv(dev); + struct udevice *ctlr = pci_get_controller(dev); + struct pci_controller *hose = dev_get_uclass_priv(ctlr); + int ret = 0; + + priv->dw.first_busno = dev_seq(dev); + priv->dw.dev = dev; + + ret = qcom_pcie_parse_dt(dev); + if (ret) + return ret; + + ret = qcom_pcie_init_port(dev); + if (ret) { + dm_gpio_free(dev, &priv->rst_gpio); + return ret; + } + + if (qcom_pcie_wait_link_up(priv)) + printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", + dev_seq(dev), pcie_dw_get_link_speed(&priv->dw), + pcie_dw_get_link_width(&priv->dw), + hose->first_busno); + else + printf("PCIE-%d: Link up timeout\n", dev_seq(dev)); + + return pcie_dw_prog_outbound_atu_unroll(&priv->dw, + PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_MEM, + priv->dw.mem.phys_start, + priv->dw.mem.bus_start, + priv->dw.mem.size); +} + +static const struct dm_pci_ops qcom_pcie_ops = { + .read_config = pcie_dw_read_config, + .write_config = pcie_dw_write_config, +}; + +static const struct qcom_pcie_ops ops_1_9_0 = { + .config_sid = qcom_pcie_config_sid_1_9_0, +}; + +static const struct udevice_id qcom_pcie_ids[] = { + { .compatible = "qcom,pcie-sa8540p", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-sc7280", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-sc8180x", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-sc8280xp", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-sdm845" }, + { .compatible = "qcom,pcie-sdx55", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-sm8150", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-sm8250", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-sm8350", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-sm8450-pcie0", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-sm8450-pcie1", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-sm8550", .data = (ulong)&ops_1_9_0 }, + { .compatible = "qcom,pcie-x1e80100", .data = (ulong)&ops_1_9_0 }, + { } +}; + +U_BOOT_DRIVER(qcom_dw_pcie) = { + .name = "pcie_dw_qcom", + .id = UCLASS_PCI, + .of_match = qcom_pcie_ids, + .ops = &qcom_pcie_ops, + .probe = qcom_pcie_probe, + .priv_auto = sizeof(struct qcom_pcie), +}; diff --git a/include/pci.h b/include/pci.h index 5fea815b48c326cde13ef35e24affd3ff263606e..4b0facd6dcf1b373c739d4f18ef385159c7bc49b 100644 --- a/include/pci.h +++ b/include/pci.h @@ -390,6 +390,9 @@ #define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */ #define PCI_EXP_LNKCAP_SLS_8_0GB 0x00000003 /* LNKCAP2 SLS Vector bit 2 */ #define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ +#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ +#define PCI_EXP_LNKCAP_ASPM_L0S 0x00000400 /* ASPM L0s Support */ +#define PCI_EXP_LNKCAP_ASPM_L1 0x00000800 /* ASPM L1 Support */ #define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */ #define PCI_EXP_LNKCTL 16 /* Link Control */ #define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */ @@ -404,6 +407,7 @@ #define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ #define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */ #define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCAP_HPC 0x00000040 /* Hot-Plug Capable */ #define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */ #define PCI_EXP_RTCTL 28 /* Root Control */ #define PCI_EXP_RTCTL_CRSSVE 0x0010 /* CRS Software Visibility Enable */

On Mon, 25 Nov 2024 10:46:15 +0100, Neil Armstrong wrote:
Add support for the DWC PCIe controllers found on the Qualcomm SoCs, it requires introducing pcie_dw_find_capability() to properly configure the Host controller capabilities.
Applied, thanks!
[1/2] pci: pcie_dw_common: introduce pcie_dw_find_capability() https://source.denx.de/u-boot/custodians/u-boot-snapdragon/-/commit/aeeebdad... [2/2] pci: Add support for Qualcomm PCIe controller https://source.denx.de/u-boot/custodians/u-boot-snapdragon/-/commit/5b7ec7fb...
Best regards,
participants (2)
-
Caleb Connolly
-
Neil Armstrong