diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2025-10-06 10:41:03 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2025-10-06 10:41:03 -0700 |
commit | 2f2c7254931f41b5736e3ba12aaa9ac1bbeeeb92 (patch) | |
tree | e9d9aa57eceb8f57ef3a88ec99a05d34c947ea83 /drivers/pci/controller/dwc/pcie-qcom.c | |
parent | e4c0fdd5af4c590ca07880b97e286c6532437658 (diff) | |
parent | 51204faa4273a64b7066b5c1b5383e9b20d58caa (diff) |
Merge tag 'pci-v6.18-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci
Pull pci updates from Bjorn Helgaas:
"Enumeration:
- Add PCI_FIND_NEXT_CAP() and PCI_FIND_NEXT_EXT_CAP() macros that
take config space accessor functions.
Implement pci_find_capability(), pci_find_ext_capability(), and
dwc, dwc endpoint, and cadence capability search interfaces with
them (Hans Zhang)
- Leave parent unit address 0 in 'interrupt-map' so that when we
build devicetree nodes to describe PCI functions that contain
multiple peripherals, we can build this property even when
interrupt controllers lack 'reg' properties (Lorenzo Pieralisi)
- Add a Xeon 6 quirk to disable Extended Tags and limit Max Read
Request Size to 128B to avoid a performance issue (Ilpo Järvinen)
- Add sysfs 'serial_number' file to expose the Device Serial Number
(Matthew Wood)
- Fix pci_acpi_preserve_config() memory leak (Nirmoy Das)
Resource management:
- Align m68k pcibios_enable_device() with other arches (Ilpo
Järvinen)
- Remove sparc pcibios_enable_device() implementations that don't do
anything beyond what pci_enable_resources() does (Ilpo Järvinen)
- Remove mips pcibios_enable_resources() and use
pci_enable_resources() instead (Ilpo Järvinen)
- Clean up bridge window sizing and assignment (Ilpo Järvinen),
including:
- Leave non-claimed bridge windows disabled
- Enable bridges even if a window wasn't assigned because not all
windows are required by downstream devices
- Preserve bridge window type when releasing the resource, since
the type is needed for reassignment
- Consolidate selection of bridge windows into two new
interfaces, pbus_select_window() and
pbus_select_window_for_type(), so this is done consistently
- Compute bridge window start and end earlier to avoid logging
stale information
MSI:
- Add quirk to disable MSI on RDC PCI to PCIe bridges (Marcos Del Sol
Vives)
Error handling:
- Align AER with EEH by allowing drivers to request a Bus Reset on
Non-Fatal Errors (in addition to the reset on Fatal Errors that we
already do) (Lukas Wunner)
- If error recovery fails, emit FAILED_RECOVERY uevents for the
devices, not for the bridge leading to them.
This makes them correspond to BEGIN_RECOVERY uevents (Lukas Wunner)
- Align AER with EEH by calling err_handler.error_detected()
callbacks to notify drivers if error recovery fails (Lukas Wunner)
- Align AER with EEH by restoring device error_state to
pci_channel_io_normal before the err_handler.slot_reset() callback.
This is earlier than before the err_handler.resume() callback
(Lukas Wunner)
- Emit a BEGIN_RECOVERY uevent when driver's
err_handler.error_detected() requests a reset, as well as when it
says recovery is complete or can be done without a reset (Niklas
Schnelle)
- Align s390 with AER and EEH by emitting uevents during error
recovery (Niklas Schnelle)
- Align EEH with AER and s390 by emitting BEGIN_RECOVERY,
SUCCESSFUL_RECOVERY, or FAILED_RECOVERY uevents depending on the
result of err_handler.error_detected() (Niklas Schnelle)
- Fix a NULL pointer dereference in aer_ratelimit() when ACPI GHES
error information identifies a device without an AER Capability
(Breno Leitao)
- Update error decoding and TLP Log printing for new errors in
current PCIe base spec (Lukas Wunner)
- Update error recovery documentation to match the current code
and use consistent nomenclature (Lukas Wunner)
ASPM:
- Enable all ClockPM and ASPM states for devicetree platforms, since
there's typically no firmware that enables ASPM
This is a risky change that may uncover hardware or configuration
defects at boot-time rather than when users enable ASPM via sysfs
later. Booting with "pcie_aspm=off" prevents this enabling
(Manivannan Sadhasivam)
- Remove the qcom code that enabled ASPM (Manivannan Sadhasivam)
Power management:
- If a device has already been disconnected, e.g., by a hotplug
removal, don't bother trying to resume it to D0 when detaching the
driver.
This avoids annoying "Unable to change power state from D3cold to
D0" messages (Mario Limonciello)
- Ensure devices are powered up before config reads for
'max_link_width', 'current_link_speed', 'current_link_width',
'secondary_bus_number', and 'subordinate_bus_number' sysfs files.
This prevents using invalid data (~0) in drivers or lspci and,
depending on how the PCIe controller reports errors, may avoid
error interrupts or crashes (Brian Norris)
Virtualization:
- Add rescan/remove locking when enabling/disabling SR-IOV, which
avoids list corruption on s390, where disabling SR-IOV also
generates hotplug events (Niklas Schnelle)
Peer-to-peer DMA:
- Free struct p2p_pgmap, not a member within it, in the
pci_p2pdma_add_resource() error path (Sungho Kim)
Endpoint framework:
- Document sysfs interface for BAR assignment of vNTB endpoint
functions (Jerome Brunet)
- Fix array underflow in endpoint BAR test case (Dan Carpenter)
- Skip endpoint IRQ test if the IRQ is out of range to avoid false
errors (Christian Bruel)
- Fix endpoint test case for controllers with fixed-size BARs smaller
than requested by the test (Marek Vasut)
- Restore inbound translation when disabling doorbell so the endpoint
doorbell test case can be run more than once (Niklas Cassel)
- Avoid a NULL pointer dereference when releasing DMA channels in
endpoint DMA test case (Shin'ichiro Kawasaki)
- Convert tegra194 interrupt number to MSI vector to fix endpoint
Kselftest MSI_TEST test case (Niklas Cassel)
- Reset tegra194 BARs when running in endpoint mode so the BAR tests
don't overwrite the ATU settings in BAR4 (Niklas Cassel)
- Handle errors in tegra194 BPMP transactions so we don't mistakenly
skip future PERST# assertion (Vidya Sagar)
AMD MDB PCIe controller driver:
- Update DT binding example to separate PERST# to a Root Port stanza
to make multiple Root Ports possible in the future (Sai Krishna
Musham)
- Add driver support for PERST# being described in a Root Port
stanza, falling back to the host bridge if not found there (Sai
Krishna Musham)
Freescale i.MX6 PCIe controller driver:
- Enable the 3.3V Vaux supply if available so devices can request
wakeup with either Beacon or WAKE# (Richard Zhu)
MediaTek PCIe Gen3 controller driver:
- Add optional sys clock ready time setting to avoid sys_clk_rdy
signal glitching in MT6991 and MT8196 (AngeloGioacchino Del Regno)
- Add DT binding and driver support for MT6991 and MT8196
(AngeloGioacchino Del Regno)
NVIDIA Tegra PCIe controller driver:
- When asserting PERST#, disable the controller instead of mistakenly
disabling the PLL twice (Nagarjuna Kristam)
- Convert struct tegra_msi mask_lock to raw spinlock to avoid a lock
nesting error (Marek Vasut)
Qualcomm PCIe controller driver:
- Select PCI Power Control Slot driver so slot voltage rails can be
turned on/off if described in Root Port devicetree node (Qiang Yu)
- Parse only PCI bridge child nodes in devicetree, 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)
- 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)
- Add SM8750 compatible to qcom,pcie-sm8550.yaml (Krishna Chaitanya
Chundru)
- Update qcom,pcie-x1e80100.yaml to allow fifth PCIe host on Qualcomm
Glymur, which is compatible with X1E80100 but doesn't have the
cnoc_sf_axi clock (Qiang Yu)
Renesas R-Car PCIe controller driver:
- Fix a typo that prevented correct PHY initialization (Marek Vasut)
- Add a missing 1ms delay after PWR reset assertion as required by
the V4H manual (Marek Vasut)
- Assure reset has completed before DBI access to avoid SError (Marek
Vasut)
- Fix inverted PHY initialization check, which sometimes led to
timeouts and failure to start the controller (Marek Vasut)
- Pass the correct IRQ domain to generic_handle_domain_irq() to fix a
regression when converting to msi_create_parent_irq_domain()
(Claudiu Beznea)
- Drop the spinlock protecting the PMSR register - it's no longer
required since pci_lock already serializes accesses (Marek Vasut)
- Convert struct rcar_msi mask_lock to raw spinlock to avoid a lock
nesting error (Marek Vasut)
SOPHGO PCIe controller driver:
- Check for existence of struct cdns_pcie.ops before using it to
allow Cadence drivers that don't need to supply ops (Chen Wang)
- Add DT binding and driver for the SOPHGO SG2042 PCIe controller
(Chen Wang)
STMicroelectronics STM32MP25 PCIe controller driver:
- Update pinctrl documentation of initial states and use in runtime
suspend/resume (Christian Bruel)
- Add pinctrl_pm_select_init_state() for use by stm32 driver, which
needs it during resume (Christian Bruel)
- Add devicetree bindings and drivers for the STMicroelectronics
STM32MP25 in host and endpoint modes (Christian Bruel)
Synopsys DesignWare PCIe controller driver:
- Add support for x16 in devicetree 'num-lanes' property (Konrad
Dybcio)
- Verify that if DT specifies a single IRQ for all eDMA channels, it
is named 'dma' (Niklas Cassel)
TI J721E PCIe driver:
- Add MODULE_DEVICE_TABLE() so driver can be autoloaded (Siddharth
Vadapalli)
- Power controller off before configuring the glue layer so the
controller latches the correct values on power-on (Siddharth
Vadapalli)
TI Keystone PCIe controller driver:
- Use devm_request_irq() so 'ks-pcie-error-irq' is freed when driver
exits with error (Siddharth Vadapalli)
- Add Peripheral Virtualization Unit (PVU), which restricts DMA from
PCIe devices to specific regions of host memory, to the ti,am65
binding (Jan Kiszka)
Xilinx NWL PCIe controller driver:
- Clear bootloader E_ECAM_CONTROL before merging in the new driver
value to avoid writing invalid values (Jani Nurminen)"
* tag 'pci-v6.18-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci: (141 commits)
PCI/AER: Avoid NULL pointer dereference in aer_ratelimit()
MAINTAINERS: Add entry for ST STM32MP25 PCIe drivers
PCI: stm32-ep: Add PCIe Endpoint support for STM32MP25
dt-bindings: PCI: Add STM32MP25 PCIe Endpoint bindings
PCI: stm32: Add PCIe host support for STM32MP25
PCI: xilinx-nwl: Fix ECAM programming
PCI: j721e: Fix incorrect error message in probe()
PCI: keystone: Use devm_request_irq() to free "ks-pcie-error-irq" on exit
dt-bindings: PCI: qcom,pcie-x1e80100: Set clocks minItems for the fifth Glymur PCIe Controller
PCI: dwc: Support 16-lane operation
PCI: Add lockdep assertion in pci_stop_and_remove_bus_device()
PCI/IOV: Add PCI rescan-remove locking when enabling/disabling SR-IOV
PCI: rcar-host: Convert struct rcar_msi mask_lock into raw spinlock
PCI: tegra194: Rename 'root_bus' to 'root_port_bus' in tegra_pcie_downstream_dev_to_D0()
PCI: tegra: Convert struct tegra_msi mask_lock into raw spinlock
PCI: rcar-gen4: Fix inverted break condition in PHY initialization
PCI: rcar-gen4: Assure reset occurs before DBI access
PCI: rcar-gen4: Add missing 1ms delay after PWR reset assertion
PCI: Set up bridge resources earlier
PCI: rcar-host: Drop PMSR spinlock
...
Diffstat (limited to 'drivers/pci/controller/dwc/pcie-qcom.c')
-rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom.c | 211 |
1 files changed, 116 insertions, 95 deletions
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 294babe1816e..805edbbfe7eb 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -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) @@ -247,7 +262,6 @@ struct qcom_pcie_ops { int (*get_resources)(struct qcom_pcie *pcie); int (*init)(struct qcom_pcie *pcie); int (*post_init)(struct qcom_pcie *pcie); - void (*host_post_init)(struct qcom_pcie *pcie); void (*deinit)(struct qcom_pcie *pcie); void (*ltssm_enable)(struct qcom_pcie *pcie); int (*config_sid)(struct qcom_pcie *pcie); @@ -276,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; @@ -297,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); } @@ -318,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) @@ -414,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) @@ -1040,25 +1094,6 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) return 0; } -static int qcom_pcie_enable_aspm(struct pci_dev *pdev, void *userdata) -{ - /* - * Downstream devices need to be in D0 state before enabling PCI PM - * substates. - */ - pci_set_power_state_locked(pdev, PCI_D0); - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL); - - return 0; -} - -static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie) -{ - struct dw_pcie_rp *pp = &pcie->pci->pp; - - pci_walk_bus(pp->bridge->bus, qcom_pcie_enable_aspm, NULL); -} - static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; @@ -1253,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); @@ -1318,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; @@ -1358,19 +1380,9 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp) pcie->cfg->ops->deinit(pcie); } -static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct qcom_pcie *pcie = to_qcom_pcie(pci); - - if (pcie->cfg->ops->host_post_init) - pcie->cfg->ops->host_post_init(pcie); -} - static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { .init = qcom_pcie_host_init, .deinit = qcom_pcie_host_deinit, - .post_init = qcom_pcie_host_post_init, }; /* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */ @@ -1432,7 +1444,6 @@ static const struct qcom_pcie_ops ops_1_9_0 = { .get_resources = qcom_pcie_get_resources_2_7_0, .init = qcom_pcie_init_2_7_0, .post_init = qcom_pcie_post_init_2_7_0, - .host_post_init = qcom_pcie_host_post_init_2_7_0, .deinit = qcom_pcie_deinit_2_7_0, .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, .config_sid = qcom_pcie_config_sid_1_9_0, @@ -1443,7 +1454,6 @@ static const struct qcom_pcie_ops ops_1_21_0 = { .get_resources = qcom_pcie_get_resources_2_7_0, .init = qcom_pcie_init_2_7_0, .post_init = qcom_pcie_post_init_2_7_0, - .host_post_init = qcom_pcie_host_post_init_2_7_0, .deinit = qcom_pcie_deinit_2_7_0, .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, }; @@ -1740,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; @@ -1748,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; } @@ -1757,20 +1771,32 @@ err_port_del: 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; } @@ -1861,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) { @@ -1984,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); |