Merge branch 'pci/controller/qcom'

- Select PCI Power Control Slot driver so slot voltage rails can be turned
  on/off if described in Root Port device tree node (Qiang Yu)

- Parse only PCI bridge child nodes in device tree, skipping unrelated
  nodes such as OPP (Operating Performance Points), which caused probe
  failures (Krishna Chaitanya Chundru)

- Add 8.0 GT/s and 32.0 GT/s equalization settings (Ziyue Zhang)

- Fix typo in CURSOR macro names (Ziyue Zhang)

- Consolidate Root Port 'phy' and 'reset' properties in struct
  qcom_pcie_port, regardless of whether we got them from the Root Port node
  or the host bridge node (Manivannan Sadhasivam)

- Fetch and map the ELBI register space in the DWC core rather than in each
  driver individually (Krishna Chaitanya Chundru)

- Enable ECAM mechanism in DWC core by setting up iATU with 'CFG Shift
  Feature' and use this in the qcom driver (Krishna Chaitanya Chundru)

* pci/controller/qcom:
  PCI: dwc: Support ECAM mechanism by enabling iATU 'CFG Shift Feature'
  PCI: qcom: Prepare for the DWC ECAM enablement
  PCI: dwc: Prepare the driver for enabling ECAM mechanism using iATU 'CFG Shift Feature'
  PCI: dwc: Add support for ELBI resource mapping
  PCI: qcom: Move host bridge 'phy' and 'reset' pointers to struct qcom_pcie_port
  PCI: qcom: Fix macro typo for CURSOR
  PCI: qcom: Add equalization settings for 8.0 GT/s and 32.0 GT/s
  PCI: qcom: Restrict port parsing only to PCIe bridge child nodes
  PCI: qcom: Select PCI Power Control Slot driver
This commit is contained in:
Bjorn Helgaas 2025-10-03 12:13:20 -05:00
commit 531abff0fa
10 changed files with 343 additions and 152 deletions

View File

@ -20,6 +20,7 @@ config PCIE_DW_HOST
bool
select PCIE_DW
select IRQ_MSI_LIB
select PCI_HOST_COMMON
config PCIE_DW_EP
bool
@ -298,6 +299,7 @@ config PCIE_QCOM
select CRC8
select PCIE_QCOM_COMMON
select PCI_HOST_COMMON
select PCI_PWRCTRL_SLOT
help
Say Y here to enable PCIe controller support on Qualcomm SoCs. The
PCIe controller uses the DesignWare core plus Qualcomm-specific

View File

