Commit 2b12e31c authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/controller/dwc'

- Update PORT_LOGIC_LTSSM_STATE_MASK to be a 6-bit mask as per spec, not a
  5-bit mask (Shawn Lin)

- Clear L1 PM Substate Capability 'Supported' bits unless glue driver says
  it's supported, which prevents users from enabling non-working L1SS.
  Currently only qcom and tegra194 support L1SS (Bjorn Helgaas)

- Remove now-superfluous L1SS disable code from tegra194 (Bjorn Helgaas)

- Configure L1SS support in dw-rockchip when DT says 'supports-clkreq'
  (Shawn Lin)

* pci/controller/dwc:
  PCI: dw-rockchip: Configure L1SS support
  PCI: tegra194: Remove unnecessary L1SS disable code
  PCI: dwc: Advertise L1 PM Substates only if driver requests it
  PCI: dwc: Fix wrong PORT_LOGIC_LTSSM_STATE_MASK definition
parents f4620f62 b5e719f2
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1060,6 +1060,8 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
		PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
	dw_pcie_writel_dbi(pci, PCI_COMMAND, val);

	dw_pcie_hide_unsupported_l1ss(pci);

	dw_pcie_config_presets(pp);
	/*
	 * If the platform provides its own child bus config accesses, it means
+24 −0
Original line number Diff line number Diff line
@@ -1081,6 +1081,30 @@ void dw_pcie_edma_remove(struct dw_pcie *pci)
	dw_edma_remove(&pci->edma);
}

void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci)
{
	u16 l1ss;
	u32 l1ss_cap;

	if (pci->l1ss_support)
		return;

	l1ss = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
	if (!l1ss)
		return;

	/*
	 * Unless the driver claims "l1ss_support", don't advertise L1 PM
	 * Substates because they require CLKREQ# and possibly other
	 * device-specific configuration.
	 */
	l1ss_cap = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
	l1ss_cap &= ~(PCI_L1SS_CAP_PCIPM_L1_1 | PCI_L1SS_CAP_ASPM_L1_1 |
		      PCI_L1SS_CAP_PCIPM_L1_2 | PCI_L1SS_CAP_ASPM_L1_2 |
		      PCI_L1SS_CAP_L1_PM_SS);
	dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, l1ss_cap);
}

void dw_pcie_setup(struct dw_pcie *pci)
{
	u32 val;
+3 −1
Original line number Diff line number Diff line
@@ -97,7 +97,7 @@
#define PORT_LANE_SKEW_INSERT_MASK	GENMASK(23, 0)

#define PCIE_PORT_DEBUG0		0x728
#define PORT_LOGIC_LTSSM_STATE_MASK	0x1f
#define PORT_LOGIC_LTSSM_STATE_MASK	0x3f
#define PORT_LOGIC_LTSSM_STATE_L0	0x11
#define PCIE_PORT_DEBUG1		0x72C
#define PCIE_PORT_DEBUG1_LINK_UP		BIT(4)
@@ -516,6 +516,7 @@ struct dw_pcie {
	int			max_link_speed;
	u8			n_fts[2];
	struct dw_edma_chip	edma;
	bool			l1ss_support;	/* L1 PM Substates support */
	struct clk_bulk_data	app_clks[DW_PCIE_NUM_APP_CLKS];
	struct clk_bulk_data	core_clks[DW_PCIE_NUM_CORE_CLKS];
	struct reset_control_bulk_data	app_rsts[DW_PCIE_NUM_APP_RSTS];
@@ -573,6 +574,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
				int type, u64 parent_bus_addr,
				u8 bar, size_t size);
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci);
void dw_pcie_setup(struct dw_pcie *pci);
void dw_pcie_iatu_detect(struct dw_pcie *pci);
int dw_pcie_edma_detect(struct dw_pcie *pci);
+40 −0
Original line number Diff line number Diff line
@@ -62,6 +62,12 @@
/* Interrupt Mask Register Related to Miscellaneous Operation */
#define PCIE_CLIENT_INTR_MASK_MISC	0x24

/* Power Management Control Register */
#define PCIE_CLIENT_POWER_CON		0x2c
#define  PCIE_CLKREQ_READY		FIELD_PREP_WM16(BIT(0), 1)
#define  PCIE_CLKREQ_NOT_READY		FIELD_PREP_WM16(BIT(0), 0)
#define  PCIE_CLKREQ_PULL_DOWN		FIELD_PREP_WM16(GENMASK(13, 12), 1)

/* Hot Reset Control Register */
#define PCIE_CLIENT_HOT_RESET_CTRL	0x180
#define  PCIE_LTSSM_APP_DLY2_EN		BIT(1)
@@ -85,6 +91,7 @@ struct rockchip_pcie {
	struct regulator *vpcie3v3;
	struct irq_domain *irq_domain;
	const struct rockchip_pcie_of_data *data;
	bool supports_clkreq;
};

struct rockchip_pcie_of_data {
@@ -200,6 +207,35 @@ static bool rockchip_pcie_link_up(struct dw_pcie *pci)
	return FIELD_GET(PCIE_LINKUP_MASK, val) == PCIE_LINKUP;
}

/*
 * See e.g. section '11.6.6.4 L1 Substate' in the RK3588 TRM V1.0 for the steps
 * needed to support L1 substates. Currently, just enable L1 substates for RC
 * mode if CLKREQ# is properly connected and supports-clkreq is present in DT.
 * For EP mode, there are more things should be done to actually save power in
 * L1 substates, so disable L1 substates until there is proper support.
 */
static void rockchip_pcie_configure_l1ss(struct dw_pcie *pci)
{
	struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);

	/* Enable L1 substates if CLKREQ# is properly connected */
	if (rockchip->supports_clkreq) {
		rockchip_pcie_writel_apb(rockchip, PCIE_CLKREQ_READY,
					 PCIE_CLIENT_POWER_CON);
		pci->l1ss_support = true;
		return;
	}

	/*
	 * Otherwise, assert CLKREQ# unconditionally.  Since
	 * pci->l1ss_support is not set, the DWC core will prevent L1
	 * Substates support from being advertised.
	 */
	rockchip_pcie_writel_apb(rockchip,
				 PCIE_CLKREQ_PULL_DOWN | PCIE_CLKREQ_NOT_READY,
				 PCIE_CLIENT_POWER_CON);
}

static void rockchip_pcie_enable_l0s(struct dw_pcie *pci)
{
	u32 cap, lnkcap;
@@ -264,6 +300,7 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
	irq_set_chained_handler_and_data(irq, rockchip_pcie_intx_handler,
					 rockchip);

	rockchip_pcie_configure_l1ss(pci);
	rockchip_pcie_enable_l0s(pci);

	return 0;
@@ -412,6 +449,9 @@ static int rockchip_pcie_resource_get(struct platform_device *pdev,
		return dev_err_probe(&pdev->dev, PTR_ERR(rockchip->rst),
				     "failed to get reset lines\n");

	rockchip->supports_clkreq = of_property_read_bool(pdev->dev.of_node,
							  "supports-clkreq");

	return 0;
}

+2 −0
Original line number Diff line number Diff line
@@ -1067,6 +1067,8 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
	val &= ~REQ_NOT_ENTR_L1;
	writel(val, pcie->parf + PARF_PM_CTRL);

	pci->l1ss_support = true;

	val = readl(pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2);
	val |= EN;
	writel(val, pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT_V2);
Loading