Merge branch 'pci/controller/qcom'

- Add OF support for parsing DT 'eq-presets-<N>gts' property for lane
  equalization presets (Krishna Chaitanya Chundru)

- Read Maximum Link Width from the Link Capabilities register if DT lacks
  'num-lanes' property (Krishna Chaitanya Chundru)

- Add Physical Layer 64 GT/s Capability ID and register offsets for 8, 32,
  and 64 GT/s lane equalization registers (Krishna Chaitanya Chundru)

- Add generic dwc support for configuring lane equalization presets
  (Krishna Chaitanya Chundru)

- Add DT and driver support for PCIe on IPQ5018 SoC (Nitheesh Sekar)

* pci/controller/qcom:
  PCI: qcom: Add support for IPQ5018
  dt-bindings: PCI: qcom: Add IPQ5018 SoC
  PCI: dwc: Add support for configuring lane equalization presets
  PCI: Add lane equalization register offsets
  PCI: dwc: Update pci->num_lanes to maximum supported link width
  PCI: of: Add of_pci_get_equalization_presets() API
This commit is contained in:
Bjorn Helgaas 2025-06-04 10:50:42 -05:00
commit 05cf00aa05
8 changed files with 228 additions and 2 deletions

View File

@ -21,6 +21,7 @@ properties:
- qcom,pcie-apq8064
- qcom,pcie-apq8084
- qcom,pcie-ipq4019
- qcom,pcie-ipq5018
- qcom,pcie-ipq6018
- qcom,pcie-ipq8064
- qcom,pcie-ipq8064-v2
@ -168,6 +169,7 @@ allOf:
compatible:
contains:
enum:
- qcom,pcie-ipq5018
- qcom,pcie-ipq6018
- qcom,pcie-ipq8074-gen3
- qcom,pcie-ipq9574
@ -322,6 +324,53 @@ allOf:
- const: ahb # AHB reset
- const: phy_ahb # PHY AHB reset
- if:
properties:
compatible:
contains:
enum:
- qcom,pcie-ipq5018
then:
properties:
clocks:
minItems: 6
maxItems: 6
clock-names:
items:
- const: iface # PCIe to SysNOC BIU clock
- const: axi_m # AXI Master clock
- const: axi_s # AXI Slave clock
- const: ahb # AHB clock
- const: aux # Auxiliary clock
- const: axi_bridge # AXI bridge clock
resets:
minItems: 8
maxItems: 8
reset-names:
items:
- const: pipe # PIPE reset
- const: sleep # Sleep reset
- const: sticky # Core sticky reset
- const: axi_m # AXI master reset
- const: axi_s # AXI slave reset
- const: ahb # AHB reset
- const: axi_m_sticky # AXI master sticky reset
- const: axi_s_sticky # AXI slave sticky reset
interrupts:
minItems: 9
maxItems: 9
interrupt-names:
items:
- const: msi0
- const: msi1
- const: msi2
- const: msi3
- const: msi4
- const: msi5
- const: msi6
- const: msi7
- const: global
- if:
properties:
compatible:
@ -562,6 +611,7 @@ allOf:
enum:
- qcom,pcie-apq8064
- qcom,pcie-ipq4019
- qcom,pcie-ipq5018
- qcom,pcie-ipq8064
- qcom,pcie-ipq8064v2
- qcom,pcie-ipq8074

View File