@ -53,7 +53,6 @@
struct exynos_pcie {
struct dw_pcie pci;
void __iomem *elbi_base;
struct clk_bulk_data *clks;
struct phy *phy;
struct regulator_bulk_data supplies[2];
@ -71,73 +70,78 @@ static u32 exynos_pcie_readl(void __iomem *base, u32 reg)
static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on)
{
struct dw_pcie *pci = &ep->pci;
u32 val;
val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC);
val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_SLV_AWMISC);
if (on)
val |= PCIE_ELBI_SLV_DBI_ENABLE;
else
val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
exynos_pcie_writel(pci->elbi_base, val, PCIE_ELBI_SLV_AWMISC);
}
static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on)
{
struct dw_pcie *pci = &ep->pci;
u32 val;
val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC);
val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_SLV_ARMISC);
if (on)
val |= PCIE_ELBI_SLV_DBI_ENABLE;
else
val &= ~PCIE_ELBI_SLV_DBI_ENABLE;
exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
exynos_pcie_writel(pci->elbi_base, val, PCIE_ELBI_SLV_ARMISC);
}
static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep)
{
struct dw_pcie *pci = &ep->pci;
u32 val;
val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
val = exynos_pcie_readl(pci->elbi_base, PCIE_CORE_RESET);
val &= ~PCIE_CORE_RESET_ENABLE;
exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET);
exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET);
exynos_pcie_writel(pci->elbi_base, val, PCIE_CORE_RESET);
exynos_pcie_writel(pci->elbi_base, 0, PCIE_STICKY_RESET);
exynos_pcie_writel(pci->elbi_base, 0, PCIE_NONSTICKY_RESET);
}
static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep)
{
struct dw_pcie *pci = &ep->pci;
u32 val;
val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET);
val = exynos_pcie_readl(pci->elbi_base, PCIE_CORE_RESET);
val |= PCIE_CORE_RESET_ENABLE;
exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET);
exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET);
exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET);
exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET);
exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET);
exynos_pcie_writel(pci->elbi_base, val, PCIE_CORE_RESET);
exynos_pcie_writel(pci->elbi_base, 1, PCIE_STICKY_RESET);
exynos_pcie_writel(pci->elbi_base, 1, PCIE_NONSTICKY_RESET);
exynos_pcie_writel(pci->elbi_base, 1, PCIE_APP_INIT_RESET);
exynos_pcie_writel(pci->elbi_base, 0, PCIE_APP_INIT_RESET);
}
static int exynos_pcie_start_link(struct dw_pcie *pci)
{
struct exynos_pcie *ep = to_exynos_pcie(pci);
u32 val;
val = exynos_pcie_readl(ep->elbi_base, PCIE_SW_WAKE);
val = exynos_pcie_readl(pci->elbi_base, PCIE_SW_WAKE);
val &= ~PCIE_BUS_EN;
exynos_pcie_writel(ep->elbi_base, val, PCIE_SW_WAKE);
exynos_pcie_writel(pci->elbi_base, val, PCIE_SW_WAKE);
/* assert LTSSM enable */
exynos_pcie_writel(ep->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
exynos_pcie_writel(pci->elbi_base, PCIE_ELBI_LTSSM_ENABLE,
PCIE_APP_LTSSM_ENABLE);
return 0;
}
static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep)
{
u32 val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE);
struct dw_pcie *pci = &ep->pci;
exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE);
u32 val = exynos_pcie_readl(pci->elbi_base, PCIE_IRQ_PULSE);
exynos_pcie_writel(pci->elbi_base, val, PCIE_IRQ_PULSE);
}
static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
@ -150,12 +154,14 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep)
{
struct dw_pcie *pci = &ep->pci;
u32 val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
IRQ_INTC_ASSERT | IRQ_INTD_ASSERT;
exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE);
exynos_pcie_writel(ep->elbi_base, 0, PCIE_IRQ_EN_LEVEL);
exynos_pcie_writel(ep->elbi_base, 0, PCIE_IRQ_EN_SPECIAL);
exynos_pcie_writel(pci->elbi_base, val, PCIE_IRQ_EN_PULSE);
exynos_pcie_writel(pci->elbi_base, 0, PCIE_IRQ_EN_LEVEL);
exynos_pcie_writel(pci->elbi_base, 0, PCIE_IRQ_EN_SPECIAL);
}
static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base,
@ -211,8 +217,7 @@ static struct pci_ops exynos_pci_ops = {
static bool exynos_pcie_link_up(struct dw_pcie *pci)
{
struct exynos_pcie *ep = to_exynos_pcie(pci);
u32 val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_RDLH_LINKUP);
u32 val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_RDLH_LINKUP);
return val & PCIE_ELBI_XMLH_LINKUP;
}
@ -295,11 +300,6 @@ static int exynos_pcie_probe(struct platform_device *pdev)
if (IS_ERR(ep->phy))
return PTR_ERR(ep->phy);
/* External Local Bus interface (ELBI) registers */
ep->elbi_base = devm_platform_ioremap_resource_byname(pdev, "elbi");
if (IS_ERR(ep->elbi_base))
return PTR_ERR(ep->elbi_base);
ret = devm_clk_bulk_get_all_enabled(dev, &ep->clks);
if (ret < 0)
return ret;

View File

@ -352,6 +352,7 @@ static int al_pcie_probe(struct platform_device *pdev)
return -ENOENT;
}
al_pcie->ecam_size = resource_size(ecam_res);
pci->pp.native_ecam = true;
controller_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"controller");

View File