@ -523,6 +523,13 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
dw_pcie_iatu_detect(pci);
if (pci->num_lanes < 1)
pci->num_lanes = dw_pcie_link_get_max_link_width(pci);
ret = of_pci_get_equalization_presets(dev, &pp->presets, pci->num_lanes);
if (ret)
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
@ -828,6 +835,77 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
return 0;
}
static void dw_pcie_program_presets(struct dw_pcie_rp *pp, enum pci_bus_speed speed)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
u8 lane_eq_offset, lane_reg_size, cap_id;
u8 *presets;
u32 cap;
int i;
if (speed == PCIE_SPEED_8_0GT) {
presets = (u8 *)pp->presets.eq_presets_8gts;
lane_eq_offset = PCI_SECPCI_LE_CTRL;
cap_id = PCI_EXT_CAP_ID_SECPCI;
/* For data rate of 8 GT/S each lane equalization control is 16bits wide*/
lane_reg_size = 0x2;
} else if (speed == PCIE_SPEED_16_0GT) {
presets = pp->presets.eq_presets_Ngts[EQ_PRESET_TYPE_16GTS - 1];
lane_eq_offset = PCI_PL_16GT_LE_CTRL;
cap_id = PCI_EXT_CAP_ID_PL_16GT;
lane_reg_size = 0x1;
} else if (speed == PCIE_SPEED_32_0GT) {
presets = pp->presets.eq_presets_Ngts[EQ_PRESET_TYPE_32GTS - 1];
lane_eq_offset = PCI_PL_32GT_LE_CTRL;
cap_id = PCI_EXT_CAP_ID_PL_32GT;
lane_reg_size = 0x1;
} else if (speed == PCIE_SPEED_64_0GT) {
presets = pp->presets.eq_presets_Ngts[EQ_PRESET_TYPE_64GTS - 1];
lane_eq_offset = PCI_PL_64GT_LE_CTRL;
cap_id = PCI_EXT_CAP_ID_PL_64GT;
lane_reg_size = 0x1;
} else {
return;
}
if (presets[0] == PCI_EQ_RESV)
return;
cap = dw_pcie_find_ext_capability(pci, cap_id);
if (!cap)
return;
/*
* Write preset values to the registers byte-by-byte for the given
* number of lanes and register size.
*/
for (i = 0; i < pci->num_lanes * lane_reg_size; i++)
dw_pcie_writeb_dbi(pci, cap + lane_eq_offset + i, presets[i]);
}
static void dw_pcie_config_presets(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
enum pci_bus_speed speed = pcie_link_speed[pci->max_link_speed];
/*
* Lane equalization settings need to be applied for all data rates the
* controller supports and for all supported lanes.
*/
if (speed >= PCIE_SPEED_8_0GT)
dw_pcie_program_presets(pp, PCIE_SPEED_8_0GT);
if (speed >= PCIE_SPEED_16_0GT)
dw_pcie_program_presets(pp, PCIE_SPEED_16_0GT);
if (speed >= PCIE_SPEED_32_0GT)
dw_pcie_program_presets(pp, PCIE_SPEED_32_0GT);
if (speed >= PCIE_SPEED_64_0GT)
dw_pcie_program_presets(pp, PCIE_SPEED_64_0GT);
}
int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@ -881,6 +959,7 @@ 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_config_presets(pp);
/*
* If the platform provides its own child bus config accesses, it means
* the platform uses its own address translation component rather than

View File

@ -781,6 +781,14 @@ static void dw_pcie_link_set_max_speed(struct dw_pcie *pci)
}
int dw_pcie_link_get_max_link_width(struct dw_pcie *pci)
{
u8 cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
u32 lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
return FIELD_GET(PCI_EXP_LNKCAP_MLW, lnkcap);
}
static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
{
u32 lnkcap, lwsc, plc;

View File

@ -25,6 +25,8 @@
#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
#include "../../pci.h"
/* DWC PCIe IP-core versions (native support since v4.70a) */
#define DW_PCIE_VER_365A 0x3336352a
#define DW_PCIE_VER_460A 0x3436302a
@ -412,6 +414,7 @@ struct dw_pcie_rp {
int msg_atu_index;
struct resource *msg_res;
bool use_linkup_irq;
struct pci_eq_presets presets;
};
struct dw_pcie_ep_ops {
@ -540,6 +543,7 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 reg, size_t size, u32 val);
bool dw_pcie_link_up(struct dw_pcie *pci);
void dw_pcie_upconfig_setup(struct dw_pcie *pci);
int dw_pcie_wait_for_link(struct dw_pcie *pci);
int dw_pcie_link_get_max_link_width(struct dw_pcie *pci);
int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
const struct dw_pcie_ob_atu_cfg *atu);
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,

View File