@ -8,6 +8,7 @@
* Author: Jingoo Han <jg1.han@samsung.com>
*/
#include <linux/align.h>
#include <linux/iopoll.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqchip/irq-msi-lib.h>
@ -32,6 +33,8 @@ static struct pci_ops dw_child_pcie_ops;
MSI_FLAG_PCI_MSIX | \
MSI_GENERIC_FLAGS_MASK)
#define IS_256MB_ALIGNED(x) IS_ALIGNED(x, SZ_256M)
static const struct msi_parent_ops dw_pcie_msi_parent_ops = {
.required_flags = DW_PCIE_MSI_FLAGS_REQUIRED,
.supported_flags = DW_PCIE_MSI_FLAGS_SUPPORTED,
@ -413,6 +416,95 @@ static void dw_pcie_host_request_msg_tlp_res(struct dw_pcie_rp *pp)
}
}
static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct dw_pcie_ob_atu_cfg atu = {0};
resource_size_t bus_range_max;
struct resource_entry *bus;
int ret;
bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);
/*
* Root bus under the host bridge doesn't require any iATU configuration
* as DBI region will be used to access root bus config space.
* Immediate bus under Root Bus, needs type 0 iATU configuration and
* remaining buses need type 1 iATU configuration.
*/
atu.index = 0;
atu.type = PCIE_ATU_TYPE_CFG0;
atu.parent_bus_addr = pp->cfg0_base + SZ_1M;
/* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */
atu.size = SZ_1M;
atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;
ret = dw_pcie_prog_outbound_atu(pci, &atu);
if (ret)
return ret;
bus_range_max = resource_size(bus->res);
if (bus_range_max < 2)
return 0;
/* Configure remaining buses in type 1 iATU configuration */
atu.index = 1;
atu.type = PCIE_ATU_TYPE_CFG1;
atu.parent_bus_addr = pp->cfg0_base + SZ_2M;
atu.size = (SZ_1M * bus_range_max) - SZ_2M;
atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;
return dw_pcie_prog_outbound_atu(pci, &atu);
}
static int dw_pcie_create_ecam_window(struct dw_pcie_rp *pp, struct resource *res)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct device *dev = pci->dev;
struct resource_entry *bus;
bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS);
if (!bus)
return -ENODEV;
pp->cfg = pci_ecam_create(dev, res, bus->res, &pci_generic_ecam_ops);
if (IS_ERR(pp->cfg))
return PTR_ERR(pp->cfg);
pci->dbi_base = pp->cfg->win;
pci->dbi_phys_addr = res->start;
return 0;
}
static bool dw_pcie_ecam_enabled(struct dw_pcie_rp *pp, struct resource *config_res)
{
struct resource *bus_range;
u64 nr_buses;
/* Vendor glue drivers may implement their own ECAM mechanism */
if (pp->native_ecam)
return false;
/*
* PCIe spec r6.0, sec 7.2.2 mandates the base address used for ECAM to
* be aligned on a 2^(n+20) byte boundary, where n is the number of bits
* used for representing 'bus' in BDF. Since the DWC cores always use 8
* bits for representing 'bus', the base address has to be aligned to
* 2^28 byte boundary, which is 256 MiB.
*/
if (!IS_256MB_ALIGNED(config_res->start))
return false;
bus_range = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS)->res;
if (!bus_range)
return false;
nr_buses = resource_size(config_res) >> PCIE_ECAM_BUS_SHIFT;
return nr_buses >= resource_size(bus_range);
}
static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@ -422,10 +514,6 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)
struct resource *res;
int ret;
ret = dw_pcie_get_resources(pci);
if (ret)
return ret;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
if (!res) {
dev_err(dev, "Missing \"config\" reg space\n");
@ -435,9 +523,32 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp)
pp->cfg0_size = resource_size(res);
pp->cfg0_base = res->start;
pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res);
if (IS_ERR(pp->va_cfg0_base))
return PTR_ERR(pp->va_cfg0_base);
pp->ecam_enabled = dw_pcie_ecam_enabled(pp, res);
if (pp->ecam_enabled) {
ret = dw_pcie_create_ecam_window(pp, res);
if (ret)
return ret;
pp->bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops;
pp->bridge->sysdata = pp->cfg;
pp->cfg->priv = pp;
} else {
pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res);
if (IS_ERR(pp->va_cfg0_base))
return PTR_ERR(pp->va_cfg0_base);
/* Set default bus ops */
pp->bridge->ops = &dw_pcie_ops;
pp->bridge->child_ops = &dw_child_pcie_ops;
pp->bridge->sysdata = pp;
}
ret = dw_pcie_get_resources(pci);
if (ret) {
if (pp->cfg)
pci_ecam_free(pp->cfg);
return ret;
}
/* Get the I/O range from DT */
win = resource_list_first_type(&pp->bridge->windows, IORESOURCE_IO);
@ -476,14 +587,10 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
return ret;
/* Set default bus ops */
bridge->ops = &dw_pcie_ops;
bridge->child_ops = &dw_child_pcie_ops;
if (pp->ops->init) {
ret = pp->ops->init(pp);
if (ret)
return ret;
goto err_free_ecam;
}
if (pci_msi_enabled()) {
@ -525,6 +632,14 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
goto err_free_msi;
if (pp->ecam_enabled) {
ret = dw_pcie_config_ecam_iatu(pp);
if (ret) {
dev_err(dev, "Failed to configure iATU in ECAM mode\n");
goto err_free_msi;
}
}
/*
* Allocate the resource for MSG TLP before programming the iATU
* outbound window in dw_pcie_setup_rc(). Since the allocation depends
@ -560,8 +675,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
/* Ignore errors, the link may come up later */
dw_pcie_wait_for_link(pci);
bridge->sysdata = pp;
ret = pci_host_probe(bridge);
if (ret)
goto err_stop_link;
@ -587,6 +700,10 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
if (pp->ops->deinit)
pp->ops->deinit(pp);
err_free_ecam:
if (pp->cfg)
pci_ecam_free(pp->cfg);
return ret;
}
EXPORT_SYMBOL_GPL(dw_pcie_host_init);
@ -609,6 +726,9 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp)
if (pp->ops->deinit)
pp->ops->deinit(pp);
if (pp->cfg)
pci_ecam_free(pp->cfg);
}
EXPORT_SYMBOL_GPL(dw_pcie_host_deinit);

View File

@ -167,6 +167,14 @@ int dw_pcie_get_resources(struct dw_pcie *pci)
}
}
/* ELBI is an optional resource */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
if (res) {
pci->elbi_base = devm_ioremap_resource(pci->dev, res);
if (IS_ERR(pci->elbi_base))
return PTR_ERR(pci->elbi_base);
}
/* LLDD is supposed to manually switch the clocks and resets state */
if (dw_pcie_cap_is(pci, REQ_RES)) {
ret = dw_pcie_get_clocks(pci);
@ -500,7 +508,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
val = dw_pcie_enable_ecrc(val);
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val);
val = PCIE_ATU_ENABLE;
val = PCIE_ATU_ENABLE | atu->ctrl2;
if (atu->type == PCIE_ATU_TYPE_MSG) {
/* The data-less messages only for now */
val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code;

View File

@ -20,6 +20,7 @@
#include <linux/irq.h>
#include <linux/msi.h>
#include <linux/pci.h>
#include <linux/pci-ecam.h>
#include <linux/reset.h>
#include <linux/pci-epc.h>
@ -124,7 +125,6 @@
#define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16)
#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24
#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK GENMASK(25, 24)
#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_16_0GT 0x1
#define GEN3_EQ_CONTROL_OFF 0x8A8
#define GEN3_EQ_CONTROL_OFF_FB_MODE GENMASK(3, 0)
@ -135,8 +135,8 @@
#define GEN3_EQ_FB_MODE_DIR_CHANGE_OFF 0x8AC
#define GEN3_EQ_FMDC_T_MIN_PHASE23 GENMASK(4, 0)
#define GEN3_EQ_FMDC_N_EVALS GENMASK(9, 5)
#define GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA GENMASK(13, 10)
#define GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA GENMASK(17, 14)
#define GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA GENMASK(13, 10)
#define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA GENMASK(17, 14)
#define PCIE_PORT_MULTI_LANE_CTRL 0x8C0
#define PORT_MLTI_UPCFG_SUPPORT BIT(7)
@ -170,6 +170,7 @@
#define PCIE_ATU_REGION_CTRL2 0x004
#define PCIE_ATU_ENABLE BIT(31)
#define PCIE_ATU_BAR_MODE_ENABLE BIT(30)
#define PCIE_ATU_CFG_SHIFT_MODE_ENABLE BIT(28)
#define PCIE_ATU_INHIBIT_PAYLOAD BIT(22)
#define PCIE_ATU_FUNC_NUM_MATCH_EN BIT(19)
#define PCIE_ATU_LOWER_BASE 0x008
@ -388,6 +389,7 @@ struct dw_pcie_ob_atu_cfg {
u8 func_no;
u8 code;
u8 routing;
u32 ctrl2;
u64 parent_bus_addr;
u64 pci_addr;
u64 size;
@ -426,6 +428,9 @@ struct dw_pcie_rp {
struct resource *msg_res;
bool use_linkup_irq;
struct pci_eq_presets presets;
struct pci_config_window *cfg;
bool ecam_enabled;
bool native_ecam;
};
struct dw_pcie_ep_ops {
@ -493,6 +498,7 @@ struct dw_pcie {
resource_size_t dbi_phys_addr;
void __iomem *dbi_base2;
void __iomem *atu_base;
void __iomem *elbi_base;
resource_size_t atu_phys_addr;
size_t atu_size;
resource_size_t parent_bus_offset;

View File

@ -8,9 +8,11 @@
#include "pcie-designware.h"
#include "pcie-qcom-common.h"
void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci)
void qcom_pcie_common_set_equalization(struct dw_pcie *pci)
{
struct device *dev = pci->dev;
u32 reg;
u16 speed;
/*
* GEN3_RELATED_OFF register is repurposed to apply equalization
@ -19,32 +21,40 @@ void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci)
* determines the data rate for which these equalization settings are
* applied.
*/
reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK,
GEN3_RELATED_OFF_RATE_SHADOW_SEL_16_0GT);
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg);
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF);
reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 |
GEN3_EQ_FMDC_N_EVALS |
GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA |
GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA);
reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) |
FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) |
FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA, 0x5) |
FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA, 0x5);
dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg);
for (speed = PCIE_SPEED_8_0GT; speed <= pcie_link_speed[pci->max_link_speed]; speed++) {
if (speed > PCIE_SPEED_32_0GT) {
dev_warn(dev, "Skipped equalization settings for unsupported data rate\n");
break;
}
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
reg &= ~(GEN3_EQ_CONTROL_OFF_FB_MODE |
GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE |
GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL |
GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC);
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, reg);
reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK,
speed - PCIE_SPEED_8_0GT);
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg);
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF);
reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 |
GEN3_EQ_FMDC_N_EVALS |
GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA |
GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA);
reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) |
FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) |
FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, 0x5) |
FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, 0x5);
dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg);
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
reg &= ~(GEN3_EQ_CONTROL_OFF_FB_MODE |
GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE |
GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL |
GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC);
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, reg);
}
}
EXPORT_SYMBOL_GPL(qcom_pcie_common_set_16gt_equalization);
EXPORT_SYMBOL_GPL(qcom_pcie_common_set_equalization);
void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci)
{

View File

@ -8,7 +8,7 @@
struct dw_pcie;
void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci);
void qcom_pcie_common_set_equalization(struct dw_pcie *pci);
void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci);
#endif