@ -1840,6 +1840,7 @@ static const struct of_device_id qcom_pcie_match[] = {
{ .compatible = "qcom,pcie-apq8064", .data = &cfg_2_1_0 },
{ .compatible = "qcom,pcie-apq8084", .data = &cfg_1_0_0 },
{ .compatible = "qcom,pcie-ipq4019", .data = &cfg_2_4_0 },
{ .compatible = "qcom,pcie-ipq5018", .data = &cfg_2_9_0 },
{ .compatible = "qcom,pcie-ipq6018", .data = &cfg_2_9_0 },
{ .compatible = "qcom,pcie-ipq8064", .data = &cfg_2_1_0 },
{ .compatible = "qcom,pcie-ipq8064-v2", .data = &cfg_2_1_0 },

View File

@ -966,3 +966,47 @@ u32 of_pci_get_slot_power_limit(struct device_node *node,
return slot_power_limit_mw;
}
EXPORT_SYMBOL_GPL(of_pci_get_slot_power_limit);
/**
* of_pci_get_equalization_presets - Parses the "eq-presets-Ngts" property.
*
* @dev: Device containing the properties.
* @presets: Pointer to store the parsed data.
* @num_lanes: Maximum number of lanes supported.
*
* If the property is present, read and store the data in the @presets structure.
* Else, assign a default value of PCI_EQ_RESV.
*
* Return: 0 if the property is not available or successfully parsed else
* errno otherwise.
*/
int of_pci_get_equalization_presets(struct device *dev,
struct pci_eq_presets *presets,
int num_lanes)
{
char name[20];
int ret;
presets->eq_presets_8gts[0] = PCI_EQ_RESV;
ret = of_property_read_u16_array(dev->of_node, "eq-presets-8gts",
presets->eq_presets_8gts, num_lanes);
if (ret && ret != -EINVAL) {
dev_err(dev, "Error reading eq-presets-8gts: %d\n", ret);
return ret;
}
for (int i = 0; i < EQ_PRESET_TYPE_MAX - 1; i++) {
presets->eq_presets_Ngts[i][0] = PCI_EQ_RESV;
snprintf(name, sizeof(name), "eq-presets-%dgts", 8 << (i + 1));
ret = of_property_read_u8_array(dev->of_node, name,
presets->eq_presets_Ngts[i],
num_lanes);
if (ret && ret != -EINVAL) {
dev_err(dev, "Error reading %s: %d\n", name, ret);
return ret;
}
}
return 0;
}
EXPORT_SYMBOL_GPL(of_pci_get_equalization_presets);

View File

@ -9,6 +9,8 @@ struct pcie_tlp_log;
/* Number of possible devfns: 0.0 to 1f.7 inclusive */
#define MAX_NR_DEVFNS 256
#define MAX_NR_LANES 16
#define PCI_FIND_CAP_TTL 48
#define PCI_VSEC_ID_INTEL_TBT 0x1234 /* Thunderbolt */
@ -891,6 +893,21 @@ static inline u64 pci_rebar_size_to_bytes(int size)
struct device_node;
#define PCI_EQ_RESV 0xff
enum equalization_preset_type {
EQ_PRESET_TYPE_8GTS,
EQ_PRESET_TYPE_16GTS,
EQ_PRESET_TYPE_32GTS,
EQ_PRESET_TYPE_64GTS,
EQ_PRESET_TYPE_MAX
};
struct pci_eq_presets {
u16 eq_presets_8gts[MAX_NR_LANES];
u8 eq_presets_Ngts[EQ_PRESET_TYPE_MAX - 1][MAX_NR_LANES];
};
#ifdef CONFIG_OF
int of_get_pci_domain_nr(struct device_node *node);
int of_pci_get_max_link_speed(struct device_node *node);
@ -905,7 +922,9 @@ void pci_release_bus_of_node(struct pci_bus *bus);
int devm_of_pci_bridge_init(struct device *dev, struct pci_host_bridge *bridge);
bool of_pci_supply_present(struct device_node *np);
int of_pci_get_equalization_presets(struct device *dev,
struct pci_eq_presets *presets,
int num_lanes);
#else
static inline int
of_get_pci_domain_nr(struct device_node *node)
@ -950,6 +969,17 @@ static inline bool of_pci_supply_present(struct device_node *np)
{
return false;
}
static inline int of_pci_get_equalization_presets(struct device *dev,
struct pci_eq_presets *presets,
int num_lanes)
{
presets->eq_presets_8gts[0] = PCI_EQ_RESV;
for (int i = 0; i < EQ_PRESET_TYPE_MAX - 1; i++)
presets->eq_presets_Ngts[i][0] = PCI_EQ_RESV;
return 0;
}
#endif /* CONFIG_OF */
struct of_changeset;

View File

@ -750,7 +750,8 @@
#define PCI_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Management */
#define PCI_EXT_CAP_ID_PL_32GT 0x2A /* Physical Layer 32.0 GT/s */
#define PCI_EXT_CAP_ID_DOE 0x2E /* Data Object Exchange */
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DOE
#define PCI_EXT_CAP_ID_PL_64GT 0x31 /* Physical Layer 64.0 GT/s */
#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_64GT
#define PCI_EXT_CAP_DSN_SIZEOF 12
#define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
@ -1144,12 +1145,21 @@
#define PCI_DLF_CAP 0x04 /* Capabilities Register */
#define PCI_DLF_EXCHANGE_ENABLE 0x80000000 /* Data Link Feature Exchange Enable */
/* Secondary PCIe Capability 8.0 GT/s */
#define PCI_SECPCI_LE_CTRL 0x0c /* Lane Equalization Control Register */
/* Physical Layer 16.0 GT/s */
#define PCI_PL_16GT_LE_CTRL 0x20 /* Lane Equalization Control Register */
#define PCI_PL_16GT_LE_CTRL_DSP_TX_PRESET_MASK 0x0000000F
#define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK 0x000000F0
#define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT 4
/* Physical Layer 32.0 GT/s */
#define PCI_PL_32GT_LE_CTRL 0x20 /* Lane Equalization Control Register */
/* Physical Layer 64.0 GT/s */
#define PCI_PL_64GT_LE_CTRL 0x20 /* Lane Equalization Control Register */
/* Native PCIe Enclosure Management */
#define PCI_NPEM_CAP 0x04 /* NPEM capability register */
#define PCI_NPEM_CAP_CAPABLE 0x00000001 /* NPEM Capable */