View File

@ -179,7 +179,6 @@ struct qcom_pcie_ep_cfg {
* struct qcom_pcie_ep - Qualcomm PCIe Endpoint Controller
* @pci: Designware PCIe controller struct
* @parf: Qualcomm PCIe specific PARF register base
* @elbi: Designware PCIe specific ELBI register base
* @mmio: MMIO register base
* @perst_map: PERST regmap
* @mmio_res: MMIO region resource
@ -202,7 +201,6 @@ struct qcom_pcie_ep {
struct dw_pcie pci;
void __iomem *parf;
void __iomem *elbi;
void __iomem *mmio;
struct regmap *perst_map;
struct resource *mmio_res;
@ -267,10 +265,9 @@ static void qcom_pcie_ep_configure_tcsr(struct qcom_pcie_ep *pcie_ep)
static bool qcom_pcie_dw_link_up(struct dw_pcie *pci)
{
struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci);
u32 reg;
reg = readl_relaxed(pcie_ep->elbi + ELBI_SYS_STTS);
reg = readl_relaxed(pci->elbi_base + ELBI_SYS_STTS);
return reg & XMLH_LINK_UP;
}
@ -294,16 +291,15 @@ static void qcom_pcie_dw_stop_link(struct dw_pcie *pci)
static void qcom_pcie_dw_write_dbi2(struct dw_pcie *pci, void __iomem *base,
u32 reg, size_t size, u32 val)
{
struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci);
int ret;
writel(1, pcie_ep->elbi + ELBI_CS2_ENABLE);
writel(1, pci->elbi_base + ELBI_CS2_ENABLE);
ret = dw_pcie_write(pci->dbi_base2 + reg, size, val);
if (ret)
dev_err(pci->dev, "Failed to write DBI2 register (0x%x): %d\n", reg, ret);
writel(0, pcie_ep->elbi + ELBI_CS2_ENABLE);
writel(0, pci->elbi_base + ELBI_CS2_ENABLE);
}
static void qcom_pcie_ep_icc_update(struct qcom_pcie_ep *pcie_ep)
@ -511,10 +507,10 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
goto err_disable_resources;
}
if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) {
qcom_pcie_common_set_16gt_equalization(pci);
qcom_pcie_common_set_equalization(pci);
if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT)
qcom_pcie_common_set_16gt_lane_margining(pci);
}
/*
* The physical address of the MMIO region which is exposed as the BAR
@ -583,11 +579,6 @@ static int qcom_pcie_ep_get_io_resources(struct platform_device *pdev,
return PTR_ERR(pci->dbi_base);
pci->dbi_base2 = pci->dbi_base;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
pcie_ep->elbi = devm_pci_remap_cfg_resource(dev, res);
if (IS_ERR(pcie_ep->elbi))
return PTR_ERR(pcie_ep->elbi);
pcie_ep->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"mmio");
if (!pcie_ep->mmio_res) {

View File

@ -55,6 +55,7 @@
#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8
#define PARF_Q2A_FLUSH 0x1ac
#define PARF_LTSSM 0x1b0
#define PARF_SLV_DBI_ELBI 0x1b4
#define PARF_INT_ALL_STATUS 0x224
#define PARF_INT_ALL_CLEAR 0x228
#define PARF_INT_ALL_MASK 0x22c
@ -64,6 +65,16 @@
#define PARF_DBI_BASE_ADDR_V2_HI 0x354
#define PARF_SLV_ADDR_SPACE_SIZE_V2 0x358
#define PARF_SLV_ADDR_SPACE_SIZE_V2_HI 0x35c
#define PARF_BLOCK_SLV_AXI_WR_BASE 0x360
#define PARF_BLOCK_SLV_AXI_WR_BASE_HI 0x364
#define PARF_BLOCK_SLV_AXI_WR_LIMIT 0x368
#define PARF_BLOCK_SLV_AXI_WR_LIMIT_HI 0x36c
#define PARF_BLOCK_SLV_AXI_RD_BASE 0x370
#define PARF_BLOCK_SLV_AXI_RD_BASE_HI 0x374
#define PARF_BLOCK_SLV_AXI_RD_LIMIT 0x378
#define PARF_BLOCK_SLV_AXI_RD_LIMIT_HI 0x37c
#define PARF_ECAM_BASE 0x380
#define PARF_ECAM_BASE_HI 0x384
#define PARF_NO_SNOOP_OVERRIDE 0x3d4
#define PARF_ATU_BASE_ADDR 0x634
#define PARF_ATU_BASE_ADDR_HI 0x638
@ -87,6 +98,7 @@
/* PARF_SYS_CTRL register fields */
#define MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN BIT(29)
#define PCIE_ECAM_BLOCKER_EN BIT(26)
#define MST_WAKEUP_EN BIT(13)
#define SLV_WAKEUP_EN BIT(12)
#define MSTR_ACLK_CGC_DIS BIT(10)
@ -134,6 +146,9 @@
/* PARF_LTSSM register fields */
#define LTSSM_EN BIT(8)
/* PARF_SLV_DBI_ELBI */
#define SLV_DBI_ELBI_ADDR_BASE GENMASK(11, 0)
/* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */
#define PARF_INT_ALL_LINK_UP BIT(13)
#define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23)
@ -275,11 +290,8 @@ struct qcom_pcie_port {
struct qcom_pcie {
struct dw_pcie *pci;
void __iomem *parf; /* DT parf */
void __iomem *elbi; /* DT elbi */
void __iomem *mhi;
union qcom_pcie_resources res;
struct phy *phy;
struct gpio_desc *reset;
struct icc_path *icc_mem;
struct icc_path *icc_cpu;
const struct qcom_pcie_cfg *cfg;
@ -296,11 +308,8 @@ static void qcom_perst_assert(struct qcom_pcie *pcie, bool assert)
struct qcom_pcie_port *port;
int val = assert ? 1 : 0;
if (list_empty(&pcie->ports))
gpiod_set_value_cansleep(pcie->reset, val);
else
list_for_each_entry(port, &pcie->ports, list)
gpiod_set_value_cansleep(port->reset, val);
list_for_each_entry(port, &pcie->ports, list)
gpiod_set_value_cansleep(port->reset, val);
usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
}
@ -317,14 +326,55 @@ static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
qcom_perst_assert(pcie, false);
}
static void qcom_pci_config_ecam(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct qcom_pcie *pcie = to_qcom_pcie(pci);
u64 addr, addr_end;
u32 val;
writel_relaxed(lower_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE);
writel_relaxed(upper_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE_HI);
/*
* The only device on the root bus is a single Root Port. If we try to
* access any devices other than Device/Function 00.0 on Bus 0, the TLP
* will go outside of the controller to the PCI bus. But with CFG Shift
* Feature (ECAM) enabled in iATU, there is no guarantee that the
* response is going to be all F's. Hence, to make sure that the
* requester gets all F's response for accesses other than the Root
* Port, configure iATU to block the transactions starting from
* function 1 of the root bus to the end of the root bus (i.e., from
* dbi_base + 4KB to dbi_base + 1MB).
*/
addr = pci->dbi_phys_addr + SZ_4K;
writel_relaxed(lower_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE);
writel_relaxed(upper_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE_HI);
writel_relaxed(lower_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE);
writel_relaxed(upper_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE_HI);
addr_end = pci->dbi_phys_addr + SZ_1M - 1;
writel_relaxed(lower_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT);
writel_relaxed(upper_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT_HI);
writel_relaxed(lower_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT);
writel_relaxed(upper_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT_HI);
val = readl_relaxed(pcie->parf + PARF_SYS_CTRL);
val |= PCIE_ECAM_BLOCKER_EN;
writel_relaxed(val, pcie->parf + PARF_SYS_CTRL);
}
static int qcom_pcie_start_link(struct dw_pcie *pci)
{
struct qcom_pcie *pcie = to_qcom_pcie(pci);
if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) {
qcom_pcie_common_set_16gt_equalization(pci);
qcom_pcie_common_set_equalization(pci);
if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT)
qcom_pcie_common_set_16gt_lane_margining(pci);
}
/* Enable Link Training state machine */
if (pcie->cfg->ops->ltssm_enable)
@ -413,12 +463,17 @@ static void qcom_pcie_configure_dbi_atu_base(struct qcom_pcie *pcie)
static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie)
{
struct dw_pcie *pci = pcie->pci;
u32 val;
if (!pci->elbi_base) {
dev_err(pci->dev, "ELBI is not present\n");
return;
}
/* enable link training */
val = readl(pcie->elbi + ELBI_SYS_CTRL);
val = readl(pci->elbi_base + ELBI_SYS_CTRL);
val |= ELBI_SYS_CTRL_LT_ENABLE;
writel(val, pcie->elbi + ELBI_SYS_CTRL);
writel(val, pci->elbi_base + ELBI_SYS_CTRL);
}
static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie)
@ -1233,63 +1288,39 @@ static bool qcom_pcie_link_up(struct dw_pcie *pci)
return val & PCI_EXP_LNKSTA_DLLLA;
}
static void qcom_pcie_phy_exit(struct qcom_pcie *pcie)
{
struct qcom_pcie_port *port;
if (list_empty(&pcie->ports))
phy_exit(pcie->phy);
else
list_for_each_entry(port, &pcie->ports, list)
phy_exit(port->phy);
}
static void qcom_pcie_phy_power_off(struct qcom_pcie *pcie)
{
struct qcom_pcie_port *port;
if (list_empty(&pcie->ports)) {
phy_power_off(pcie->phy);
} else {
list_for_each_entry(port, &pcie->ports, list)
phy_power_off(port->phy);
}
list_for_each_entry(port, &pcie->ports, list)
phy_power_off(port->phy);
}
static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie)
{
struct qcom_pcie_port *port;
int ret = 0;
int ret;
if (list_empty(&pcie->ports)) {
ret = phy_set_mode_ext(pcie->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
list_for_each_entry(port, &pcie->ports, list) {
ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
if (ret)
return ret;
ret = phy_power_on(pcie->phy);
if (ret)
ret = phy_power_on(port->phy);
if (ret) {
qcom_pcie_phy_power_off(pcie);
return ret;
} else {
list_for_each_entry(port, &pcie->ports, list) {
ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
if (ret)
return ret;
ret = phy_power_on(port->phy);
if (ret) {
qcom_pcie_phy_power_off(pcie);
return ret;
}
}
}
return ret;
return 0;
}
static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct qcom_pcie *pcie = to_qcom_pcie(pci);
u16 offset;
int ret;
qcom_ep_reset_assert(pcie);
@ -1298,6 +1329,17 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
return ret;
if (pp->ecam_enabled) {
/*
* Override ELBI when ECAM is enabled, as when ECAM is enabled,
* ELBI moves under the 'config' space.
*/
offset = FIELD_GET(SLV_DBI_ELBI_ADDR_BASE, readl(pcie->parf + PARF_SLV_DBI_ELBI));
pci->elbi_base = pci->dbi_base + offset;
qcom_pci_config_ecam(pp);
}
ret = qcom_pcie_phy_power_on(pcie);
if (ret)
goto err_deinit;
@ -1708,6 +1750,8 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
int ret = -ENOENT;
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
if (!of_node_is_type(of_port, "pci"))
continue;
ret = qcom_pcie_parse_port(pcie, of_port);
if (ret)
goto err_port_del;
@ -1716,8 +1760,10 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
return ret;
err_port_del:
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
phy_exit(port->phy);
list_del(&port->list);
}
return ret;
}
@ -1725,20 +1771,32 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
{
struct device *dev = pcie->pci->dev;
struct qcom_pcie_port *port;
struct gpio_desc *reset;
struct phy *phy;
int ret;
pcie->phy = devm_phy_optional_get(dev, "pciephy");
if (IS_ERR(pcie->phy))
return PTR_ERR(pcie->phy);
phy = devm_phy_optional_get(dev, "pciephy");
if (IS_ERR(phy))
return PTR_ERR(phy);
pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
if (IS_ERR(pcie->reset))
return PTR_ERR(pcie->reset);
reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH);
if (IS_ERR(reset))
return PTR_ERR(reset);
ret = phy_init(pcie->phy);
ret = phy_init(phy);
if (ret)
return ret;
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
port->reset = reset;
port->phy = phy;
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);
return 0;
}
@ -1829,12 +1887,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
goto err_pm_runtime_put;
}
pcie->elbi = devm_platform_ioremap_resource_byname(pdev, "elbi");
if (IS_ERR(pcie->elbi)) {
ret = PTR_ERR(pcie->elbi);
goto err_pm_runtime_put;
}
/* MHI region is optional */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mhi");
if (res) {
@ -1952,9 +2004,10 @@ static int qcom_pcie_probe(struct platform_device *pdev)
err_host_deinit:
dw_pcie_host_deinit(pp);
err_phy_exit:
qcom_pcie_phy_exit(pcie);
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
phy_exit(port->phy);
list_del(&port->list);
}
err_pm_runtime_put:
pm_runtime_put(dev);
pm_runtime_disable(dev);