pci-v7.4-changes

-----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCgAyFiEEgMe7l+5h9hnxdsnuWYigwDrT+vwFAmrZFXsUHGJoZWxnYWFz
 QGdvb2dsZS5jb20ACgkQWYigwDrT+vxlSxAAwXOwRpzBXQjGzvpA7usELOuBqUn6
 kToTlY5VHQZ01Iax3IwbENP8DP3Nh4Zmq3GVOwLREdZ6ouAw/jXGfdnAnFgl6EV2
 5m6yW0RXElZgxIbPJZIbGve7UwrpgBIvvsPl6KVZRY/6lzobEU8XLIHWFKi1EF9l
 nicYADjI1gocFaq8GyAqAoh5WpHSpP2rNBY2fQmZSm+ihGBUzvy6inL0f3j19A1G
 L6av4MwpAQQI137oN1WG2tR3Donc1r7bIfsGiYkMg6TjV/yeqNeafCJ3jbZnTUAS
 UHVfnCxImkKmQXYElVXsn93VwigRMbtH3KXaAVjctp6VoUxNCTJRtjvvwrpu1tlp
 92eazDj9BU2SgcHxH5D4TYmWgslp43l8EbnbBgt64lLthsdhv2evOkQbGbMtZwzL
 FnSYG7Y/wfPxM2u3ODFfjXoKBdmbJqKzwmjs1CQFJVrp0TMLUHUpTI2XDxCOYh+J
 HpNSnAevSjUUFaXf+IPvgOnZZanN6K2/ZUns48GIYQruebViTv7RCfPFZKjLI1Ny
 1w5VwUZ9KEEidlB3vAmeAbACWoRGumoeS9AElzuBLRcOWKTiofmupTwO7GoTwqGa
 69SpDgU9t99UK2Q9++NLO+rW1ZwCtdi0ImmJ0t9rZtGPMDR11LZwdIRmMxEjdj3r
 fg+9TQDdxiMF+r0=
 =1Pqr
 -----END PGP SIGNATURE-----

Merge tag 'pci-v7.4-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci

Pull PCI updates from Bjorn Helgaas:
 "Enumeration:

   - Set supported_speeds in set_pcie_port_type() so sparc64 and
     powerpc, which call it via of_create_pci_dev() instead of the usual
     pci_setup_device(), correctly set the supported PCIe link speeds
     (Stian Halseth)

   - When pcie_set_mps() changes MPS, update the value in the saved
     state so a subsequent restore doesn't restore an invalid value
     (Niklas Cassel)

   - Skip Target Speed quirk on clamped ports with no link (Maciej W.
     Rozycki)

  Driver binding:

   - Assume control of portdrv-related features only when portdrv is
     enabled, since we can't use them without portdrv (Kuppuswamy
     Sathyanarayanan)

   - Centralize feature ownership checking so code only needs to check
     'host->native_X' and doesn't need to also check 'pcie_ports_native'
     (Kuppuswamy Sathyanarayanan)

  Error handling:

   - Fix leaks when ACPI APEI/GHES error events overflow the kfifo or
     mention a device that no longer exists (Priyank Rathod)

   - Fix aer_process_err_devices() leak when a device is recorded as an
     error source purely by being mentioned in the Root Port Error
     Source and the device is inaccessible or logged only masked errors
     (Priyank Rathod)

  ASPM:

   - Disable ASPM when drivers request that, even if the OS doesn't own
     ASPM; otherwise drivers do it on their own, which is worse (Ilpo
     Järvinen)

  Power management:

   - Align hibernate poweroff flow with suspend flow for bridges so they
     can be powered down at hibernate as they are for suspend (Mario
     Limonciello)

   - Restore BARs after powering up during enumeration to fix a
     regression on devices that don't preserve state in D3hot (Keng-Yu
     Lin)

   - Avoid D3 on PLX PEX 8532/8533/8548 downstream ports, where putting
     them in D3hot takes down the whole switch (Imre Kaloz)

  Power control:

   - Add GPIO driver for Toshiba TC9563 embedded GPIO controller (Alex
     Elder)

   - Add DT documentation and pwrctrl driver support for TC9563 embedded
     GPIO controller (Lorenzo Bianconi)

   - Switch the tc9563 per-port reset to use the GPIO descriptor API
     instead of direct regmap accesses (Lorenzo Bianconi)

   - Only destroy devices created by pwrctrl core, so that when removing
     a PCIe controller driver that uses pwrctrl, we don't remove devices
     populated by another mechanism (Shawn Lin)

   - Power off child devices on power-on failure (Lorenzo Bianconi)

  PCIe native device hotplug:

   - Make the 'pciehp_poll_mode' parameter read-only to prevent changes
     via sysfs, which can cause errors when unbinding (Runyu Xiao)

  Virtualization:

   - Read SR-IOV VF Revision ID and Subsystem ID from VF instead of
     assuming they're the same as VF 0; they may differ (Logan Odell)

   - Add ACS quirk for Pericom PI7C9X2G304 switches (Hans de Goede)

  Peer-to-peer DMA:

   - Add Zhaoxin KX-6000, KH-40000, KX-6000G, KX-7000, and KH-50000 host
     bridges to allowlist (LeoLiu-oc)

   - Add Alibaba T-HEAD Yitian 710 host bridge to allowlist (Feng Tang)

   - Add Intel Haswell client host bridge to allowlist (Scott Lee)

   - Intel Xeon E3/E5/E7 0x6f00 host bridge to allowlist (kai zhang)

   - Fix handling of errors in pci_p2pdma_add_resource() to avoid (a)
     unmapping a BAR if registering another fails and (b) use after free
     of p2p_pgmap (Leon Romanovsky)

   - Wait for RCU readers before freeing state to avoid use-after-free
     of pdev->p2pdma (Leon Romanovsky)

   - Restrict pci_p2pmem_find_many() to providers backed by a pool (Leon
     Romanovsky)

   - Safely terminate ACS redirect lists to avoid out-of-bounds access
     to the buffer (Leon Romanovsky)

   - Silence 'Host bridge not in P2PDMA whitelist' warnings unless
     caller, e.g., pci_p2pmem_find_many(), requests 'verbose' (Leon
     Romanovsky)

  Sysfs:

   - Return -ENODEV or -EIO sysfs 'label' or 'acpi_index' attributes
     when Device Name _DSM fails, not -1, which is interpreted as -EPERM
     (Krzysztof Wilczyński)

   - Return valid sysfs 'acpi_index' attribute even if the Device Name
     _DSM returned an invalid 'label' (Krzysztof Wilczyński)

   - Expose sysfs 'acpi_index' and 'label' based on whether the Device
     Name _DSM returns valid data, not merely whether the _DSM is
     implemented (Krzysztof Wilczyński)

   - Avoid out-of-bounds reads of the label returned by the Device Name
     _DSM, which is in UTF-16 characters, not bytes (Krzysztof
     Wilczyński)

   - Reject unaligned resource I/O port accesses, which can oops on
     arm64 (Deepanshu Kartikey)

Endpoint framework:

   - Pass the correct physical and virtual endpoint function numbers
     when setting translation of inbound memory windows (Koichiro Den)

   - Make embedded doorbell IRQ non-shared, since currently only the
     first EPF can allocate doorbells (Koichiro Den)

   - MSI doorbells currently target the first EPF attached to an EPC;
     let other EPFs try the embedded doorbell instead of failing when
     they can't use the MSI doorbell (Koichiro Den)

   - Serialize scan of vNTB virtual PCI bus with other PCI topology
     changes (Koichiro Den)

   - Manage vNTB lifetimes to avoid leaking virtual devices and buses
     and allow removal (Koichiro Den)

   - Track vNTB link state from both sides so both see the same
     effective link state (Koichiro Den)

   - Configure vNTB MSI-X when supported to avoid IOMMU faults and
     failure to deliver doorbell interrupts on RC (Koichiro Den)

  APM X-Gene PCIe controller driver:

   - Use devm for clocks so they're automatically disabled, unprepared,
     and released if errors happen during probe (Yuho Choi)

  ASPEED PCIe controller driver:

   - Fix clk and PHY leaks in aspeed_pcie_port_init() error paths (Liu
     Zhenlong)

  Broadcom iProc PCIe controller driver:

   - Expose devm_pci_alloc_host_bridge_release() so drivers can share a
     devm release function even if they don't need the device tree
     parsing done by devm_pci_alloc_host_bridge() (Bjorn Helgaas)

   - Request only the memory window from BCMA Enumeration ROM and warn
     if it doesn't match the one in DT, which fixes an iproc-bcma probe
     failure (Semih Baskan)

  Broadcom STB PCIe controller driver:

   - Replace bool properties for each SoC configuration with a u32
     bitvector and a BFLAG() helper (Jim Quinlan)

   - Use a similar BQUIRK() helper to test various SoC quirks (Jim
     Quinlan)

  Cadence PCIe controller driver:

   - Remove device links to PHY, which are redundant because the PHY
     core itself adds them (Vladimir Oltean)

   - Return error (not success) from cdns_plat_pcie_probe() if
     pm_runtime_get_sync() or cdns_pcie_ep_setup() failed (Vladimir
     Oltean)

   - Correct the driver data pointer usage; previously .probe() stored
     'struct cdns_plat_pcie *', but .shutdown() used the pointer as
     'struct cdns_pcie *', resulting in accessing garbage (Linmao Li)

  ESWIN PCIe controller driver:

   - Publish the platform driver MODULE_DEVICE_TABLE so the driver can
     be autoloaded when built as a module (Pengpeng Hou)

  Freescale i.MX6 PCIe controller driver:

   - Fix resource and pwrctrl device leaks .probe() (Zhijian Han)

   - Improve i.MX95 Gen3 link stability by changing MPLLB_BANDWIDTH from
     default 120 to 140 (Richard Zhu)

   - Use dev_err_probe() in host init error path to silence
     -EPROBE_DEFER messages (Alexander Stein)

   - Avoid dereferencing NULL clock names when 'clocks' includes more
     phandles than 'clock-names' has names (Rudi Heitbaum)

   - Add suspend_poweroff flag so platforms like i.MX that power off RC
     controllers during suspend can enter L2 so endpoints can get
     PME_Turn_Off notifications before power loss (Richard Zhu)

  Freescale Layerscape PCIe controller driver:

   - Enable PROBE_PREFER_ASYNCHRONOUS to reduce probe time of the four
     controllers on LX2160A (Vincent Jardin)

  Intel VMD host bridge driver:

   - Flush DMA writes before handling MSI on Meteor Lake: MSI may arrive
     before write has completed, resulting in an nvme 30 second stall
     before a QID timeout and possibly other similar problems (Rickey
     Bartlett)

   - Flush DMA writes before handling MSI on Arrow Lake (Vladimir
     Nedoshivin)

  Marvell Aardvark PCIe controller driver:

   - Disable PHY on probe failures (Myeonghun Pak)

  Marvell MVEBU PCIe controller driver:

   - Refcount child OF nodes during .probe() to make a concurrent OF
     overlay removal safe (Krzysztof Kozlowski)

  MediaTek PCIe controller driver:

   - Find INTx DT property by 'interrupt-controller' name instead of
     assuming it's the first child node (Zhengping Zhang)

  MediaTek PCIe Gen3 controller driver:

   - Fix a 64-bit type truncation in mtk_pcie_set_trans_table() to avoid
     issues with windows above or larger than 4GB (Sreeraj S Kurup)

  NVIDIA Tegra264 PCIe controller driver:

   - Fix the Link Capabilities offset so we read the correct register
     (Bjorn Helgaas)

  Qualcomm PCIe controller driver:

   - Only collect reset GPIOs from PCI bridge DT nodes to avoid stealing
     resets used by downstream devices like USB controllers (Christopher
     Obbard)

   - Block accesses to downstream devices on link down to reduce a storm
     of Completion Timeout AER interrupts (Qiang Yu)

   - Skip system suspend/resume for firmware-managed PCIe, where
     firmware manages clocks, regulators, resets, etc (Mrinmay Sarkar)

   - Honor IOMMU provider's #iommu-cells in qcom_pcie_config_sid_1_9_0()
     (Manivannan Sadhasivam)

   - Deinitialize endpoint on remove (Guangshuo Li)

   - Treat Root Port error recovery as successful even if link doesn't
     come up after reset because the device was hot-unplugged (Konrad
     Dybcio)

   - Skip unnecessary iMSI-RX initialization if DT 'msi-map' is present,
     indicating an external MSI controller (Ayiluri Naga Rashmi)

  Renesas R-Car PCIe controller driver:

   - Limit Max_Read_Request_Size to 256 to avoid hardware erratum (Marek
     Vasut)

   - Support PERST# via reset subsystem as well as the existing GPIO
     support to prepare for R-Car Gen5 SoCs (Marek Vasut)

   - Add DT and driver support for the R-Car Gen 5 X5H (R8A78000) PCIe
     Gen 4 core (Marek Vasut)

   - Add Application/Local register reset DT description and driver
     support for both host and endpoint mode (Marek Vasut)

   - Add PM ops so controller works after suspend/resume (Marek Vasut)

  Renesas RZ/G3S PCIe controller driver:

   - Disable refclk on probe failure (Lad Prabhakar)

   - Propagate platform_get_irq_byname() errors so probe deferral works
     correctly (Lad Prabhakar)

   - Tear down INTx handlers if subsequent initialization fails (Lad
     Prabhakar)

   - Add RZ/G3L (R9A08G046) DT and driver support (Biju Das)

  Rockchip PCIe controller driver:

   - Limit host and endpoint controllers to 2.5 GT/s since 5.0 GT/s is
     unsafe per Rockchip (Geraldo Nascimento)

  Synopsys DesignWare PCIe controller driver:

   - Set DMA_BYPASS bit in outbound IATU CTRL2 register to avoid data
     corruption if the iATU translates DMA engine transactions (Sumit
     Kumar)

   - Use FIELD_PREP() to remove need for *_SHIFT definitions (Hans
     Zhang)

  TI DRA7xx PCIe controller driver:

   - Remove device links to PHY, which are redundant because the PHY
     core itself adds them (Vladimir Oltean)

   - Fix optional clock leak when .probe() fails by using
     devm_clk_get_optional_enabled() (Felix Gu)

  TI Keystone PCIe controller driver:

   - Remove device links to PHY, which are redundant because the PHY
     core itself adds them (Vladimir Oltean)

  UltraRISC PCIe controller driver:

   - Allow DWC drivers to skip outbound iATU setup if they don't need
     translation between CPU and PCI addresses (Xincheng Zhang)

   - Skip MEM outbound iATU setup on DP1000 (Xincheng Zhang)

  Xilinx Versal CPM PCIe controller driver:

   - Add DT and driver support for optional PERST# for use in warm reset
     scenarios (Sai Krishna Musham)

  Xilinx XDMA PCIe controller driver:

   - Fix device node reference leak in xilinx_pl_dma_pcie_init_irq_domain()
     (Wentao Liang)"

* tag 'pci-v7.4-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci: (139 commits)
  PCI/pwrctrl: Power off child devices on power-on failure
  PCI/pwrctrl: Only destroy devices created by pwrctrl core
  PCI/pwrctrl: generic: Fix leaking array from of_regulator_bulk_get_all()
  PCI/pwrctrl: tc9563: Switch per-port reset to GPIO descriptor API
  PCI/pwrctrl: tc9563: Add GPIO auxiliary device support
  dt-bindings: PCI: toshiba,tc9563: Document embedded GPIO controller
  gpio: tc9563: Add support for the embedded GPIO controller
  PCI: qcom: Skip DWC MSI host initialization when DT 'msi-map' is present
  PCI: qcom: Ignore -ENODEV after Root Port reset
  PCI: qcom-ep: Deinitialize endpoint on remove
  PCI/sysfs: Reject unaligned resource I/O port accesses
  PCI: rcar-gen4: Add missing PM ops
  PCI: endpoint: pci-epf-vntb: Honor MSI-X selection
  PCI: endpoint: pci-epf-vntb: Use a local epf pointer in interrupt setup
  PCI: dwc: imx6: Use regmap_assign_bits() for conditional set/clear
  PCI: dwc: Describe DWC PCIe fields with full masks and FIELD_PREP()
  PCI: dwc: Rename pp->irq_domain to pp->msi_irq_domain
  PCI: dwc: Rename dw_pcie_allocate_domains() to dw_pcie_allocate_msi_domains()
  PCI: dwc: Correct PCIE_DW_PLAT_EP reference in Kconfig help
  PCI/ASPM: Disable ASPM when driver requests it
  ...
This commit is contained in:
Linus Torvalds 2026-10-23 15:34:47 -10:00
commit 17bb0e2f6c
89 changed files with 3134 additions and 1829 deletions

View File

@ -244,10 +244,16 @@ Contact: Narendra K <narendra_k@dell.com>, linux-bugs@dell.com
Description:
Reading this attribute will provide the firmware
given name (SMBIOS type 41 string or ACPI _DSM string) of
the PCI device. The attribute will be created only
if the firmware has given a name to the PCI device.
ACPI _DSM string name will be given priority if the
system firmware provides SMBIOS type 41 string also.
the PCI device. The attribute will be created only if the
firmware naming mechanism is implemented for the device:
a SMBIOS type 41 record with a non-empty reference
designation, or a Device Name _DSM that returns the name
as a string or a buffer. The value read is empty when the
firmware implements the _DSM but gives the device no name,
which per PCI Firmware r3.3, sec 4.6.7 the firmware reports
as a NULL string. ACPI _DSM string name will be given
priority if the system firmware provides SMBIOS type 41
string also.
Users:
Userspace applications interested in knowing the
firmware assigned name of the PCI device.

View File

@ -8,4 +8,3 @@ PCI Native Host Bridge and Endpoint Drivers
:maxdepth: 2
pci-controller-drivers
rcar-pcie-firmware

View File

@ -1,32 +0,0 @@
.. SPDX-License-Identifier: GPL-2.0
=================================================
Firmware of PCIe controller for Renesas R-Car V4H
=================================================
Renesas R-Car V4H (r8a779g0) has a PCIe controller, requiring a specific
firmware download during startup.
However, Renesas currently cannot distribute the firmware free of charge.
The firmware file "104_PCIe_fw_addr_data_ver1.05.txt" (note that the file name
might be different between different datasheet revisions) can be found in the
datasheet encoded as text, and as such, the file's content must be converted
back to binary form. This can be achieved using the following example script:
.. code-block:: sh
$ awk '/^\s*0x[0-9A-Fa-f]{4}\s+0x[0-9A-Fa-f]{4}/ { print substr($2,5,2) substr($2,3,2) }' \
104_PCIe_fw_addr_data_ver1.05.txt | \
xxd -p -r > rcar_gen4_pcie.bin
Once the text content has been converted into a binary firmware file, verify
its checksum as follows:
.. code-block:: sh
$ sha1sum rcar_gen4_pcie.bin
1d0bd4b189b4eb009f5d564b1f93a79112994945 rcar_gen4_pcie.bin
The resulting binary file called "rcar_gen4_pcie.bin" should be placed in the
"/lib/firmware" directory before the driver runs.

View File

@ -1,670 +0,0 @@
NVIDIA Tegra PCIe controller
Required properties:
- compatible: Must be:
- "nvidia,tegra20-pcie": for Tegra20
- "nvidia,tegra30-pcie": for Tegra30
- "nvidia,tegra124-pcie": for Tegra124 and Tegra132
- "nvidia,tegra210-pcie": for Tegra210
- "nvidia,tegra186-pcie": for Tegra186
- power-domains: To ungate power partition by BPMP powergate driver. Must
contain BPMP phandle and PCIe power partition ID. This is required only
for Tegra186.
- device_type: Must be "pci"
- reg: A list of physical base address and length for each set of controller
registers. Must contain an entry for each entry in the reg-names property.
- reg-names: Must include the following entries:
"pads": PADS registers
"afi": AFI registers
"cs": configuration space region
- interrupts: A list of interrupt outputs of the controller. Must contain an
entry for each entry in the interrupt-names property.
- interrupt-names: Must include the following entries:
"intr": The Tegra interrupt that is asserted for controller interrupts
"msi": The Tegra interrupt that is asserted when an MSI is received
- bus-range: Range of bus numbers associated with this controller
- #address-cells: Address representation for root ports (must be 3)
- cell 0 specifies the bus and device numbers of the root port:
[23:16]: bus number
[15:11]: device number
- cell 1 denotes the upper 32 address bits and should be 0
- cell 2 contains the lower 32 address bits and is used to translate to the
CPU address space
- #size-cells: Size representation for root ports (must be 2)
- ranges: Describes the translation of addresses for root ports and standard
PCI regions. The entries must be 6 cells each, where the first three cells
correspond to the address as described for the #address-cells property
above, the fourth cell is the physical CPU address to translate to and the
fifth and six cells are as described for the #size-cells property above.
- The first two entries are expected to translate the addresses for the root
port registers, which are referenced by the assigned-addresses property of
the root port nodes (see below).
- The remaining entries setup the mapping for the standard I/O, memory and
prefetchable PCI regions. The first cell determines the type of region
that is setup:
- 0x81000000: I/O memory region
- 0x82000000: non-prefetchable memory region
- 0xc2000000: prefetchable memory region
Please refer to the standard PCI bus binding document for a more detailed
explanation.
- #interrupt-cells: Size representation for interrupts (must be 1)
- interrupt-map-mask and interrupt-map: Standard PCI IRQ mapping properties
Please refer to the standard PCI bus binding document for a more detailed
explanation.
- clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
- pex
- afi
- pll_e
- cml (not required for Tegra20)
- resets: Must contain an entry for each entry in reset-names.
See ../reset/reset.txt for details.
- reset-names: Must include the following entries:
- pex
- afi
- pcie_x
Optional properties:
- pinctrl-names: A list of pinctrl state names. Must contain the following
entries:
- "default": active state, puts PCIe I/O out of deep power down state
- "idle": puts PCIe I/O into deep power down state
- pinctrl-0: phandle for the default/active state of pin configurations.
- pinctrl-1: phandle for the idle state of pin configurations.
Required properties on Tegra124 and later (deprecated):
- phys: Must contain an entry for each entry in phy-names.
- phy-names: Must include the following entries:
- pcie
These properties are deprecated in favour of per-lane PHYs define in each of
the root ports (see below).
Power supplies for Tegra20:
- avdd-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
- vdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
- avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
supply 1.05 V.
- avdd-plle-supply: Power supply for PLLE, which is shared with SATA. Must
supply 1.05 V.
- vddio-pex-clk-supply: Power supply for PCIe clock. Must supply 3.3 V.
Power supplies for Tegra30:
- Required:
- avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
supply 1.05 V.
- avdd-plle-supply: Power supply for PLLE, which is shared with SATA. Must
supply 1.05 V.
- vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
supply 1.8 V.
- hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
Must supply 3.3 V.
- Optional:
- If lanes 0 to 3 are used:
- avdd-pexa-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
- vdd-pexa-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
- If lanes 4 or 5 are used:
- avdd-pexb-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
- vdd-pexb-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
Power supplies for Tegra124:
- Required:
- avddio-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
- dvddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
- hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
Must supply 3.3 V.
- vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
supply 2.8-3.3 V.
Power supplies for Tegra210:
- Required:
- hvddio-pex-supply: High-voltage supply for PCIe I/O and PCIe output
clocks. Must supply 1.8 V.
- dvddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
- vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
supply 1.8 V.
Power supplies for Tegra186:
- Required:
- dvdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
- hvdd-pex-pll-supply: High-voltage supply for PLLE (shared with USB3). Must
supply 1.8 V.
- hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
Must supply 1.8 V.
- vddio-pexctl-aud-supply: Power supply for PCIe side band signals. Must
supply 1.8 V.
Root ports are defined as subnodes of the PCIe controller node.
Required properties:
- device_type: Must be "pci"
- assigned-addresses: Address and size of the port configuration registers
- reg: PCI bus address of the root port
- #address-cells: Must be 3
- #size-cells: Must be 2
- ranges: Sub-ranges distributed from the PCIe controller node. An empty
property is sufficient.
- nvidia,num-lanes: Number of lanes to use for this port. Valid combinations
are:
- Root port 0 uses 4 lanes, root port 1 is unused.
- Both root ports use 2 lanes.
Required properties for Tegra124 and later:
- phys: Must contain an phandle to a PHY for each entry in phy-names.
- phy-names: Must include an entry for each active lane. Note that the number
of entries does not have to (though usually will) be equal to the specified
number of lanes in the nvidia,num-lanes property. Entries are of the form
"pcie-N": where N ranges from 0 to the value specified in nvidia,num-lanes.
Examples:
=========
Tegra20:
--------
SoC DTSI:
pcie-controller@80003000 {
compatible = "nvidia,tegra20-pcie";
device_type = "pci";
reg = <0x80003000 0x00000800 /* PADS registers */
0x80003800 0x00000200 /* AFI registers */
0x90000000 0x10000000>; /* configuration space */
reg-names = "pads", "afi", "cs";
interrupts = <0 98 0x04 /* controller interrupt */
0 99 0x04>; /* MSI interrupt */
interrupt-names = "intr", "msi";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0>;
interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x82000000 0 0x80000000 0x80000000 0 0x00001000 /* port 0 registers */
0x82000000 0 0x80001000 0x80001000 0 0x00001000 /* port 1 registers */
0x81000000 0 0 0x82000000 0 0x00010000 /* downstream I/O */
0x82000000 0 0xa0000000 0xa0000000 0 0x10000000 /* non-prefetchable memory */
0xc2000000 0 0xb0000000 0xb0000000 0 0x10000000>; /* prefetchable memory */
clocks = <&tegra_car 70>, <&tegra_car 72>, <&tegra_car 118>;
clock-names = "pex", "afi", "pll_e";
resets = <&tegra_car 70>, <&tegra_car 72>, <&tegra_car 74>;
reset-names = "pex", "afi", "pcie_x";
status = "disabled";
pci@1,0 {
device_type = "pci";
assigned-addresses = <0x82000800 0 0x80000000 0 0x1000>;
reg = <0x000800 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <2>;
};
pci@2,0 {
device_type = "pci";
assigned-addresses = <0x82001000 0 0x80001000 0 0x1000>;
reg = <0x001000 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <2>;
};
};
Board DTS:
pcie-controller@80003000 {
status = "okay";
vdd-supply = <&pci_vdd_reg>;
pex-clk-supply = <&pci_clk_reg>;
/* root port 00:01.0 */
pci@1,0 {
status = "okay";
/* bridge 01:00.0 (optional) */
pci@0,0 {
reg = <0x010000 0 0 0 0>;
#address-cells = <3>;
#size-cells = <2>;
device_type = "pci";
/* endpoint 02:00.0 */
pci@0,0 {
reg = <0x020000 0 0 0 0>;
};
};
};
};
Note that devices on the PCI bus are dynamically discovered using PCI's bus
enumeration and therefore don't need corresponding device nodes in DT. However
if a device on the PCI bus provides a non-probeable bus such as I2C or SPI,
device nodes need to be added in order to allow the bus' children to be
instantiated at the proper location in the operating system's device tree (as
illustrated by the optional nodes in the example above).
Tegra30:
--------
SoC DTSI:
pcie-controller@3000 {
compatible = "nvidia,tegra30-pcie";
device_type = "pci";
reg = <0x00003000 0x00000800 /* PADS registers */
0x00003800 0x00000200 /* AFI registers */
0x10000000 0x10000000>; /* configuration space */
reg-names = "pads", "afi", "cs";
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH /* controller interrupt */
GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
interrupt-names = "intr", "msi";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0>;
interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x82000000 0 0x00000000 0x00000000 0 0x00001000 /* port 0 configuration space */
0x82000000 0 0x00001000 0x00001000 0 0x00001000 /* port 1 configuration space */
0x82000000 0 0x00004000 0x00004000 0 0x00001000 /* port 2 configuration space */
0x81000000 0 0 0x02000000 0 0x00010000 /* downstream I/O */
0x82000000 0 0x20000000 0x20000000 0 0x08000000 /* non-prefetchable memory */
0xc2000000 0 0x28000000 0x28000000 0 0x18000000>; /* prefetchable memory */
clocks = <&tegra_car TEGRA30_CLK_PCIE>,
<&tegra_car TEGRA30_CLK_AFI>,
<&tegra_car TEGRA30_CLK_PLL_E>,
<&tegra_car TEGRA30_CLK_CML0>;
clock-names = "pex", "afi", "pll_e", "cml";
resets = <&tegra_car 70>,
<&tegra_car 72>,
<&tegra_car 74>;
reset-names = "pex", "afi", "pcie_x";
status = "disabled";
pci@1,0 {
device_type = "pci";
assigned-addresses = <0x82000800 0 0x00000000 0 0x1000>;
reg = <0x000800 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <2>;
};
pci@2,0 {
device_type = "pci";
assigned-addresses = <0x82001000 0 0x00001000 0 0x1000>;
reg = <0x001000 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <2>;
};
pci@3,0 {
device_type = "pci";
assigned-addresses = <0x82001800 0 0x00004000 0 0x1000>;
reg = <0x001800 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <2>;
};
};
Board DTS:
pcie-controller@3000 {
status = "okay";
avdd-pexa-supply = <&ldo1_reg>;
vdd-pexa-supply = <&ldo1_reg>;
avdd-pexb-supply = <&ldo1_reg>;
vdd-pexb-supply = <&ldo1_reg>;
avdd-pex-pll-supply = <&ldo1_reg>;
avdd-plle-supply = <&ldo1_reg>;
vddio-pex-ctl-supply = <&sys_3v3_reg>;
hvdd-pex-supply = <&sys_3v3_pexs_reg>;
pci@1,0 {
status = "okay";
};
pci@3,0 {
status = "okay";
};
};
Tegra124:
---------
SoC DTSI:
pcie-controller@1003000 {
compatible = "nvidia,tegra124-pcie";
device_type = "pci";
reg = <0x0 0x01003000 0x0 0x00000800 /* PADS registers */
0x0 0x01003800 0x0 0x00000800 /* AFI registers */
0x0 0x02000000 0x0 0x10000000>; /* configuration space */
reg-names = "pads", "afi", "cs";
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
interrupt-names = "intr", "msi";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0>;
interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x82000000 0 0x01000000 0x0 0x01000000 0 0x00001000 /* port 0 configuration space */
0x82000000 0 0x01001000 0x0 0x01001000 0 0x00001000 /* port 1 configuration space */
0x81000000 0 0x0 0x0 0x12000000 0 0x00010000 /* downstream I/O (64 KiB) */
0x82000000 0 0x13000000 0x0 0x13000000 0 0x0d000000 /* non-prefetchable memory (208 MiB) */
0xc2000000 0 0x20000000 0x0 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */
clocks = <&tegra_car TEGRA124_CLK_PCIE>,
<&tegra_car TEGRA124_CLK_AFI>,
<&tegra_car TEGRA124_CLK_PLL_E>,
<&tegra_car TEGRA124_CLK_CML0>;
clock-names = "pex", "afi", "pll_e", "cml";
resets = <&tegra_car 70>,
<&tegra_car 72>,
<&tegra_car 74>;
reset-names = "pex", "afi", "pcie_x";
status = "disabled";
pci@1,0 {
device_type = "pci";
assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>;
reg = <0x000800 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <2>;
};
pci@2,0 {
device_type = "pci";
assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>;
reg = <0x001000 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <1>;
};
};
Board DTS:
pcie-controller@1003000 {
status = "okay";
avddio-pex-supply = <&vdd_1v05_run>;
dvddio-pex-supply = <&vdd_1v05_run>;
avdd-pex-pll-supply = <&vdd_1v05_run>;
hvdd-pex-supply = <&vdd_3v3_lp0>;
hvdd-pex-pll-e-supply = <&vdd_3v3_lp0>;
vddio-pex-ctl-supply = <&vdd_3v3_lp0>;
avdd-pll-erefe-supply = <&avdd_1v05_run>;
/* Mini PCIe */
pci@1,0 {
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-4}>;
phy-names = "pcie-0";
status = "okay";
};
/* Gigabit Ethernet */
pci@2,0 {
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-2}>;
phy-names = "pcie-0";
status = "okay";
};
};
Tegra210:
---------
SoC DTSI:
pcie-controller@1003000 {
compatible = "nvidia,tegra210-pcie";
device_type = "pci";
reg = <0x0 0x01003000 0x0 0x00000800 /* PADS registers */
0x0 0x01003800 0x0 0x00000800 /* AFI registers */
0x0 0x02000000 0x0 0x10000000>; /* configuration space */
reg-names = "pads", "afi", "cs";
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
interrupt-names = "intr", "msi";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0>;
interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x82000000 0 0x01000000 0x0 0x01000000 0 0x00001000 /* port 0 configuration space */
0x82000000 0 0x01001000 0x0 0x01001000 0 0x00001000 /* port 1 configuration space */
0x81000000 0 0x0 0x0 0x12000000 0 0x00010000 /* downstream I/O (64 KiB) */
0x82000000 0 0x13000000 0x0 0x13000000 0 0x0d000000 /* non-prefetchable memory (208 MiB) */
0xc2000000 0 0x20000000 0x0 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */
clocks = <&tegra_car TEGRA210_CLK_PCIE>,
<&tegra_car TEGRA210_CLK_AFI>,
<&tegra_car TEGRA210_CLK_PLL_E>,
<&tegra_car TEGRA210_CLK_CML0>;
clock-names = "pex", "afi", "pll_e", "cml";
resets = <&tegra_car 70>,
<&tegra_car 72>,
<&tegra_car 74>;
reset-names = "pex", "afi", "pcie_x";
status = "disabled";
pci@1,0 {
device_type = "pci";
assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>;
reg = <0x000800 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <4>;
};
pci@2,0 {
device_type = "pci";
assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>;
reg = <0x001000 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <1>;
};
};
Board DTS:
pcie-controller@1003000 {
status = "okay";
avdd-pll-uerefe-supply = <&avdd_1v05_pll>;
hvddio-pex-supply = <&vdd_1v8>;
dvddio-pex-supply = <&vdd_pex_1v05>;
dvdd-pex-pll-supply = <&vdd_pex_1v05>;
hvdd-pex-pll-e-supply = <&vdd_1v8>;
vddio-pex-ctl-supply = <&vdd_1v8>;
pci@1,0 {
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-0}>,
<&{/padctl@7009f000/pads/pcie/lanes/pcie-1}>,
<&{/padctl@7009f000/pads/pcie/lanes/pcie-2}>,
<&{/padctl@7009f000/pads/pcie/lanes/pcie-3}>;
phy-names = "pcie-0", "pcie-1", "pcie-2", "pcie-3";
status = "okay";
};
pci@2,0 {
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-4}>;
phy-names = "pcie-0";
status = "okay";
};
};
Tegra186:
---------
SoC DTSI:
pcie@10003000 {
compatible = "nvidia,tegra186-pcie";
power-domains = <&bpmp TEGRA186_POWER_DOMAIN_PCX>;
device_type = "pci";
reg = <0x0 0x10003000 0x0 0x00000800 /* PADS registers */
0x0 0x10003800 0x0 0x00000800 /* AFI registers */
0x0 0x40000000 0x0 0x10000000>; /* configuration space */
reg-names = "pads", "afi", "cs";
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
<GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
interrupt-names = "intr", "msi";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0>;
interrupt-map = <0 0 0 0 &gic GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x82000000 0 0x10000000 0x0 0x10000000 0 0x00001000 /* port 0 configuration space */
0x82000000 0 0x10001000 0x0 0x10001000 0 0x00001000 /* port 1 configuration space */
0x82000000 0 0x10004000 0x0 0x10004000 0 0x00001000 /* port 2 configuration space */
0x81000000 0 0x0 0x0 0x50000000 0 0x00010000 /* downstream I/O (64 KiB) */
0x82000000 0 0x50100000 0x0 0x50100000 0 0x07F00000 /* non-prefetchable memory (127 MiB) */
0xc2000000 0 0x58000000 0x0 0x58000000 0 0x28000000>; /* prefetchable memory (640 MiB) */
clocks = <&bpmp TEGRA186_CLK_AFI>,
<&bpmp TEGRA186_CLK_PCIE>,
<&bpmp TEGRA186_CLK_PLLE>;
clock-names = "afi", "pex", "pll_e";
resets = <&bpmp TEGRA186_RESET_AFI>,
<&bpmp TEGRA186_RESET_PCIE>,
<&bpmp TEGRA186_RESET_PCIEXCLK>;
reset-names = "afi", "pex", "pcie_x";
status = "disabled";
pci@1,0 {
device_type = "pci";
assigned-addresses = <0x82000800 0 0x10000000 0 0x1000>;
reg = <0x000800 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <2>;
};
pci@2,0 {
device_type = "pci";
assigned-addresses = <0x82001000 0 0x10001000 0 0x1000>;
reg = <0x001000 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <1>;
};
pci@3,0 {
device_type = "pci";
assigned-addresses = <0x82001800 0 0x10004000 0 0x1000>;
reg = <0x001800 0 0 0 0>;
status = "disabled";
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <1>;
};
};
Board DTS:
pcie@10003000 {
status = "okay";
dvdd-pex-supply = <&vdd_pex>;
hvdd-pex-pll-supply = <&vdd_1v8>;
hvdd-pex-supply = <&vdd_1v8>;
vddio-pexctl-aud-supply = <&vdd_1v8>;
pci@1,0 {
nvidia,num-lanes = <4>;
status = "okay";
};
pci@2,0 {
nvidia,num-lanes = <0>;
status = "disabled";
};
pci@3,0 {
nvidia,num-lanes = <1>;
status = "disabled";
};
};

View File

@ -0,0 +1,532 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/pci/nvidia,tegra20-pcie.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NVIDIA Tegra PCIe controller
maintainers:
- Thierry Reding <thierry.reding@gmail.com>
- Jon Hunter <jonathanh@nvidia.com>
properties:
compatible:
oneOf:
- enum:
- nvidia,tegra20-pcie
- nvidia,tegra30-pcie
- nvidia,tegra124-pcie
- nvidia,tegra210-pcie
- nvidia,tegra186-pcie
- items:
- const: nvidia,tegra132-pcie
- const: nvidia,tegra124-pcie
reg:
items:
- description: PADS registers
- description: AFI registers
- description: configuration space region
reg-names:
items:
- const: pads
- const: afi
- const: cs
interrupts:
items:
- description: PCIe controller interrupt
- description: MSI controller interrupt
interrupt-names:
items:
- const: intr
- const: msi
clocks:
items:
- description: PCI controller clock
- description: AFI interface clock
- description: reference PLL clock
- description: CML clock
minItems: 3
clock-names:
items:
- const: pex
- const: afi
- const: pll_e
- const: cml
minItems: 3
resets:
items:
- description: AFI interface reset
- description: PCI controller reset
- description: PCI bus reset
reset-names:
items:
- const: pex
- const: afi
- const: pcie_x
interconnects:
items:
- description: AFI memory read client
- description: AFI memory write client
interconnect-names:
items:
- const: dma-mem # read
- const: write
iommus:
maxItems: 1
operating-points-v2:
$ref: /schemas/types.yaml#/definitions/phandle
power-domains:
items:
- description: phandle to the core power domain
pinctrl-names:
items:
# active state, puts PCIe I/O out of deep power down state
- const: default
# puts PCIe I/O into deep power down state
- const: idle
patternProperties:
"^pci@1?[0-9a-f](,[0-7])?$":
description: Root ports are defined as subnodes of the PCIe controller
node.
Note that devices on the PCI bus are dynamically discovered using PCI's
bus enumeration and therefore don't need corresponding device nodes in
DT. However if a device on the PCI bus provides a non-probeable bus such
as I2C or SPI, device nodes need to be added in order to allow the bus'
children to be instantiated at the proper location in the operating
system's device tree (as illustrated by the optional nodes in the
examples below).
$ref: /schemas/pci/pci-pci-bridge.yaml#
unevaluatedProperties: false
properties:
nvidia,num-lanes:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Number of lanes to use for this port. Valid combinations
are:
- Root port 0 uses 4 lanes, root port 1 is unused.
- Both root ports use 2 lanes.
enum: [ 0, 1, 2, 4 ]
phys:
description: Must contain an phandle to a PHY for each entry in
phy-names.
minItems: 1
maxItems: 4
phy-names:
description: Must include an entry for each active lane. Note that the
number of entries does not have to (though usually will) be equal to
the specified number of lanes in the nvidia,num-lanes property.
Entries are of the form "pcie-N", where N ranges from 0 to the value
specified in nvidia,num-lanes.
items:
enum:
- pcie-0
- pcie-1
- pcie-2
- pcie-3
minItems: 1
maxItems: 4
required:
- reg
- reg-names
- interrupts
- interrupt-names
- clocks
- clock-names
- resets
- reset-names
unevaluatedProperties: false
allOf:
- $ref: /schemas/pinctrl/pinctrl-consumer.yaml#
- $ref: /schemas/pci/pci-host-bridge.yaml#
- $ref: pci-iommu.yaml#
- if:
properties:
compatible:
contains:
const: nvidia,tegra20-pcie
then:
properties:
clocks:
maxItems: 3
clock-names:
maxItems: 3
avdd-pex-supply:
description: Power supply for analog PCIe logic. Must supply 1.05 V.
vdd-pex-supply:
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
avdd-pex-pll-supply:
description: Power supply for dedicated (internal) PCIe PLL. Must
supply 1.05 V.
avdd-plle-supply:
description: Power supply for PLLE, which is shared with SATA. Must
supply 1.05 V.
vddio-pex-clk-supply:
description: Power supply for PCIe clock. Must supply 3.3 V.
required:
- avdd-pex-supply
- vdd-pex-supply
- avdd-pex-pll-supply
- avdd-plle-supply
- vddio-pex-clk-supply
- if:
properties:
compatible:
contains:
const: nvidia,tegra30-pcie
then:
properties:
avdd-pex-pll-supply:
description: Power supply for dedicated (internal) PCIe PLL. Must
supply 1.05 V.
avdd-plle-supply:
description: Power supply for PLLE, which is shared with SATA. Must
supply 1.05 V.
vddio-pex-ctl-supply:
description: Power supply for PCIe control I/O partition. Must
supply 1.8 V.
hvdd-pex-supply:
description: High-voltage supply for PCIe I/O and PCIe output
clocks. Must supply 3.3 V.
avdd-pexa-supply:
description: Power supply for analog PCIe logic. Must supply 1.05 V.
Required if lanes 0 through 3 are used.
vdd-pexa-supply:
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
Required if lanes 0 through 3 are used.
avdd-pexb-supply:
description: Power supply for analog PCIe logic. Must supply 1.05 V.
Required if lanes 4 and 5 are used.
vdd-pexb-supply:
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
Required if lanes 4 and 5 are used.
required:
- avdd-pex-pll-supply
- avdd-plle-supply
- vddio-pex-ctl-supply
- hvdd-pex-supply
- if:
properties:
compatible:
contains:
const: nvidia,tegra124-pcie
then:
properties:
phys:
minItems: 1
maxItems: 1
deprecated: true
phy-names:
items:
- const: pcie
deprecated: true
avddio-pex-supply:
description: Power supply for analog PCIe logic. Must supply 1.05 V.
dvddio-pex-supply:
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
hvdd-pex-supply:
description: High-voltage supply for PCIe I/O and PCIe output
clocks. Must supply 3.3 V.
vddio-pex-ctl-supply:
description: Power supply for PCIe control I/O partition. Must
supply 2.8-3.3 V.
avdd-pex-pll-supply:
deprecated: true
hvdd-pex-pll-e-supply:
deprecated: true
avdd-pll-erefe-supply:
deprecated: true
required:
- avddio-pex-supply
- dvddio-pex-supply
- hvdd-pex-supply
- vddio-pex-ctl-supply
- if:
properties:
compatible:
contains:
const: nvidia,tegra210-pcie
then:
properties:
hvddio-pex-supply:
description: High-voltage supply for PCIe I/O and PCIe output
clocks. Must supply 1.8 V.
dvddio-pex-supply:
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
vddio-pex-ctl-supply:
description: Power supply for PCIe control I/O partition. Must
supply 1.8 V.
avdd-pll-uerefe-supply:
deprecated: true
dvdd-pex-pll-supply:
deprecated: true
hvdd-pex-pll-e-supply:
deprecated: true
required:
- hvddio-pex-supply
- dvddio-pex-supply
- vddio-pex-ctl-supply
- if:
properties:
compatible:
contains:
const: nvidia,tegra186-pcie
then:
properties:
clocks:
maxItems: 3
clock-names:
maxItems: 3
power-domains:
description: To ungate power partition by BPMP powergate driver.
Must contain BPMP phandle and PCIe power partition ID.
dvdd-pex-supply:
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
hvdd-pex-pll-supply:
description: High-voltage supply for PLLE (shared with USB3). Must
supply 1.8 V.
hvdd-pex-supply:
description: High-voltage supply for PCIe I/O and PCIe output
clocks. Must supply 1.8 V.
vddio-pexctl-aud-supply:
description: Power supply for PCIe side band signals. Must supply
1.8 V.
required:
- dvdd-pex-supply
- hvdd-pex-pll-supply
- hvdd-pex-supply
- vddio-pexctl-aud-supply
examples:
# Tegra20
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
pcie@80003000 {
compatible = "nvidia,tegra20-pcie";
device_type = "pci";
reg = <0x80003000 0x00000800>, /* PADS registers */
<0x80003800 0x00000200>, /* AFI registers */
<0x90000000 0x10000000>; /* configuration space */
reg-names = "pads", "afi", "cs";
interrupts = <0 98 0x04>, /* controller interrupt */
<0 99 0x04>; /* MSI interrupt */
interrupt-names = "intr", "msi";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0>;
interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x02000000 0 0x80000000 0x80000000 0 0x00001000>, /* port 0 registers */
<0x02000000 0 0x80001000 0x80001000 0 0x00001000>, /* port 1 registers */
<0x01000000 0 0 0x82000000 0 0x00010000>, /* downstream I/O */
<0x02000000 0 0xa0000000 0xa0000000 0 0x10000000>, /* non-prefetchable memory */
<0x42000000 0 0xb0000000 0xb0000000 0 0x10000000>; /* prefetchable memory */
clocks = <&tegra_car 70>, <&tegra_car 72>, <&tegra_car 118>;
clock-names = "pex", "afi", "pll_e";
resets = <&tegra_car 70>, <&tegra_car 72>, <&tegra_car 74>;
reset-names = "pex", "afi", "pcie_x";
avdd-pex-supply = <&pci_vdd_reg>;
vdd-pex-supply = <&pci_vdd_reg>;
avdd-pex-pll-supply = <&pci_vdd_reg>;
avdd-plle-supply = <&pci_vdd_reg>;
vddio-pex-clk-supply = <&pci_clk_reg>;
/* root port 00:01.0 */
pci@1,0 {
device_type = "pci";
assigned-addresses = <0x82000800 0 0x80000000 0 0x1000>;
reg = <0x000800 0 0 0 0>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <2>;
/* bridge 01:00.0 (optional) */
pci@0,0 {
reg = <0x010000 0 0 0 0>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
device_type = "pci";
/* endpoint 02:00.0 */
ethernet@0,0 {
reg = <0x020000 0 0 0 0>;
};
};
};
pci@2,0 {
device_type = "pci";
assigned-addresses = <0x82001000 0 0x80001000 0 0x1000>;
reg = <0x001000 0 0 0 0>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <2>;
};
};
# Tegra210
- |
#include <dt-bindings/clock/tegra210-car.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
pcie@1003000 {
compatible = "nvidia,tegra210-pcie";
device_type = "pci";
reg = <0x01003000 0x00000800>, /* PADS registers */
<0x01003800 0x00000800>, /* AFI registers */
<0x02000000 0x10000000>; /* configuration space */
reg-names = "pads", "afi", "cs";
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
interrupt-names = "intr", "msi";
#interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 0>;
interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges = <0x02000000 0 0x01000000 0x01000000 0 0x00001000>, /* port 0 configuration space */
<0x02000000 0 0x01001000 0x01001000 0 0x00001000>, /* port 1 configuration space */
<0x01000000 0 0x0 0x12000000 0 0x00010000>, /* downstream I/O (64 KiB) */
<0x02000000 0 0x13000000 0x13000000 0 0x0d000000>, /* non-prefetchable memory (208 MiB) */
<0x42000000 0 0x20000000 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */
clocks = <&tegra_car TEGRA210_CLK_PCIE>,
<&tegra_car TEGRA210_CLK_AFI>,
<&tegra_car TEGRA210_CLK_PLL_E>,
<&tegra_car TEGRA210_CLK_CML0>;
clock-names = "pex", "afi", "pll_e", "cml";
resets = <&tegra_car 70>,
<&tegra_car 72>,
<&tegra_car 74>;
reset-names = "pex", "afi", "pcie_x";
avdd-pll-uerefe-supply = <&avdd_1v05_pll>;
hvddio-pex-supply = <&vdd_1v8>;
dvddio-pex-supply = <&vdd_pex_1v05>;
dvdd-pex-pll-supply = <&vdd_pex_1v05>;
hvdd-pex-pll-e-supply = <&vdd_1v8>;
vddio-pex-ctl-supply = <&vdd_1v8>;
pci@1,0 {
device_type = "pci";
assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>;
reg = <0x000800 0 0 0 0>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <4>;
phys = <&phy_pcie0>, <&phy_pcie1>, <&phy_pcie2>, <&phy_pcie3>;
phy-names = "pcie-0", "pcie-1", "pcie-2", "pcie-3";
};
pci@2,0 {
device_type = "pci";
assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>;
reg = <0x001000 0 0 0 0>;
bus-range = <0x00 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
nvidia,num-lanes = <1>;
phys = <&phy_pcie4>;
phy-names = "pcie-0";
};
};

View File

@ -56,11 +56,14 @@ properties:
maxItems: 1
resets:
maxItems: 1
minItems: 1
maxItems: 2
reset-names:
minItems: 1
items:
- const: pwr
- const: core
max-link-speed:
maximum: 4
@ -109,8 +112,8 @@ examples:
clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
clock-names = "core", "ref";
power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
resets = <&cpg 624>;
reset-names = "pwr";
resets = <&cpg 624>,<&cpg 1121>;
reset-names = "pwr", "core";
max-link-speed = <4>;
num-lanes = <2>;
max-functions = /bits/ 8 <2>;

View File

@ -10,30 +10,20 @@ title: Renesas R-Car Gen4 PCIe Host
maintainers:
- Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
allOf:
- $ref: snps,dw-pcie.yaml#
properties:
compatible:
items:
- enum:
- renesas,r8a779f0-pcie # R-Car S4-8
- renesas,r8a779g0-pcie # R-Car V4H
- renesas,r8a779h0-pcie # R-Car V4M
- const: renesas,rcar-gen4-pcie # R-Car Gen4
oneOf:
- items:
- enum:
- renesas,r8a779f0-pcie # R-Car S4-8
- renesas,r8a779g0-pcie # R-Car V4H
- renesas,r8a779h0-pcie # R-Car V4M
- const: renesas,rcar-gen4-pcie # R-Car Gen4
reg:
maxItems: 7
reg-names:
items:
- const: dbi
- const: dbi2
- const: atu
- const: dma
- const: app
- const: phy
- const: config
- items:
- enum:
- renesas,r8a78000-pcie4 # R-Car X5H PCIe4
- const: renesas,rcar-gen5-pcie4 # R-Car Gen5 PCIe4
interrupts:
maxItems: 4
@ -57,11 +47,14 @@ properties:
maxItems: 1
resets:
maxItems: 1
minItems: 1
maxItems: 2
reset-names:
minItems: 1
items:
- const: pwr
- const: core
max-link-speed:
maximum: 4
@ -84,6 +77,46 @@ required:
- resets
- reset-names
allOf:
- $ref: snps,dw-pcie.yaml#
- if:
properties:
compatible:
contains:
const: renesas,rcar-gen4-pcie
then:
properties:
reg:
maxItems: 7
reg-names:
items:
- const: dbi
- const: dbi2
- const: atu
- const: dma
- const: app
- const: phy
- const: config
- if:
properties:
compatible:
contains:
const: renesas,rcar-gen5-pcie4
then:
properties:
reg:
maxItems: 6
reg-names:
items:
- const: dbi
- const: dbi2
- const: atu
- const: dma
- const: app
- const: config
unevaluatedProperties: false
examples:
@ -111,8 +144,8 @@ examples:
clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
clock-names = "core", "ref";
power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
resets = <&cpg 624>;
reset-names = "pwr";
resets = <&cpg 624>,<&cpg 1121>;
reset-names = "pwr", "core";
max-link-speed = <4>;
num-lanes = <2>;
#address-cells = <3>;

View File

@ -13,7 +13,7 @@ description: |
PCIe host controller found in Renesas RZ/G3S and similar SoCs complies
with PCIe Base Specification 4.0 and supports different link speeds
depending on the SoC variant:
- Gen2 (5 GT/s): RZ/G3S
- Gen2 (5 GT/s): RZ/G3{L,S}
- Gen3 (8 GT/s): RZ/G3E, RZ/V2H(P), RZ/V2N
properties:
@ -21,6 +21,7 @@ properties:
oneOf:
- enum:
- renesas,r9a08g045-pcie # RZ/G3S
- renesas,r9a08g046-pcie # RZ/G3L
- renesas,r9a09g047-pcie # RZ/G3E
- renesas,r9a09g057-pcie # RZ/V2H(P)
- items:
@ -87,14 +88,18 @@ properties:
interrupt-controller: true
clocks:
minItems: 2
items:
- description: System clock
- description: PM control clock or clock for L1 substate handling
- description: PM control clock
clock-names:
minItems: 2
items:
- const: aclk
- enum: [pm, pmu]
- const: pmu
resets:
minItems: 1
@ -215,6 +220,8 @@ allOf:
maxItems: 16
interrupt-names:
maxItems: 16
clocks:
maxItems: 2
clock-names:
items:
- const: aclk
@ -223,6 +230,28 @@ allOf:
minItems: 7
reset-names:
minItems: 7
- if:
properties:
compatible:
contains:
const: renesas,r9a08g046-pcie
then:
properties:
interrupts:
maxItems: 16
interrupt-names:
maxItems: 16
clocks:
minItems: 3
clock-names:
items:
- const: aclk
- const: pm
- const: pmu
resets:
maxItems: 1
reset-names:
maxItems: 1
- if:
properties:
compatible:
@ -236,6 +265,8 @@ allOf:
minItems: 23
interrupt-names:
minItems: 23
clocks:
maxItems: 2
clock-names:
items:
- const: aclk

View File

@ -31,6 +31,11 @@ properties:
description:
GPIO controlling the RESX# pin.
gpio-controller: true
'#gpio-cells':
const: 2
vdd18-supply: true
vdd09-supply: true
@ -128,7 +133,7 @@ examples:
ranges;
bus-range = <0x01 0xff>;
pcie@0,0 {
tc9563: pcie@0,0 {
compatible = "pci1179,0623";
reg = <0x10000 0x0 0x0 0x0 0x0>;
@ -149,6 +154,9 @@ examples:
resx-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
gpio-controller;
#gpio-cells = <2>;
pcie@1,0 {
compatible = "pciclass,0604";
reg = <0x20800 0x0 0x0 0x0 0x0>;
@ -158,6 +166,8 @@ examples:
ranges;
bus-range = <0x03 0xff>;
reset-gpios = <&tc9563 2 GPIO_ACTIVE_LOW>;
toshiba,no-dfe-support;
};
@ -170,6 +180,8 @@ examples:
ranges;
bus-range = <0x04 0xff>;
reset-gpios = <&tc9563 3 GPIO_ACTIVE_LOW>;
toshiba,tx-amplitude-microvolt = <10>;
};

View File

@ -41,6 +41,10 @@ properties:
description:
Maps a Requester ID to an MSI controller and associated MSI sideband data.
resets:
description: Reset for the PCIe controller.
maxItems: 1
ranges:
maxItems: 2
@ -61,6 +65,17 @@ properties:
interrupt-controller: true
patternProperties:
'^pcie@[0-2],0$':
type: object
$ref: /schemas/pci/pci-pci-bridge.yaml#
properties:
reg:
maxItems: 1
unevaluatedProperties: false
required:
- reg
- reg-names
@ -76,6 +91,9 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/reset/xlnx-versal-resets.h>
versal {
#address-cells = <2>;
#size-cells = <2>;
@ -99,6 +117,16 @@ examples:
reg = <0x0 0xfca10000 0x0 0x1000>,
<0x6 0x00000000 0x0 0x10000000>;
reg-names = "cpm_slcr", "cfg";
resets = <&versal_reset VERSAL_RST_PCIE_CORE0>;
pcie@0,0 {
device_type = "pci";
reg = <0x0 0x0 0x0 0x0 0x0>;
reset-gpios = <&gpio1 38 GPIO_ACTIVE_LOW>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
};
pcie_intc_0: interrupt-controller {
#address-cells = <0>;
#interrupt-cells = <1>;
@ -127,6 +155,16 @@ examples:
<0x06 0x00000000 0x00 0x1000000>,
<0x00 0xfce20000 0x00 0x1000000>;
reg-names = "cpm_slcr", "cfg", "cpm_csr";
resets = <&versal_reset VERSAL_RST_PCIE_CORE0>;
pcie@0,0 {
device_type = "pci";
reg = <0x0 0x0 0x0 0x0 0x0>;
reset-gpios = <&gpio1 38 GPIO_ACTIVE_LOW>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
};
pcie_intc_1: interrupt-controller {
#address-cells = <0>;

View File

@ -167,9 +167,11 @@ In this case the initiator and target pci_devices are known and the P2P subsyste
is used to determine the mapping type. The phys_addr_t-based DMA API is used to
establish the dma_addr_t.
Lifecycle is controlled by DMABUF move_notify(). When the exporting driver wants
Lifecycle is controlled by DMABUF revocation. When the exporting driver wants
to remove() it must deliver an invalidation shutdown to all DMABUF importing
drivers through move_notify() and synchronously DMA unmap all the MMIO.
drivers through dma_buf_invalidate_mappings() and synchronously DMA unmap all
the MMIO. Importers unable to complete that unmap within bounded time have to
be rejected when they attach, which dma_buf_attach_revocable() checks for.
No importing driver can continue to have a DMA map to the MMIO after the
exporting driver has destroyed its p2p_provider.

View File

@ -21110,7 +21110,6 @@ M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
L: linux-pci@vger.kernel.org
L: linux-renesas-soc@vger.kernel.org
S: Maintained
F: Documentation/PCI/controller/rcar-pcie-firmware.rst
F: Documentation/devicetree/bindings/pci/*rcar*
F: drivers/pci/controller/*rcar*
F: drivers/pci/controller/dwc/*rcar*

View File

@ -993,6 +993,25 @@ static void acpi_pci_root_release_info(struct pci_host_bridge *bridge)
__acpi_pci_root_release_info(bridge->release_data);
}
#define OSC_OWNER(ctrl, bit, flag) \
do { \
if (!((ctrl) & (bit))) \
flag = 0; \
} while (0)
#define FLAG(x) ((x) ? '+' : '-')
/*
* _OSC control bits for the features implemented by the PCIe port driver,
* i.e., the ones "pcie_ports=native" applies to. LTR and SHPC hotplug are
* negotiated via _OSC as well, but they are not portdrv services, so
* "pcie_ports=" has no bearing on them.
*/
#define OSC_PCIE_PORT_SERVICE_CONTROLS (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \
OSC_PCI_EXPRESS_PME_CONTROL | \
OSC_PCI_EXPRESS_AER_CONTROL | \
OSC_PCI_EXPRESS_DPC_CONTROL)
struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
struct acpi_pci_root_ops *ops,
struct acpi_pci_root_info *info,
@ -1003,6 +1022,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
int node = acpi_get_node(device->handle);
struct pci_bus *bus;
struct pci_host_bridge *host_bridge;
u32 ctrl, ext_ctrl, override = 0;
info->root = root;
info->bridge = device;
@ -1028,21 +1048,49 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
goto out_release_info;
host_bridge = to_pci_host_bridge(bus->bridge);
if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
host_bridge->native_pcie_hotplug = 0;
if (!(root->osc_control_set & OSC_PCI_SHPC_NATIVE_HP_CONTROL))
host_bridge->native_shpc_hotplug = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL))
host_bridge->native_aer = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL))
host_bridge->native_pme = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_LTR_CONTROL))
host_bridge->native_ltr = 0;
if (!(root->osc_control_set & OSC_PCI_EXPRESS_DPC_CONTROL))
host_bridge->native_dpc = 0;
if (!(root->osc_ext_control_set & OSC_CXL_ERROR_REPORTING_CONTROL))
host_bridge->native_cxl_error = 0;
ctrl = root->osc_control_set;
ext_ctrl = root->osc_ext_control_set;
/*
* If the user specified "pcie_ports=native", use the PCIe port
* services regardless of what _OSC says, i.e., proceed as though the
* platform had granted us control of them. "pcie_ports=dpc-native"
* does the same for DPC alone. This may conflict with firmware that
* expects to own those features.
*/
if (pcie_ports_native)
override = OSC_PCIE_PORT_SERVICE_CONTROLS & ~ctrl;
else if (pcie_ports_dpc_native)
override = OSC_PCI_EXPRESS_DPC_CONTROL & ~ctrl;
if (override) {
decode_osc_control(root, pcie_ports_native ?
"OS forcing control (\"pcie_ports=native\") of" :
"OS forcing control (\"pcie_ports=dpc-native\") of",
override);
ctrl |= override;
}
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_NATIVE_HP_CONTROL,
host_bridge->native_pcie_hotplug);
OSC_OWNER(ctrl, OSC_PCI_SHPC_NATIVE_HP_CONTROL,
host_bridge->native_shpc_hotplug);
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_AER_CONTROL, host_bridge->native_aer);
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_PME_CONTROL, host_bridge->native_pme);
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_LTR_CONTROL, host_bridge->native_ltr);
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_DPC_CONTROL, host_bridge->native_dpc);
OSC_OWNER(ext_ctrl, OSC_CXL_ERROR_REPORTING_CONTROL,
host_bridge->native_cxl_error);
dev_info(&root->device->dev, "OS native features: SHPCHotplug%c PCIeHotplug%c PME%c AER%c DPC%c LTR%c\n",
FLAG(host_bridge->native_shpc_hotplug),
FLAG(host_bridge->native_pcie_hotplug),
FLAG(host_bridge->native_pme),
FLAG(host_bridge->native_aer),
FLAG(host_bridge->native_dpc),
FLAG(host_bridge->native_ltr));
acpi_dev_power_up_children_with_adr(device);

View File

@ -1875,6 +1875,18 @@ config GPIO_LTC4283
endmenu
config GPIO_TC9563
tristate "Toshiba TC9563 GPIO support"
depends on PCI_PWRCTRL_TC9563 || COMPILE_TEST
default m if ARCH_QCOM
select AUXILIARY_BUS
select GPIO_REGMAP
help
This enables support for the GPIO controller embedded in the Toshiba
TC9563 (and Qualcomm QPS615). This device connects to the host
via PCIe port, which is the upstream port on an internal PCIe
switch.
menu "PCI GPIO expanders"
depends on PCI

View File

@ -185,6 +185,7 @@ obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
obj-$(CONFIG_GPIO_TANGIER) += gpio-tangier.o
obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_GPIO_TC9563) += gpio-tc9563.o
obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o
obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o

View File

@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026 by RISCstar Solutions Corporation. All rights reserved.
*/
/*
* The Toshiba TC9563 implements a PCIe Gen 3 switch that connects an
* upstream x4 port to two downstream PCIe x2 ports. It incorporates
* an internal endpoint on a internal PCIe port that implements two
* Synopsys XGMAC Ethernet interfaces.
*
* 37 GPIOs are also implemented by an embedded GPIO controller. Three
* registers control the first 32 GPIOs (other than 20 and 21, which are
* reserved). Three other registers control GPIOs 32 through 36. GPIOs
* 22-24, 27-28, 31, and 34 are treated as "input only".
*
*/
#include <linux/auxiliary_bus.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/regmap.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/soc/qcom/tc9563.h>
/*
* There are two sets of registers, each representing (up to) 32 GPIOs with a
* stride of 4 bytes (IN1 is 4 bytes past IN0, EN1 is 4 bytes past EN0, etc.).
*/
#define TC9563_GPIO_COUNT 37
#define TC9563_GPIO_PER_REG 32
#define TC9563_GPIO_REG_STRIDE 4
static int tc9563_gpio_init_valid_mask(struct gpio_chip *gc,
unsigned long *valid_mask,
unsigned int ngpios)
{
/* GPIOs 20 and 21 are reserved */
bitmap_fill(valid_mask, ngpios);
bitmap_clear(valid_mask, 20, 2);
return 0;
}
static int tc9563_gpio_probe(struct auxiliary_device *adev,
const struct auxiliary_device_id *id)
{
struct gpio_regmap_config config = {
.parent = &adev->dev,
.ngpio = TC9563_GPIO_COUNT,
.reg_stride = TC9563_GPIO_REG_STRIDE,
.ngpio_per_reg = TC9563_GPIO_PER_REG,
.reg_dat_base = GPIO_REGMAP_ADDR(TC9563_GPIO_IN0_OFFSET),
.reg_set_base = GPIO_REGMAP_ADDR(TC9563_GPIO_OUT0_OFFSET),
.reg_dir_in_base = GPIO_REGMAP_ADDR(TC9563_GPIO_EN0_OFFSET),
.init_valid_mask = tc9563_gpio_init_valid_mask,
};
DECLARE_BITMAP(fixed_dir_mask, TC9563_GPIO_COUNT);
DECLARE_BITMAP(fixed_dir_out, TC9563_GPIO_COUNT);
config.regmap = dev_get_platdata(&adev->dev);
if (!config.regmap)
return -EINVAL;
/*
* Only some of our GPIOs are fixed direction:
* 22, 23, 24, 27, 28, 31, and 34 are input-only.
*/
bitmap_zero(fixed_dir_mask, TC9563_GPIO_COUNT);
bitmap_set(fixed_dir_mask, 22, 3);
bitmap_set(fixed_dir_mask, 27, 2);
set_bit(31, fixed_dir_mask);
set_bit(34, fixed_dir_mask);
config.fixed_direction_mask = fixed_dir_mask;
bitmap_zero(fixed_dir_out, TC9563_GPIO_COUNT);
config.fixed_direction_output = fixed_dir_out;
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&adev->dev, &config));
};
static const struct auxiliary_device_id tc9563_gpio_ids[] = {
{ "pci_pwrctrl_tc9563." TC9563_GPIO_DEV_NAME },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(auxiliary, tc9563_gpio_ids);
static struct auxiliary_driver tc9563_gpio_driver = {
.name = TC9563_GPIO_DEV_NAME,
.probe = tc9563_gpio_probe,
.id_table = tc9563_gpio_ids,
};
module_auxiliary_driver(tc9563_gpio_driver);
MODULE_AUTHOR("Alex Elder <elder@riscstar.com>");
MODULE_AUTHOR("Daniel Thompson <daniel@riscstar.com>");
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>");
MODULE_DESCRIPTION("Toshiba TC9563 GPIO Driver");
MODULE_LICENSE("GPL");

View File

@ -210,24 +210,21 @@ static const char *cdns_pcie_hpa_ltssm_status_string(enum cdns_pcie_hpa_ltssm lt
static int ltssm_status_show(struct seq_file *s, void *v)
{
struct cdns_pcie *pci = s->private;
enum cdns_pcie_lga_ltssm lga_ltssm;
enum cdns_pcie_hpa_ltssm hpa_ltssm;
const char *str_ltssm;
u32 val;
u32 val, ltssm_val;
if (pci->is_hpa) {
val = cdns_pcie_hpa_readl(pci, REG_BANK_IP_REG,
CDNS_PCIE_HPA_PHY_DBG_STS_REG0);
hpa_ltssm = FIELD_GET(CDNS_PCIE_HPA_LTSSM_STATUS_MASK, val);
str_ltssm = cdns_pcie_hpa_ltssm_status_string(hpa_ltssm);
ltssm_val = FIELD_GET(CDNS_PCIE_HPA_LTSSM_STATUS_MASK, val);
str_ltssm = cdns_pcie_hpa_ltssm_status_string(ltssm_val);
} else {
val = cdns_pcie_readl(pci, CDNS_PCIE_LM_BASE);
lga_ltssm = FIELD_GET(CDNS_PCIE_LGA_LTSSM_STATUS_MASK, val);
str_ltssm = cdns_pcie_lga_ltssm_status_string(lga_ltssm);
ltssm_val = FIELD_GET(CDNS_PCIE_LGA_LTSSM_STATUS_MASK, val);
str_ltssm = cdns_pcie_lga_ltssm_status_string(ltssm_val);
}
seq_printf(s, "%s (0x%02x)\n", str_ltssm,
pci->is_hpa ? hpa_ltssm : lga_ltssm);
seq_printf(s, "%s (0x%02x)\n", str_ltssm, ltssm_val);
return 0;
}

View File

@ -14,14 +14,6 @@
#define CDNS_PLAT_CPU_TO_BUS_ADDR 0x0FFFFFFF
/**
* struct cdns_plat_pcie - private data for this PCIe platform driver
* @pcie: Cadence PCIe controller
*/
struct cdns_plat_pcie {
struct cdns_pcie *pcie;
};
static const struct of_device_id cdns_plat_pcie_of_match[];
static u64 cdns_plat_cpu_addr_fixup(struct cdns_pcie *pcie, u64 cpu_addr)
@ -36,12 +28,11 @@ static const struct cdns_pcie_ops cdns_plat_ops = {
static int cdns_plat_pcie_probe(struct platform_device *pdev)
{
const struct cdns_plat_pcie_of_data *data;
struct cdns_plat_pcie *cdns_plat_pcie;
struct device *dev = &pdev->dev;
struct pci_host_bridge *bridge;
struct cdns_pcie *pcie;
struct cdns_pcie_ep *ep;
struct cdns_pcie_rc *rc;
int phy_count;
bool is_rc;
int ret;
@ -52,11 +43,6 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
is_rc = data->is_rc;
pr_debug(" Started %s with is_rc: %d\n", __func__, is_rc);
cdns_plat_pcie = devm_kzalloc(dev, sizeof(*cdns_plat_pcie), GFP_KERNEL);
if (!cdns_plat_pcie)
return -ENOMEM;
platform_set_drvdata(pdev, cdns_plat_pcie);
if (is_rc) {
if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_HOST))
return -ENODEV;
@ -68,9 +54,9 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
rc = pci_host_bridge_priv(bridge);
rc->pcie.dev = dev;
rc->pcie.ops = &cdns_plat_ops;
cdns_plat_pcie->pcie = &rc->pcie;
pcie = &rc->pcie;
ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
ret = cdns_pcie_init_phy(dev, pcie);
if (ret) {
dev_err(dev, "failed to init phy\n");
return ret;
@ -95,9 +81,9 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
ep->pcie.dev = dev;
ep->pcie.ops = &cdns_plat_ops;
cdns_plat_pcie->pcie = &ep->pcie;
pcie = &ep->pcie;
ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
ret = cdns_pcie_init_phy(dev, pcie);
if (ret) {
dev_err(dev, "failed to init phy\n");
return ret;
@ -115,18 +101,17 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
goto err_init;
}
platform_set_drvdata(pdev, pcie);
return 0;
err_init:
err_get_sync:
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
cdns_pcie_disable_phy(cdns_plat_pcie->pcie);
phy_count = cdns_plat_pcie->pcie->phy_count;
while (phy_count--)
device_link_del(cdns_plat_pcie->pcie->link[phy_count]);
cdns_pcie_disable_phy(pcie);
return 0;
return ret;
}
static void cdns_plat_pcie_shutdown(struct platform_device *pdev)

View File

@ -222,7 +222,6 @@ int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie)
struct device_node *np = dev->of_node;
int phy_count;
struct phy **phy;
struct device_link **link;
int i;
int ret;
const char *name;
@ -238,10 +237,6 @@ int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie)
if (!phy)
return -ENOMEM;
link = devm_kcalloc(dev, phy_count, sizeof(*link), GFP_KERNEL);
if (!link)
return -ENOMEM;
for (i = 0; i < phy_count; i++) {
of_property_read_string_index(np, "phy-names", i, &name);
phy[i] = devm_phy_get(dev, name);
@ -249,17 +244,10 @@ int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie)
ret = PTR_ERR(phy[i]);
goto err_phy;
}
link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS);
if (!link[i]) {
devm_phy_put(dev, phy[i]);
ret = -EINVAL;
goto err_phy;
}
}
pcie->phy_count = phy_count;
pcie->phy = phy;
pcie->link = link;
ret = cdns_pcie_enable_phy(pcie);
if (ret)
@ -268,10 +256,8 @@ int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie)
return 0;
err_phy:
while (--i >= 0) {
device_link_del(link[i]);
while (--i >= 0)
devm_phy_put(dev, phy[i]);
}
return ret;
}

View File

@ -260,7 +260,6 @@ struct cdns_plat_pcie_of_data {
* @is_hpa: indicates if the architecture is HPA
* @phy_count: number of supported PHY devices
* @phy: list of pointers to specific PHY control blocks
* @link: list of pointers to corresponding device link representations
* @ops: Platform-specific ops to control various inputs from Cadence PCIe
* wrapper
* @cdns_pcie_reg_offsets: Register bank offsets for different SoC
@ -276,7 +275,6 @@ struct cdns_pcie {
bool is_hpa;
int phy_count;
struct phy **phy;
struct device_link **link;
const struct cdns_pcie_ops *ops;
const struct cdns_plat_pcie_of_data *cdns_pcie_reg_offsets;
int max_link_speed;

View File

@ -295,7 +295,7 @@ config PCIE_DW_PLAT_HOST
Designware IP.
This controller can work either as EP or RC. In order to enable
host-specific features PCIE_DW_PLAT_HOST must be selected and in
order to enable device-specific features PCI_DW_PLAT_EP must be
order to enable device-specific features PCIE_DW_PLAT_EP must be
selected.
config PCIE_DW_PLAT_EP
@ -310,7 +310,7 @@ config PCIE_DW_PLAT_EP
in Designware IP.
This controller can work either as EP or RC. In order to enable
host-specific features PCIE_DW_PLAT_HOST must be selected and in
order to enable device-specific features PCI_DW_PLAT_EP must be
order to enable device-specific features PCIE_DW_PLAT_EP must be
selected.
config PCIE_QCOM_COMMON

View File

@ -9,7 +9,6 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@ -214,7 +213,7 @@ static int dra7xx_pcie_handle_msi(struct dw_pcie_rp *pp, int index)
pos = find_first_bit(&val, MAX_MSI_IRQS_PER_CTRL);
while (pos != MAX_MSI_IRQS_PER_CTRL) {
generic_handle_domain_irq(pp->irq_domain,
generic_handle_domain_irq(pp->msi_irq_domain,
(index * MAX_MSI_IRQS_PER_CTRL) + pos);
pos++;
pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL, pos);
@ -680,7 +679,6 @@ static int dra7xx_pcie_probe(struct platform_device *pdev)
int i;
int phy_count;
struct phy **phy;
struct device_link **link;
void __iomem *base;
struct dw_pcie *pci;
struct dra7xx_pcie *dra7xx;
@ -728,30 +726,16 @@ static int dra7xx_pcie_probe(struct platform_device *pdev)
if (!phy)
return -ENOMEM;
link = devm_kcalloc(dev, phy_count, sizeof(*link), GFP_KERNEL);
if (!link)
return -ENOMEM;
dra7xx->clk = devm_clk_get_optional(dev, NULL);
dra7xx->clk = devm_clk_get_optional_enabled(dev, NULL);
if (IS_ERR(dra7xx->clk))
return dev_err_probe(dev, PTR_ERR(dra7xx->clk),
"clock request failed");
ret = clk_prepare_enable(dra7xx->clk);
if (ret)
return ret;
for (i = 0; i < phy_count; i++) {
snprintf(name, sizeof(name), "pcie-phy%d", i);
phy[i] = devm_phy_get(dev, name);
if (IS_ERR(phy[i]))
return PTR_ERR(phy[i]);
link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS);
if (!link[i]) {
ret = -EINVAL;
goto err_link;
}
}
dra7xx->base = base;
@ -853,10 +837,6 @@ static int dra7xx_pcie_probe(struct platform_device *pdev)
pm_runtime_disable(dev);
dra7xx_pcie_disable_phy(dra7xx);
err_link:
while (--i >= 0)
device_link_del(link[i]);
return ret;
}

View File

@ -80,6 +80,18 @@
#define IMX95_SID_MASK GENMASK(5, 0)
#define IMX95_MAX_LUT 32
#define IMX95_PCIE_PHY_REG_ADDR 0x3008
#define IMX95_PCIE_PHY_REG_EN BIT(31)
#define IMX95_PCIE_PHY_REG_ADDR_MASK GENMASK(15, 0)
#define IMX95_PCIE_PHY_REG_DATA 0x300c
#define IMX95_PCIE_PHY_MPLLB_OVRD_IN 0x2004
#define IMX95_PCIE_PHY_MPLLB_OVRD_BW_EN BIT(10)
#define IMX95_PCIE_PHY_MPLLB_BW_IN 0x2005
#define IMX95_PCIE_PHY_MPLLB_BW_VAL 0x8c
#define IMX95_PCIE_RST_CTRL 0x3010
#define IMX95_PCIE_COLD_RST BIT(0)
@ -180,8 +192,12 @@ struct imx_pcie {
struct imx_lut_data luts[IMX95_MAX_LUT];
/* power domain for pcie */
struct device *pd_pcie;
/* device link for pcie power domain */
struct device_link *pd_link;
/* power domain for pcie phy */
struct device *pd_pcie_phy;
/* device link for pcie phy power domain */
struct device_link *pd_phy_link;
struct phy *phy;
const struct imx_pcie_drvdata *drvdata;
@ -270,8 +286,19 @@ static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie)
return 0;
}
static void imx95_pcie_phy_write(struct imx_pcie *imx_pcie, int addr, u16 data)
{
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_REG_ADDR,
IMX95_PCIE_PHY_REG_EN, IMX95_PCIE_PHY_REG_EN);
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_REG_ADDR,
IMX95_PCIE_PHY_REG_ADDR_MASK, addr);
regmap_write(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_REG_DATA, data);
}
static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
{
u32 val;
/*
* ERR051624: The Controller Without Vaux Cannot Exit L23 Ready
* Through Beacon or PERST# De-assertion
@ -290,6 +317,21 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
IMX95_PCIE_PHY_CR_PARA_SEL,
IMX95_PCIE_PHY_CR_PARA_SEL);
/* Flush the IMX95_PCIE_PHY_CR_PARA_SEL update */
regmap_read(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, &val);
/*
* A delay is required between the assertion of
* IMX95_PCIE_PHY_CR_PARA_SEL and subsequent PHY register write
* operation to ensure values are successfully written.
*/
udelay(200);
imx95_pcie_phy_write(imx_pcie, IMX95_PCIE_PHY_MPLLB_BW_IN,
IMX95_PCIE_PHY_MPLLB_BW_VAL);
imx95_pcie_phy_write(imx_pcie, IMX95_PCIE_PHY_MPLLB_OVRD_IN,
IMX95_PCIE_PHY_MPLLB_OVRD_BW_EN);
return 0;
}
@ -542,7 +584,7 @@ static int imx_setup_phy_mpll(struct imx_pcie *imx_pcie)
return 0;
for (i = 0; i < imx_pcie->num_clks; i++)
if (strncmp(clks[i].id, "pcie_phy", 8) == 0)
if (clks[i].id && strncmp(clks[i].id, "pcie_phy", 8) == 0)
phy_rate = clk_get_rate(clks[i].clk);
switch (phy_rate) {
@ -639,10 +681,34 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
}
#endif
static void imx_pcie_detach_pd_action(void *data)
{
struct imx_pcie *imx_pcie = data;
if (!IS_ERR_OR_NULL(imx_pcie->pd_phy_link)) {
device_link_del(imx_pcie->pd_phy_link);
imx_pcie->pd_phy_link = NULL;
}
if (!IS_ERR_OR_NULL(imx_pcie->pd_link)) {
device_link_del(imx_pcie->pd_link);
imx_pcie->pd_link = NULL;
}
if (!IS_ERR_OR_NULL(imx_pcie->pd_pcie_phy)) {
dev_pm_domain_detach(imx_pcie->pd_pcie_phy, true);
imx_pcie->pd_pcie_phy = NULL;
}
if (!IS_ERR_OR_NULL(imx_pcie->pd_pcie)) {
dev_pm_domain_detach(imx_pcie->pd_pcie, true);
imx_pcie->pd_pcie = NULL;
}
}
static int imx_pcie_attach_pd(struct device *dev)
{
struct imx_pcie *imx_pcie = dev_get_drvdata(dev);
struct device_link *link;
/* Do nothing when in a single power domain */
if (dev->pm_domain)
@ -654,11 +720,11 @@ static int imx_pcie_attach_pd(struct device *dev)
/* Do nothing when power domain missing */
if (!imx_pcie->pd_pcie)
return 0;
link = device_link_add(dev, imx_pcie->pd_pcie,
DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE);
if (!link) {
imx_pcie->pd_link = device_link_add(dev, imx_pcie->pd_pcie,
DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE);
if (!imx_pcie->pd_link) {
dev_err(dev, "Failed to add device_link to pcie pd\n");
return -EINVAL;
}
@ -667,11 +733,11 @@ static int imx_pcie_attach_pd(struct device *dev)
if (IS_ERR(imx_pcie->pd_pcie_phy))
return PTR_ERR(imx_pcie->pd_pcie_phy);
link = device_link_add(dev, imx_pcie->pd_pcie_phy,
DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE);
if (!link) {
imx_pcie->pd_phy_link = device_link_add(dev, imx_pcie->pd_pcie_phy,
DL_FLAG_STATELESS |
DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE);
if (!imx_pcie->pd_phy_link) {
dev_err(dev, "Failed to add device_link to pcie_phy pd\n");
return -EINVAL;
}
@ -681,12 +747,8 @@ static int imx_pcie_attach_pd(struct device *dev)
static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{
if (enable)
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_REF_CLK_EN);
else
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_REF_CLK_EN);
regmap_assign_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_REF_CLK_EN, enable);
return 0;
}
@ -802,12 +864,8 @@ static void imx_pcie_clk_disable(struct imx_pcie *imx_pcie)
static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
{
if (assert)
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
else
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
regmap_assign_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_TEST_POWERDOWN, assert);
/* Force PCIe PHY reset */
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET,
@ -817,12 +875,8 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
{
if (assert)
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_TEST_PD);
else
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_TEST_PD);
regmap_assign_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_TEST_PD, assert);
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_SW_RST,
assert ? IMX6Q_GPR1_PCIE_SW_RST : 0);
@ -835,12 +889,8 @@ static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
static int imx6q_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
{
if (assert)
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_TEST_PD);
else
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_TEST_PD);
regmap_assign_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
IMX6Q_GPR1_PCIE_TEST_PD, assert);
if (!assert)
usleep_range(200, 500);
@ -955,9 +1005,9 @@ static int imx_pcie_wait_for_speed_change(struct imx_pcie *imx_pcie)
unsigned int retries;
for (retries = 0; retries < 200; retries++) {
tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
tmp = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
/* Test if the speed change finished. */
if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
if (!(tmp & SPEED_CHANGE))
return 0;
usleep_range(100, 1000);
}
@ -1040,9 +1090,9 @@ static int imx_pcie_start_link(struct dw_pcie *pci)
* Start Directed Speed Change so the best possible
* speed both link partners support can be negotiated.
*/
tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
tmp |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
tmp = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
tmp |= SPEED_CHANGE;
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, tmp);
dw_pcie_dbi_ro_wr_dis(pci);
ret = imx_pcie_wait_for_speed_change(imx_pcie);
@ -1058,8 +1108,8 @@ static int imx_pcie_start_link(struct dw_pcie *pci)
err_reset_phy:
dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0),
dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG1));
dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG0),
dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG1));
imx_pcie_reset_phy(imx_pcie);
return 0;
}
@ -1389,7 +1439,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
if (!pp->skip_pwrctrl_off) {
ret = pci_pwrctrl_power_on_devices(dev);
if (ret) {
dev_err(dev, "failed to power on pwrctrl devices\n");
dev_err_probe(dev, ret, "failed to power on pwrctrl devices\n");
goto err_reg_disable;
}
}
@ -1498,18 +1548,18 @@ static void imx_pcie_host_post_init(struct dw_pcie_rp *pp)
/*
* ERR051586: Compliance with 8GT/s Receiver Impedance ECN
*
* The default value of GEN3_RELATED_OFF[GEN3_ZRXDC_NONCOMPL]
* The default value of GEN3_CTRL[GEN3_ZRXDC_NONCOMPL]
* is 1 which makes receiver non-compliant with the ZRX-DC
* parameter for 2.5 GT/s when operating at 8 GT/s or higher.
* It causes unnecessary timeout in L1.
*
* Workaround: Program GEN3_RELATED_OFF[GEN3_ZRXDC_NONCOMPL]
* Workaround: Program GEN3_CTRL[GEN3_ZRXDC_NONCOMPL]
* to 0.
*/
dw_pcie_dbi_ro_wr_en(pci);
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
val &= ~GEN3_CTRL_GEN3_ZRXDC_NONCOMPL;
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
dw_pcie_dbi_ro_wr_dis(pci);
}
@ -1521,7 +1571,7 @@ static void imx_pcie_host_post_init(struct dw_pcie_rp *pp)
}
/*
* In old DWC implementations, PCIE_ATU_INHIBIT_PAYLOAD in iATU Ctrl2
* In old DWC implementations, ATU_INHIBIT_PAYLOAD in iATU Ctrl2
* register is reserved, so the generic DWC implementation of sending the
* PME_Turn_Off message using a dummy MMIO write cannot be used.
*/
@ -1836,7 +1886,8 @@ static int imx_pcie_probe(struct platform_device *pdev)
return dev_err_probe(dev, imx_pcie->num_clks,
"failed to get clocks\n");
for (i = 0; i < imx_pcie->num_clks; i++)
if (strncmp(imx_pcie->clks[i].id, "extref", 6) == 0)
if (imx_pcie->clks[i].id &&
strncmp(imx_pcie->clks[i].id, "extref", 6) == 0)
imx_pcie->enable_ext_refclk = true;
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_PHYDRV)) {
@ -1951,6 +2002,10 @@ static int imx_pcie_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, imx_pcie);
ret = devm_add_action_or_reset(dev, imx_pcie_detach_pd_action, imx_pcie);
if (ret)
return ret;
ret = imx_pcie_attach_pd(dev);
if (ret)
return ret;
@ -1975,9 +2030,11 @@ static int imx_pcie_probe(struct platform_device *pdev)
pm_runtime_no_callbacks(dev);
ret = devm_pm_runtime_set_active_enabled(dev);
if (ret < 0)
return ret;
goto err_pwrctrl_destroy;
}
pci->pp.suspend_poweroff = true;
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY))
pci->pp.skip_l23_ready = true;
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_KEEP_MSI_CAP))

View File

@ -24,6 +24,7 @@
#include <linux/of_pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/resource.h>
#include <linux/signal.h>
@ -129,7 +130,6 @@ struct keystone_pcie {
int num_lanes;
u32 num_viewport;
struct phy **phy;
struct device_link **link;
struct device_node *msi_intc_np;
struct irq_domain *intx_irq_domain;
struct device_node *np;
@ -309,7 +309,7 @@ static int ks_pcie_msi_host_init(struct dw_pcie_rp *pp)
dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, ks_pcie->app.start);
pp->msi_irq_chip = &ks_pcie_msi_irq_chip;
return dw_pcie_allocate_domains(pp);
return dw_pcie_allocate_msi_domains(pp);
}
static void ks_pcie_handle_intx_irq(struct keystone_pcie *ks_pcie,
@ -497,8 +497,8 @@ static bool ks_pcie_link_up(struct dw_pcie *pci)
{
u32 val;
val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0);
return (val & PORT_LOGIC_LTSSM_STATE_MASK) == PORT_LOGIC_LTSSM_STATE_L0;
val = dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG0);
return (val & PORT_LINK_DEBUG0_LTSSM_MASK) == DW_PCIE_LTSSM_L0;
}
static void ks_pcie_stop_link(struct dw_pcie *pci)
@ -633,7 +633,7 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc)
vector = offset + (pos << 3);
dev_dbg(dev, "irq: bit %d, vector %d\n", pos, vector);
generic_handle_domain_irq(pp->irq_domain, vector);
generic_handle_domain_irq(pp->msi_irq_domain, vector);
}
chained_irq_exit(chip, desc);
@ -1131,7 +1131,6 @@ static int ks_pcie_probe(struct platform_device *pdev)
enum dw_pcie_device_mode mode;
struct dw_pcie *pci;
struct keystone_pcie *ks_pcie;
struct device_link **link;
struct gpio_desc *gpiod;
struct resource *res;
void __iomem *base;
@ -1202,31 +1201,17 @@ static int ks_pcie_probe(struct platform_device *pdev)
if (!phy)
return -ENOMEM;
link = devm_kcalloc(dev, num_lanes, sizeof(*link), GFP_KERNEL);
if (!link)
return -ENOMEM;
for (i = 0; i < num_lanes; i++) {
snprintf(name, sizeof(name), "pcie-phy%d", i);
phy[i] = devm_phy_optional_get(dev, name);
if (IS_ERR(phy[i])) {
ret = PTR_ERR(phy[i]);
goto err_link;
}
if (!phy[i])
continue;
link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS);
if (!link[i]) {
ret = -EINVAL;
goto err_link;
goto err;
}
}
ks_pcie->np = np;
ks_pcie->pci = pci;
ks_pcie->link = link;
ks_pcie->num_lanes = num_lanes;
ks_pcie->phy = phy;
@ -1236,7 +1221,7 @@ static int ks_pcie_probe(struct platform_device *pdev)
ret = PTR_ERR(gpiod);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get reset GPIO\n");
goto err_link;
goto err;
}
/* Obtain references to the PHYs */
@ -1251,7 +1236,7 @@ static int ks_pcie_probe(struct platform_device *pdev)
if (ret) {
dev_err(dev, "failed to enable phy\n");
goto err_link;
goto err;
}
platform_set_drvdata(pdev, ks_pcie);
@ -1338,25 +1323,18 @@ static int ks_pcie_probe(struct platform_device *pdev)
pm_runtime_disable(dev);
ks_pcie_disable_phy(ks_pcie);
err_link:
while (--i >= 0 && link[i])
device_link_del(link[i]);
err:
return ret;
}
static void ks_pcie_remove(struct platform_device *pdev)
{
struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev);
struct device_link **link = ks_pcie->link;
int num_lanes = ks_pcie->num_lanes;
struct device *dev = &pdev->dev;
pm_runtime_put(dev);
pm_runtime_disable(dev);
ks_pcie_disable_phy(ks_pcie);
while (num_lanes--)
device_link_del(link[num_lanes]);
}
static struct platform_driver ks_pcie_driver = {

View File

@ -419,6 +419,7 @@ static struct platform_driver ls_pcie_driver = {
.of_match_table = ls_pcie_of_match,
.suppress_bind_attrs = true,
.pm = &ls_pcie_pm_ops,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
module_platform_driver(ls_pcie_driver);

View File

@ -163,7 +163,7 @@ static void dw_pcie_ep_clear_ib_maps(struct dw_pcie_ep *ep, u8 func_no, enum pci
/* Tear down the BAR Match Mode mapping, if any. */
if (ep_func->bar_to_atu[bar]) {
atu_index = ep_func->bar_to_atu[bar] - 1;
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index);
dw_pcie_disable_atu(pci, ATU_REGION_DIR_IB, atu_index);
clear_bit(atu_index, ep->ib_window_map);
ep_func->bar_to_atu[bar] = 0;
return;
@ -177,7 +177,7 @@ static void dw_pcie_ep_clear_ib_maps(struct dw_pcie_ep *ep, u8 func_no, enum pci
if (!indexes)
return;
for (i = 0; i < num; i++) {
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, indexes[i]);
dw_pcie_disable_atu(pci, ATU_REGION_DIR_IB, indexes[i]);
clear_bit(indexes[i], ep->ib_window_map);
}
devm_kfree(dev, indexes);
@ -647,7 +647,7 @@ static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
return;
ep->outbound_addr[atu_index] = 0;
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, atu_index);
dw_pcie_disable_atu(pci, ATU_REGION_DIR_OB, atu_index);
clear_bit(atu_index, ep->ob_window_map);
}
@ -1077,10 +1077,10 @@ int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no,
if (!ep_func || !ep_func->msix_cap)
return -EINVAL;
msg_data = (func_no << PCIE_MSIX_DOORBELL_PF_SHIFT) |
msg_data = FIELD_PREP(MSIX_DOORBELL_PF_MASK, func_no) |
(interrupt_num - 1);
dw_pcie_writel_dbi(pci, PCIE_MSIX_DOORBELL, msg_data);
dw_pcie_writel_dbi(pci, MSIX_DOORBELL, msg_data);
return 0;
}
@ -1194,6 +1194,9 @@ void dw_pcie_ep_deinit(struct dw_pcie_ep *ep)
epc->mem->window.page_size);
pci_epc_mem_exit(epc);
if (ep->ops->post_deinit)
ep->ops->post_deinit(ep);
}
EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit);
@ -1553,7 +1556,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
ep->page_size);
if (ret < 0) {
dev_err(dev, "Failed to initialize address space\n");
return ret;
goto err_deinit;
}
ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
@ -1568,6 +1571,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
err_exit_epc_mem:
pci_epc_mem_exit(epc);
err_deinit:
if (ep->ops->post_deinit)
ep->ops->post_deinit(ep);
return ret;
}

View File

@ -82,7 +82,7 @@ void dw_handle_msi_irq(struct dw_pcie_rp *pp)
continue;
for_each_set_bit(pos, &status, MAX_MSI_IRQS_PER_CTRL)
generic_handle_demux_domain_irq(pp->irq_domain, irq_off + pos);
generic_handle_demux_domain_irq(pp->msi_irq_domain, irq_off + pos);
}
}
@ -207,7 +207,7 @@ static const struct irq_domain_ops dw_pcie_msi_domain_ops = {
.free = dw_pcie_irq_domain_free,
};
int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)
int dw_pcie_allocate_msi_domains(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct irq_domain_info info = {
@ -217,15 +217,15 @@ int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)
.host_data = pp,
};
pp->irq_domain = msi_create_parent_irq_domain(&info, &dw_pcie_msi_parent_ops);
if (!pp->irq_domain) {
pp->msi_irq_domain = msi_create_parent_irq_domain(&info, &dw_pcie_msi_parent_ops);
if (!pp->msi_irq_domain) {
dev_err(pci->dev, "Failed to create IRQ domain\n");
return -ENOMEM;
}
return 0;
}
EXPORT_SYMBOL_GPL(dw_pcie_allocate_domains);
EXPORT_SYMBOL_GPL(dw_pcie_allocate_msi_domains);
void dw_pcie_free_msi(struct dw_pcie_rp *pp)
{
@ -236,7 +236,7 @@ void dw_pcie_free_msi(struct dw_pcie_rp *pp)
irq_set_chained_handler_and_data(pp->msi_irq[ctrl], NULL, NULL);
}
irq_domain_remove(pp->irq_domain);
irq_domain_remove(pp->msi_irq_domain);
}
EXPORT_SYMBOL_GPL(dw_pcie_free_msi);
@ -342,7 +342,7 @@ int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)
pp->msi_irq_chip = &dw_pci_msi_bottom_irq_chip;
ret = dw_pcie_allocate_domains(pp);
ret = dw_pcie_allocate_msi_domains(pp);
if (ret)
return ret;
@ -440,7 +440,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
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;
atu.ctrl2 = ATU_CFG_SHIFT_MODE_ENABLE;
ret = dw_pcie_prog_outbound_atu(pci, &atu);
if (ret)
return ret;
@ -455,7 +455,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
atu.type = PCIE_TLP_TYPE_CFG1_RDWR;
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;
atu.ctrl2 = ATU_CFG_SHIFT_MODE_ENABLE;
return dw_pcie_prog_outbound_atu(pci, &atu);
}
@ -747,8 +747,8 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus,
if (!dw_pcie_link_up(pci))
return NULL;
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
PCIE_ATU_FUNC(PCI_FUNC(devfn));
busdev = ATU_BUS(bus->number) | ATU_DEV(PCI_SLOT(devfn)) |
ATU_FUNC(PCI_FUNC(devfn));
if (pci_is_root_bus(bus->parent))
type = PCIE_TLP_TYPE_CFG0_RDWR;
@ -884,10 +884,10 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
* the MEM/IO (dma-)ranges setups.
*/
for (i = 0; i < pci->num_ob_windows; i++)
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
dw_pcie_disable_atu(pci, ATU_REGION_DIR_OB, i);
for (i = 0; i < pci->num_ib_windows; i++)
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i);
dw_pcie_disable_atu(pci, ATU_REGION_DIR_IB, i);
/*
* NOTE: For outbound address translation, outbound iATU at index 0 is
@ -918,6 +918,20 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset;
atu.pci_addr = entry->res->start - entry->offset;
if (pp->bypass_ob_mem_iatu) {
/*
* The no-match pass-through path preserves the correct
* PCI address only for identity-mapped MEM ranges.
*/
if (atu.parent_bus_addr != atu.pci_addr) {
dev_err(pci->dev,
"Cannot bypass outbound iATU for non-identity MEM range %pr\n",
entry->res);
return -EINVAL;
}
continue;
}
/* Adjust iATU size if MSG TLP region was allocated before */
if (pp->msg_res && pp->msg_res->parent == entry->res)
res_size = resource_size(entry->res) -
@ -964,8 +978,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
ret = dw_pcie_prog_outbound_atu(pci, &atu);
if (ret) {
dev_err(pci->dev, "Failed to set IO range %pr\n",
entry->res);
dev_err(pci->dev, "Failed to set IO range\n");
return ret;
}
ob_iatu_index++;
@ -1165,9 +1178,9 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
/* Program correct class for RC */
dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
val = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
val |= SPEED_CHANGE;
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, val);
dw_pcie_dbi_ro_wr_dis(pci);
@ -1224,14 +1237,21 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
int dw_pcie_suspend_noirq(struct dw_pcie *pci)
{
bool pme_capable = false;
bool d3cold, pme_capable = false;
int ret = 0;
u32 val;
if (!dw_pcie_link_up(pci))
goto stop_link;
if (!pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable))
d3cold = pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable);
/*
* Enter L2 if D3cold is possible, or if the platform loses RC power
* during suspend (in which case L2 entry is required to notify
* endpoints before power loss, regardless of D3cold support).
*/
if (!d3cold && !pci->pp.suspend_poweroff)
return 0;
if (pci->pp.ops->pme_turn_off) {

View File

@ -436,9 +436,9 @@ static inline void __iomem *dw_pcie_select_atu(struct dw_pcie *pci, u32 dir,
u32 index)
{
if (dw_pcie_cap_is(pci, IATU_UNROLL))
return pci->atu_base + PCIE_ATU_UNROLL_BASE(dir, index);
return pci->atu_base + ATU_UNROLL_BASE(dir, index);
dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, dir | index);
dw_pcie_writel_dbi(pci, IATU_VIEWPORT, dir | index);
return pci->atu_base;
}
@ -480,13 +480,13 @@ static void dw_pcie_writel_atu(struct dw_pcie *pci, u32 dir, u32 index,
static inline u32 dw_pcie_readl_atu_ob(struct dw_pcie *pci, u32 index, u32 reg)
{
return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_OB, index, reg);
return dw_pcie_readl_atu(pci, ATU_REGION_DIR_OB, index, reg);
}
static inline void dw_pcie_writel_atu_ob(struct dw_pcie *pci, u32 index, u32 reg,
u32 val)
{
dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_OB, index, reg, val);
dw_pcie_writel_atu(pci, ATU_REGION_DIR_OB, index, reg, val);
}
static inline u32 dw_pcie_enable_ecrc(u32 val)
@ -527,7 +527,7 @@ static inline u32 dw_pcie_enable_ecrc(u32 val)
* handle the scenario is to program TD bit by default.
*/
return val | PCIE_ATU_TD;
return val | ATU_TD;
}
int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
@ -548,44 +548,46 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
return -EINVAL;
}
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_BASE,
dw_pcie_writel_atu_ob(pci, atu->index, ATU_LOWER_BASE,
lower_32_bits(parent_bus_addr));
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_BASE,
dw_pcie_writel_atu_ob(pci, atu->index, ATU_UPPER_BASE,
upper_32_bits(parent_bus_addr));
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LIMIT,
dw_pcie_writel_atu_ob(pci, atu->index, ATU_LIMIT,
lower_32_bits(limit_addr));
if (dw_pcie_ver_is_ge(pci, 460A))
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_LIMIT,
dw_pcie_writel_atu_ob(pci, atu->index, ATU_UPPER_LIMIT,
upper_32_bits(limit_addr));
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_TARGET,
dw_pcie_writel_atu_ob(pci, atu->index, ATU_LOWER_TARGET,
lower_32_bits(atu->pci_addr));
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_TARGET,
dw_pcie_writel_atu_ob(pci, atu->index, ATU_UPPER_TARGET,
upper_32_bits(atu->pci_addr));
val = atu->type | atu->routing | PCIE_ATU_FUNC_NUM(atu->func_no);
val = atu->type | atu->routing | ATU_FUNC_NUM(atu->func_no);
if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) &&
dw_pcie_ver_is_ge(pci, 460A))
val |= PCIE_ATU_INCREASE_REGION_SIZE;
val |= ATU_INCREASE_REGION_SIZE;
if (!dw_pcie_ver_is_ge(pci, 510A))
val = dw_pcie_enable_ecrc(val);
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val);
dw_pcie_writel_atu_ob(pci, atu->index, ATU_REGION_CTRL1, val);
val = PCIE_ATU_ENABLE | atu->ctrl2;
val = ATU_ENABLE | atu->ctrl2;
if (atu->type == PCIE_TLP_TYPE_MSG) {
/* The data-less messages only for now */
val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code;
val |= ATU_INHIBIT_PAYLOAD | atu->code;
}
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL2, val);
if (dw_pcie_ver_is_ge(pci, 460A))
val |= ATU_DMA_BYPASS;
dw_pcie_writel_atu_ob(pci, atu->index, ATU_REGION_CTRL2, val);
/*
* Make sure ATU enable takes effect before any subsequent config
* and I/O accesses.
*/
for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
val = dw_pcie_readl_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL2);
if (val & PCIE_ATU_ENABLE)
val = dw_pcie_readl_atu_ob(pci, atu->index, ATU_REGION_CTRL2);
if (val & ATU_ENABLE)
return 0;
mdelay(LINK_WAIT_IATU);
@ -598,13 +600,13 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
static inline u32 dw_pcie_readl_atu_ib(struct dw_pcie *pci, u32 index, u32 reg)
{
return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg);
return dw_pcie_readl_atu(pci, ATU_REGION_DIR_IB, index, reg);
}
static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg,
u32 val)
{
dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
dw_pcie_writel_atu(pci, ATU_REGION_DIR_IB, index, reg, val);
}
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
@ -622,36 +624,36 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
return -EINVAL;
}
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE,
dw_pcie_writel_atu_ib(pci, index, ATU_LOWER_BASE,
lower_32_bits(pci_addr));
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_BASE,
dw_pcie_writel_atu_ib(pci, index, ATU_UPPER_BASE,
upper_32_bits(pci_addr));
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LIMIT,
dw_pcie_writel_atu_ib(pci, index, ATU_LIMIT,
lower_32_bits(limit_addr));
if (dw_pcie_ver_is_ge(pci, 460A))
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_LIMIT,
dw_pcie_writel_atu_ib(pci, index, ATU_UPPER_LIMIT,
upper_32_bits(limit_addr));
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
dw_pcie_writel_atu_ib(pci, index, ATU_LOWER_TARGET,
lower_32_bits(parent_bus_addr));
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,
dw_pcie_writel_atu_ib(pci, index, ATU_UPPER_TARGET,
upper_32_bits(parent_bus_addr));
val = type;
if (upper_32_bits(limit_addr) > upper_32_bits(pci_addr) &&
dw_pcie_ver_is_ge(pci, 460A))
val |= PCIE_ATU_INCREASE_REGION_SIZE;
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, val);
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2, PCIE_ATU_ENABLE);
val |= ATU_INCREASE_REGION_SIZE;
dw_pcie_writel_atu_ib(pci, index, ATU_REGION_CTRL1, val);
dw_pcie_writel_atu_ib(pci, index, ATU_REGION_CTRL2, ATU_ENABLE);
/*
* Make sure ATU enable takes effect before any subsequent config
* and I/O accesses.
*/
for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);
if (val & PCIE_ATU_ENABLE)
val = dw_pcie_readl_atu_ib(pci, index, ATU_REGION_CTRL2);
if (val & ATU_ENABLE)
return 0;
mdelay(LINK_WAIT_IATU);
@ -671,24 +673,24 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
!IS_ALIGNED(parent_bus_addr, size))
return -EINVAL;
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
dw_pcie_writel_atu_ib(pci, index, ATU_LOWER_TARGET,
lower_32_bits(parent_bus_addr));
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,
dw_pcie_writel_atu_ib(pci, index, ATU_UPPER_TARGET,
upper_32_bits(parent_bus_addr));
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, type |
PCIE_ATU_FUNC_NUM(func_no));
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2,
PCIE_ATU_ENABLE | PCIE_ATU_FUNC_NUM_MATCH_EN |
PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
dw_pcie_writel_atu_ib(pci, index, ATU_REGION_CTRL1, type |
ATU_FUNC_NUM(func_no));
dw_pcie_writel_atu_ib(pci, index, ATU_REGION_CTRL2,
ATU_ENABLE | ATU_FUNC_NUM_MATCH_EN |
ATU_BAR_MODE_ENABLE | (bar << 8));
/*
* Make sure ATU enable takes effect before any subsequent config
* and I/O accesses.
*/
for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);
if (val & PCIE_ATU_ENABLE)
val = dw_pcie_readl_atu_ib(pci, index, ATU_REGION_CTRL2);
if (val & ATU_ENABLE)
return 0;
mdelay(LINK_WAIT_IATU);
@ -701,7 +703,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index)
{
dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0);
dw_pcie_writel_atu(pci, dir, index, ATU_REGION_CTRL2, 0);
}
const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm)
@ -824,9 +826,9 @@ bool dw_pcie_link_up(struct dw_pcie *pci)
if (pci->ops && pci->ops->link_up)
return pci->ops->link_up(pci);
val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG1);
return ((val & PCIE_PORT_DEBUG1_LINK_UP) &&
(!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING)));
val = dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG1);
return ((val & PORT_LINK_DEBUG1_LINK_UP) &&
(!(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING)));
}
EXPORT_SYMBOL_GPL(dw_pcie_link_up);
@ -834,9 +836,9 @@ void dw_pcie_upconfig_setup(struct dw_pcie *pci)
{
u32 val;
val = dw_pcie_readl_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL);
val |= PORT_MLTI_UPCFG_SUPPORT;
dw_pcie_writel_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL, val);
val = dw_pcie_readl_dbi(pci, MULTI_LANE_CTRL);
val |= MULTI_LANE_CTRL_UPCFG_SUPPORT;
dw_pcie_writel_dbi(pci, MULTI_LANE_CTRL, val);
}
EXPORT_SYMBOL_GPL(dw_pcie_upconfig_setup);
@ -894,36 +896,36 @@ static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
return;
/* Set the number of lanes */
plc = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
plc &= ~PORT_LINK_FAST_LINK_MODE;
plc &= ~PORT_LINK_MODE_MASK;
plc = dw_pcie_readl_dbi(pci, PORT_LINK_CTRL);
plc &= ~PORT_LINK_CTRL_FLM;
plc &= ~PORT_LINK_CTRL_LC_MASK;
/* Set link width speed control register */
lwsc = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
lwsc &= ~PORT_LOGIC_LINK_WIDTH_MASK;
lwsc |= PORT_LOGIC_LINK_WIDTH_1_LANES;
lwsc = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
lwsc &= ~LINK_WIDTH_MASK;
lwsc |= LINK_WIDTH_1_LANES;
switch (num_lanes) {
case 1:
plc |= PORT_LINK_MODE_1_LANES;
plc |= PORT_LINK_CTRL_LC_1_LANES;
break;
case 2:
plc |= PORT_LINK_MODE_2_LANES;
plc |= PORT_LINK_CTRL_LC_2_LANES;
break;
case 4:
plc |= PORT_LINK_MODE_4_LANES;
plc |= PORT_LINK_CTRL_LC_4_LANES;
break;
case 8:
plc |= PORT_LINK_MODE_8_LANES;
plc |= PORT_LINK_CTRL_LC_8_LANES;
break;
case 16:
plc |= PORT_LINK_MODE_16_LANES;
plc |= PORT_LINK_CTRL_LC_16_LANES;
break;
default:
dev_err(pci->dev, "num-lanes %u: invalid value\n", num_lanes);
return;
}
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, plc);
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, lwsc);
dw_pcie_writel_dbi(pci, PORT_LINK_CTRL, plc);
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, lwsc);
cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
@ -937,48 +939,48 @@ void dw_pcie_iatu_detect(struct dw_pcie *pci)
u32 val, min, dir;
u64 max;
val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT);
val = dw_pcie_readl_dbi(pci, IATU_VIEWPORT);
if (val == 0xFFFFFFFF) {
dw_pcie_cap_set(pci, IATU_UNROLL);
max_region = min((int)pci->atu_size / 512, 256);
} else {
pci->atu_base = pci->dbi_base + PCIE_ATU_VIEWPORT_BASE;
pci->atu_size = PCIE_ATU_VIEWPORT_SIZE;
pci->atu_base = pci->dbi_base + ATU_VIEWPORT_BASE;
pci->atu_size = ATU_VIEWPORT_SIZE;
dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, 0xFF);
max_region = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) + 1;
dw_pcie_writel_dbi(pci, IATU_VIEWPORT, 0xFF);
max_region = dw_pcie_readl_dbi(pci, IATU_VIEWPORT) + 1;
}
for (ob = 0; ob < max_region; ob++) {
dw_pcie_writel_atu_ob(pci, ob, PCIE_ATU_LOWER_TARGET, 0x11110000);
val = dw_pcie_readl_atu_ob(pci, ob, PCIE_ATU_LOWER_TARGET);
dw_pcie_writel_atu_ob(pci, ob, ATU_LOWER_TARGET, 0x11110000);
val = dw_pcie_readl_atu_ob(pci, ob, ATU_LOWER_TARGET);
if (val != 0x11110000)
break;
}
for (ib = 0; ib < max_region; ib++) {
dw_pcie_writel_atu_ib(pci, ib, PCIE_ATU_LOWER_TARGET, 0x11110000);
val = dw_pcie_readl_atu_ib(pci, ib, PCIE_ATU_LOWER_TARGET);
dw_pcie_writel_atu_ib(pci, ib, ATU_LOWER_TARGET, 0x11110000);
val = dw_pcie_readl_atu_ib(pci, ib, ATU_LOWER_TARGET);
if (val != 0x11110000)
break;
}
if (ob) {
dir = PCIE_ATU_REGION_DIR_OB;
dir = ATU_REGION_DIR_OB;
} else if (ib) {
dir = PCIE_ATU_REGION_DIR_IB;
dir = ATU_REGION_DIR_IB;
} else {
dev_err(pci->dev, "No iATU regions found\n");
return;
}
dw_pcie_writel_atu(pci, dir, 0, PCIE_ATU_LIMIT, 0x0);
min = dw_pcie_readl_atu(pci, dir, 0, PCIE_ATU_LIMIT);
dw_pcie_writel_atu(pci, dir, 0, ATU_LIMIT, 0x0);
min = dw_pcie_readl_atu(pci, dir, 0, ATU_LIMIT);
if (dw_pcie_ver_is_ge(pci, 460A)) {
dw_pcie_writel_atu(pci, dir, 0, PCIE_ATU_UPPER_LIMIT, 0xFFFFFFFF);
max = dw_pcie_readl_atu(pci, dir, 0, PCIE_ATU_UPPER_LIMIT);
dw_pcie_writel_atu(pci, dir, 0, ATU_UPPER_LIMIT, 0xFFFFFFFF);
max = dw_pcie_readl_atu(pci, dir, 0, ATU_UPPER_LIMIT);
} else {
max = 0;
}
@ -1273,19 +1275,19 @@ void dw_pcie_setup(struct dw_pcie *pci)
/* Configure Gen1 N_FTS */
if (pci->n_fts[0]) {
val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR);
val &= ~(PORT_AFR_N_FTS_MASK | PORT_AFR_CC_N_FTS_MASK);
val |= PORT_AFR_N_FTS(pci->n_fts[0]);
val |= PORT_AFR_CC_N_FTS(pci->n_fts[0]);
dw_pcie_writel_dbi(pci, PCIE_PORT_AFR, val);
val = dw_pcie_readl_dbi(pci, PORT_AFAC);
val &= ~(PORT_AFAC_N_FTS_MASK | PORT_AFAC_CC_N_FTS_MASK);
val |= PORT_AFAC_N_FTS(pci->n_fts[0]);
val |= PORT_AFAC_CC_N_FTS(pci->n_fts[0]);
dw_pcie_writel_dbi(pci, PORT_AFAC, val);
}
/* Configure Gen2+ N_FTS */
if (pci->n_fts[1]) {
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_N_FTS_MASK;
val = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
val &= ~N_FTS_MASK;
val |= pci->n_fts[1];
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, val);
}
if (dw_pcie_cap_is(pci, CDM_CHECK)) {
@ -1295,10 +1297,10 @@ void dw_pcie_setup(struct dw_pcie *pci)
dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val);
}
val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
val &= ~PORT_LINK_FAST_LINK_MODE;
val |= PORT_LINK_DLL_LINK_EN;
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
val = dw_pcie_readl_dbi(pci, PORT_LINK_CTRL);
val &= ~PORT_LINK_CTRL_FLM;
val |= PORT_LINK_CTRL_DLE;
dw_pcie_writel_dbi(pci, PORT_LINK_CTRL, val);
dw_pcie_link_set_max_link_width(pci, pci->num_lanes);
}

View File

@ -71,38 +71,40 @@
#define LINK_WAIT_IATU 9
/* Synopsys-specific PCIe configuration registers */
#define PCIE_PORT_FORCE 0x708
#define PORT_FORCE_LINK 0x708 /* Port Force Link */
/* Bit[7:0] LINK_NUM: Link Number. Not used for endpoint */
#define PORT_LINK_NUM_MASK GENMASK(7, 0)
#define PORT_FORCE_DO_DESKEW_FOR_SRIS BIT(23)
#define PORT_FORCE_LINK_NUM_MASK GENMASK(7, 0) /* Link Number. Not used for endpoint */
#define PORT_FORCE_LINK_DDFS BIT(23) /* do deskew for sris */
#define PCIE_PORT_AFR 0x70C
#define PORT_AFR_N_FTS_MASK GENMASK(15, 8)
#define PORT_AFR_N_FTS(n) FIELD_PREP(PORT_AFR_N_FTS_MASK, n)
#define PORT_AFR_CC_N_FTS_MASK GENMASK(23, 16)
#define PORT_AFR_CC_N_FTS(n) FIELD_PREP(PORT_AFR_CC_N_FTS_MASK, n)
#define PORT_AFR_ENTER_ASPM BIT(30)
#define PORT_AFR_L0S_ENTRANCE_LAT_SHIFT 24
#define PORT_AFR_L0S_ENTRANCE_LAT_MASK GENMASK(26, 24)
#define PORT_AFR_L1_ENTRANCE_LAT_SHIFT 27
#define PORT_AFR_L1_ENTRANCE_LAT_MASK GENMASK(29, 27)
#define PORT_AFAC 0x70C /* Ack Frequency and L0-L1 ASPM Control */
/* Ack the number of Fast Training Sequence mask */
#define PORT_AFAC_N_FTS_MASK GENMASK(15, 8)
/* Ack the number of Fast Training Sequence */
#define PORT_AFAC_N_FTS(n) FIELD_PREP(PORT_AFAC_N_FTS_MASK, n)
#define PORT_AFAC_CC_N_FTS_MASK GENMASK(23, 16) /* Common Clock N_FTS mask */
/* Common Clock N_FTS */
#define PORT_AFAC_CC_N_FTS(n) FIELD_PREP(PORT_AFAC_CC_N_FTS_MASK, n)
#define PORT_AFAC_ENTER_ASPM BIT(30) /* ASPM L1 Entry Control */
#define PORT_AFAC_L0S_ELM GENMASK(26, 24) /* L0s Entrance Latency mask */
#define PORT_AFAC_L1_ELM GENMASK(29, 27) /* L1 Entrance Latency mask */
#define PCIE_PORT_LINK_CONTROL 0x710
#define PORT_LINK_DLL_LINK_EN BIT(5)
#define PORT_LINK_FAST_LINK_MODE BIT(7)
#define PORT_LINK_MODE_MASK GENMASK(21, 16)
#define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n)
#define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1)
#define PORT_LINK_MODE_2_LANES PORT_LINK_MODE(0x3)
#define PORT_LINK_MODE_4_LANES PORT_LINK_MODE(0x7)
#define PORT_LINK_MODE_8_LANES PORT_LINK_MODE(0xf)
#define PORT_LINK_MODE_16_LANES PORT_LINK_MODE(0x1f)
#define PORT_LINK_CTRL 0x710 /* Port Link Control Register */
#define PORT_LINK_CTRL_DLE BIT(5) /* DLL Link Enable */
#define PORT_LINK_CTRL_FLM BIT(7) /* Fast Link Mode */
#define PORT_LINK_CTRL_LC_MASK GENMASK(21, 16) /* Link Mode Enable (LINK_CAPABLE) mask */
/* Link Mode Enable (LINK_CAPABLE) */
#define PORT_LINK_CTRL_LC(n) FIELD_PREP(PORT_LINK_CTRL_LC_MASK, n)
#define PORT_LINK_CTRL_LC_1_LANES PORT_LINK_CTRL_LC(0x1) /* Link Mode 1 lane */
#define PORT_LINK_CTRL_LC_2_LANES PORT_LINK_CTRL_LC(0x3) /* Link Mode 2 lane */
#define PORT_LINK_CTRL_LC_4_LANES PORT_LINK_CTRL_LC(0x7) /* Link Mode 4 lane */
#define PORT_LINK_CTRL_LC_8_LANES PORT_LINK_CTRL_LC(0xf) /* Link Mode 8 lane */
#define PORT_LINK_CTRL_LC_16_LANES PORT_LINK_CTRL_LC(0x1f) /* Link Mode 16 lane */
#define PCIE_PORT_LANE_SKEW 0x714
#define PORT_LANE_SKEW_INSERT_MASK GENMASK(23, 0)
#define PORT_LANE_SKEW 0x714 /* Lane Skew */
#define PORT_LANE_SKEW_INSERT_MASK GENMASK(23, 0)
/*
* PCIE_TIMER_CTRL_MAX_FUNC_NUM: Timer Control and Max Function Number
* TIMER_CTRL_MAX_FUNC_NUM: Timer Control and Max Function Number
* Register.
*
* This register holds the ack frequency, latency, replay, fast link
@ -116,68 +118,79 @@
* 0x2 (SF_64): Scaling Factor is 64 (1ms is 16us)
* 0x3 (SF_16): Scaling Factor is 16 (1ms is 64us)
*/
#define PCIE_TIMER_CTRL_MAX_FUNC_NUM 0x718
#define PORT_FLT_SF_MASK GENMASK(30, 29)
#define PORT_FLT_SF_VAL_1024 0x0
#define PORT_FLT_SF_VAL_256 0x1
#define PORT_FLT_SF_VAL_64 0x2
#define PORT_FLT_SF_VAL_16 0x3
#define TIMER_CTRL_MAX_FUNC_NUM 0x718
#define PORT_FLT_SF_MASK GENMASK(30, 29)
#define PORT_FLT_SF_VAL_1024 0x0
#define PORT_FLT_SF_VAL_256 0x1
#define PORT_FLT_SF_VAL_64 0x2
#define PORT_FLT_SF_VAL_16 0x3
#define PCIE_PORT_DEBUG0 0x728
#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)
#define PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29)
#define PORT_LINK_DEBUG0 0x728 /* Port Link Debug Register 0 */
#define PORT_LINK_DEBUG0_LTSSM_MASK 0x3f /* LTSSM current state mask */
#define PORT_LINK_DEBUG1 0x72C /* Port Link Debug Register 1 */
/* LTSSM reports PHY link up or LTSSM is in Loopback */
#define PORT_LINK_DEBUG1_LINK_UP BIT(4)
#define PORT_LINK_DEBUG1_LINK_IN_TRAINING BIT(29) /* LTSSM performing link training */
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
#define PORT_LOGIC_N_FTS_MASK GENMASK(7, 0)
#define PORT_LOGIC_SPEED_CHANGE BIT(17)
#define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8)
#define PORT_LOGIC_LINK_WIDTH(n) FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n)
#define PORT_LOGIC_LINK_WIDTH_1_LANES PORT_LOGIC_LINK_WIDTH(0x1)
#define PORT_LOGIC_LINK_WIDTH_2_LANES PORT_LOGIC_LINK_WIDTH(0x2)
#define PORT_LOGIC_LINK_WIDTH_4_LANES PORT_LOGIC_LINK_WIDTH(0x4)
#define PORT_LOGIC_LINK_WIDTH_8_LANES PORT_LOGIC_LINK_WIDTH(0x8)
#define LINK_WIDTH_SPEED_CTRL 0x80C /* Link Width and Speed Change Control */
#define N_FTS_MASK GENMASK(7, 0)
#define SPEED_CHANGE BIT(17)
#define LINK_WIDTH_MASK GENMASK(12, 8)
#define LINK_WIDTH(n) FIELD_PREP(LINK_WIDTH_MASK, n)
#define LINK_WIDTH_1_LANES LINK_WIDTH(0x1)
#define LINK_WIDTH_2_LANES LINK_WIDTH(0x2)
#define LINK_WIDTH_4_LANES LINK_WIDTH(0x4)
#define LINK_WIDTH_8_LANES LINK_WIDTH(0x8)
/* Integrated MSI Reception Module Lower Address */
#define PCIE_MSI_ADDR_LO 0x820
/* Integrated MSI Reception Module Upper Address */
#define PCIE_MSI_ADDR_HI 0x824
/* Integrated MSI Reception Module Interrupt0 Enable */
#define PCIE_MSI_INTR0_ENABLE 0x828
/* Integrated MSI Reception Module Interrupt0 Mask */
#define PCIE_MSI_INTR0_MASK 0x82C
/* Integrated MSI Reception Module Interrupt0 Status */
#define PCIE_MSI_INTR0_STATUS 0x830
#define GEN3_RELATED_OFF 0x890
#define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL BIT(0)
#define GEN3_RELATED_OFF_EQ_PHASE_2_3 BIT(9)
#define GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS BIT(13)
#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_CTRL 0x890 /* Gen3 Control Register */
/* Gen3 Receiver Impedance ZRX-DC Not Compliant */
#define GEN3_CTRL_GEN3_ZRXDC_NONCOMPL BIT(0)
#define GEN3_CTRL_EQ_PHASE_2_3 BIT(9) /* Equalization Phase 2 and Phase 3 */
#define GEN3_CTRL_RXEQ_RGRDLESS_RXTS BIT(13)
#define GEN3_CTRL_GEN3_EQ_DISABLE BIT(16) /* Equalization Disable */
#define GEN3_CTRL_RATE_SHADOW_SEL_MASK GENMASK(25, 24) /* Rate Shadow Select mask */
#define GEN3_EQ_CONTROL_OFF 0x8A8
#define GEN3_EQ_CONTROL_OFF_FB_MODE GENMASK(3, 0)
#define GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE BIT(4)
#define GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC GENMASK(23, 8)
#define GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL BIT(24)
#define GEN3_EQ_CTRL 0x8A8 /* Gen3 EQ Control */
#define GEN3_EQ_CTRL_FB_MODE GENMASK(3, 0) /* Feedback Mode */
#define GEN3_EQ_CTRL_PHASE23_EXIT_MODE BIT(4) /* Behavior After 24 ms Timeout */
#define GEN3_EQ_CTRL_PSET_REQ_VEC GENMASK(23, 8) /* Preset Request Vector */
/* Include or not the FOM feedback from the initial preset */
#define GEN3_EQ_CTRL_FOM_INC_INITIAL_EVAL BIT(24)
#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_CURSOR_DELTA GENMASK(13, 10)
#define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA GENMASK(17, 14)
/* Gen3 EQ Direction Change Feedback Mode Control */
#define GEN3_EQ_FB_MODE_DIR_CHANGE 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_CURSOR_DELTA GENMASK(13, 10)
#define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA GENMASK(17, 14)
#define COHERENCY_CONTROL_1_OFF 0x8E0
#define CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK GENMASK(31, 2)
#define CFG_MEMTYPE_VALUE BIT(0)
#define MISC_CONTROL_1 0x8BC /* DBI Read-Only Write Enable */
#define DBI_RO_WR_EN BIT(0) /* Write to RO Registers Using DBI */
#define COHERENCY_CONTROL_2_OFF 0x8E4
#define COHERENCY_CONTROL_3_OFF 0x8E8
#define COHERENCY_CTRL1 0x8E0 /* Cache Coherency Control Register 1 */
/* Boundary Lower Address For Memory Type */
#define CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK GENMASK(31, 2)
#define CFG_MEMTYPE_VALUE BIT(0) /* Sets the memory type */
#define PCIE_PORT_MULTI_LANE_CTRL 0x8C0
#define PORT_MLTI_UPCFG_SUPPORT BIT(7)
#define COHERENCY_CTRL2 0x8E4 /* Cache Coherency Control Register 2 */
#define COHERENCY_CTRL3 0x8E8 /* Cache Coherency Control Register 3 */
#define PCIE_VERSION_NUMBER 0x8F8
#define PCIE_VERSION_TYPE 0x8FC
#define MULTI_LANE_CTRL 0x8C0 /* UpConfigure Multi-lane Control */
#define MULTI_LANE_CTRL_UPCFG_SUPPORT BIT(7) /* Upconfigure Support */
#define PCIE_VERSION_NUMBER 0x8F8 /* PCIe Controller IIP Release Version Number */
#define PCIE_VERSION_TYPE 0x8FC /* PCIe Controller IIP Release Version Type */
/*
* iATU inbound and outbound windows CSRs. Before the IP-core v4.80a each
@ -186,38 +199,36 @@
* v4.80a in a way so the viewport was unrolled into the directly accessible
* iATU/eDMA CSRs space.
*/
#define PCIE_ATU_VIEWPORT 0x900
#define PCIE_ATU_REGION_DIR_IB BIT(31)
#define PCIE_ATU_REGION_DIR_OB 0
#define PCIE_ATU_VIEWPORT_BASE 0x904
#define PCIE_ATU_UNROLL_BASE(dir, index) \
(((index) << 9) | ((dir == PCIE_ATU_REGION_DIR_IB) ? BIT(8) : 0))
#define PCIE_ATU_VIEWPORT_SIZE 0x2C
#define PCIE_ATU_REGION_CTRL1 0x000
#define PCIE_ATU_INCREASE_REGION_SIZE BIT(13)
#define PCIE_ATU_TD BIT(8)
#define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20)
#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
#define PCIE_ATU_UPPER_BASE 0x00C
#define PCIE_ATU_LIMIT 0x010
#define PCIE_ATU_LOWER_TARGET 0x014
#define PCIE_ATU_BUS(x) FIELD_PREP(GENMASK(31, 24), x)
#define PCIE_ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x)
#define PCIE_ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x)
#define PCIE_ATU_UPPER_TARGET 0x018
#define PCIE_ATU_UPPER_LIMIT 0x020
#define IATU_VIEWPORT 0x900 /* iATU Index */
#define ATU_REGION_DIR_IB BIT(31)
#define ATU_REGION_DIR_OB 0
#define ATU_VIEWPORT_BASE 0x904
#define ATU_UNROLL_BASE(dir, index) \
(((index) << 9) | ((dir == ATU_REGION_DIR_IB) ? BIT(8) : 0))
#define ATU_VIEWPORT_SIZE 0x2C
#define ATU_REGION_CTRL1 0x000
#define ATU_INCREASE_REGION_SIZE BIT(13)
#define ATU_TD BIT(8)
#define ATU_FUNC_NUM(pf) ((pf) << 20)
#define ATU_REGION_CTRL2 0x004
#define ATU_ENABLE BIT(31)
#define ATU_BAR_MODE_ENABLE BIT(30)
#define ATU_CFG_SHIFT_MODE_ENABLE BIT(28)
#define ATU_DMA_BYPASS BIT(27)
#define ATU_INHIBIT_PAYLOAD BIT(22)
#define ATU_FUNC_NUM_MATCH_EN BIT(19)
#define ATU_LOWER_BASE 0x008
#define ATU_UPPER_BASE 0x00C
#define ATU_LIMIT 0x010
#define ATU_LOWER_TARGET 0x014
#define ATU_BUS(x) FIELD_PREP(GENMASK(31, 24), x)
#define ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x)
#define ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x)
#define ATU_UPPER_TARGET 0x018
#define ATU_UPPER_LIMIT 0x020
#define PCIE_MISC_CONTROL_1_OFF 0x8BC
#define PCIE_DBI_RO_WR_EN BIT(0)
#define PCIE_MSIX_DOORBELL 0x948
#define PCIE_MSIX_DOORBELL_PF_SHIFT 24
#define MSIX_DOORBELL 0x948
#define MSIX_DOORBELL_PF_MASK GENMASK(28, 24)
/*
* eDMA CSRs. DW PCIe IP-core v4.70a and older had the eDMA registers accessible
@ -258,37 +269,24 @@
#define MARGINING_MAXLANES GENMASK(20, 16)
#define MARGINING_SAMPLE_RATE_TIMING GENMASK(13, 8)
#define MARGINING_SAMPLE_RATE_VOLTAGE GENMASK(5, 0)
/*
* iATU Unroll-specific register definitions
* From 4.80 core version the address translation will be made by unroll
*/
#define PCIE_ATU_UNR_REGION_CTRL1 0x00
#define PCIE_ATU_UNR_REGION_CTRL2 0x04
#define PCIE_ATU_UNR_LOWER_BASE 0x08
#define PCIE_ATU_UNR_UPPER_BASE 0x0C
#define PCIE_ATU_UNR_LOWER_LIMIT 0x10
#define PCIE_ATU_UNR_LOWER_TARGET 0x14
#define PCIE_ATU_UNR_UPPER_TARGET 0x18
#define PCIE_ATU_UNR_UPPER_LIMIT 0x20
/*
* RAS-DES register definitions
*/
#define PCIE_RAS_DES_EVENT_COUNTER_CONTROL 0x8
#define RAS_DES_EVENT_COUNTER_CONTROL 0x8
#define EVENT_COUNTER_ALL_CLEAR 0x3
#define EVENT_COUNTER_ENABLE_ALL 0x7
#define EVENT_COUNTER_ENABLE_SHIFT 2
#define EVENT_COUNTER_EVENT_SEL_MASK GENMASK(7, 0)
#define EVENT_COUNTER_EVENT_SEL_SHIFT 16
#define EVENT_COUNTER_ENABLE_MASK GENMASK(4, 2)
#define EVENT_COUNTER_EVENT_SEL_MASK GENMASK(23, 16)
#define EVENT_COUNTER_GROUP_SEL_MASK GENMASK(27, 24)
#define EVENT_COUNTER_EVENT_Tx_L0S 0x2
#define EVENT_COUNTER_EVENT_Rx_L0S 0x3
#define EVENT_COUNTER_EVENT_L1 0x5
#define EVENT_COUNTER_EVENT_L1_1 0x7
#define EVENT_COUNTER_EVENT_L1_2 0x8
#define EVENT_COUNTER_GROUP_SEL_SHIFT 24
#define EVENT_COUNTER_GROUP_5 0x5
#define PCIE_RAS_DES_EVENT_COUNTER_DATA 0xc
#define RAS_DES_EVENT_COUNTER_DATA 0xc
/* PTM register definitions */
#define PTM_RES_REQ_CTRL 0x8
@ -376,7 +374,7 @@ enum dw_pcie_core_rst {
};
enum dw_pcie_ltssm {
/* Need to align with PCIE_PORT_DEBUG0 bits 0:5 */
/* Need to align with PORT_LINK_DEBUG0 bits 0:5 */
DW_PCIE_LTSSM_DETECT_QUIET = 0x0,
DW_PCIE_LTSSM_DETECT_ACT = 0x1,
DW_PCIE_LTSSM_POLL_ACTIVE = 0x2,
@ -454,7 +452,7 @@ struct dw_pcie_rp {
int irq;
const struct dw_pcie_host_ops *ops;
int msi_irq[MAX_MSI_CTRLS];
struct irq_domain *irq_domain;
struct irq_domain *msi_irq_domain;
dma_addr_t msi_data;
struct irq_chip *msi_irq_chip;
u32 num_vectors;
@ -469,12 +467,16 @@ struct dw_pcie_rp {
struct pci_config_window *cfg;
bool ecam_enabled;
bool native_ecam;
bool bypass_ob_mem_iatu;
bool skip_l23_ready;
bool skip_pwrctrl_off;
/* Set if the RC loses power during suspend */
bool suspend_poweroff;
};
struct dw_pcie_ep_ops {
int (*pre_init)(struct dw_pcie_ep *ep);
void (*post_deinit)(struct dw_pcie_ep *ep);
int (*init)(struct dw_pcie_ep *ep);
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
unsigned int type, u16 interrupt_num);
@ -813,9 +815,9 @@ static inline void dw_pcie_dbi_ro_wr_en(struct dw_pcie *pci)
u32 reg;
u32 val;
reg = PCIE_MISC_CONTROL_1_OFF;
reg = MISC_CONTROL_1;
val = dw_pcie_readl_dbi(pci, reg);
val |= PCIE_DBI_RO_WR_EN;
val |= DBI_RO_WR_EN;
dw_pcie_writel_dbi(pci, reg, val);
}
@ -824,9 +826,9 @@ static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci)
u32 reg;
u32 val;
reg = PCIE_MISC_CONTROL_1_OFF;
reg = MISC_CONTROL_1;
val = dw_pcie_readl_dbi(pci, reg);
val &= ~PCIE_DBI_RO_WR_EN;
val &= ~DBI_RO_WR_EN;
dw_pcie_writel_dbi(pci, reg, val);
}
@ -851,9 +853,9 @@ static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)
if (pci->ops && pci->ops->get_ltssm)
return pci->ops->get_ltssm(pci);
val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0);
val = dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG0);
return (enum dw_pcie_ltssm)FIELD_GET(PORT_LOGIC_LTSSM_STATE_MASK, val);
return (enum dw_pcie_ltssm)FIELD_GET(PORT_LINK_DEBUG0_LTSSM_MASK, val);
}
const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm);
@ -868,7 +870,7 @@ void dw_pcie_free_msi(struct dw_pcie_rp *pp);
int dw_pcie_setup_rc(struct dw_pcie_rp *pp);
int dw_pcie_host_init(struct dw_pcie_rp *pp);
void dw_pcie_host_deinit(struct dw_pcie_rp *pp);
int dw_pcie_allocate_domains(struct dw_pcie_rp *pp);
int dw_pcie_allocate_msi_domains(struct dw_pcie_rp *pp);
void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn,
int where);
#else
@ -909,7 +911,7 @@ static inline void dw_pcie_host_deinit(struct dw_pcie_rp *pp)
{
}
static inline int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)
static inline int dw_pcie_allocate_msi_domains(struct dw_pcie_rp *pp)
{
return 0;
}

View File

@ -21,6 +21,7 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/workqueue.h>
#include <trace/events/pci_controller.h>

View File

@ -388,6 +388,7 @@ static const struct of_device_id eswin_pcie_of_match[] = {
{ .compatible = "eswin,eic7700-pcie", .data = &eswin_eic7700_data },
{}
};
MODULE_DEVICE_TABLE(of, eswin_pcie_of_match);
static struct platform_driver eswin_pcie_driver = {
.probe = eswin_pcie_probe,

View File

@ -215,9 +215,9 @@ static int fu740_pcie_start_link(struct dw_pcie *pci)
tmp |= orig;
dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCAP, tmp);
tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
tmp |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
tmp = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
tmp |= SPEED_CHANGE;
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, tmp);
ret = dw_pcie_wait_for_link(pci);
if (ret) {

View File

@ -18,6 +18,7 @@
#include <linux/pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/resource.h>
#include <linux/reset.h>

View File

@ -107,7 +107,7 @@ static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci)
u32 ddr_base_high = upper_32_bits(S32G_MEMORY_BOUNDARY_ADDR);
dw_pcie_dbi_ro_wr_en(pci);
dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, 0x0);
dw_pcie_writel_dbi(pci, COHERENCY_CTRL3, 0x0);
/*
* Ncore is a cache-coherent interconnect module that enables the
@ -125,9 +125,9 @@ static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci)
* Define the start of DDR as seen by Linux as this boundary between
* "memory" and "peripherals", with peripherals being below.
*/
dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_1_OFF,
dw_pcie_writel_dbi(pci, COHERENCY_CTRL1,
(ddr_base_low & CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK));
dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_2_OFF, ddr_base_high);
dw_pcie_writel_dbi(pci, COHERENCY_CTRL2, ddr_base_high);
dw_pcie_dbi_ro_wr_dis(pci);
}
@ -154,13 +154,13 @@ static int s32g_init_pcie_controller(struct dw_pcie_rp *pp)
dw_pcie_dbi_ro_wr_en(pci);
val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE);
val |= PORT_FORCE_DO_DESKEW_FOR_SRIS;
dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val);
val = dw_pcie_readl_dbi(pci, PORT_FORCE_LINK);
val |= PORT_FORCE_LINK_DDFS;
dw_pcie_writel_dbi(pci, PORT_FORCE_LINK, val);
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
val |= GEN3_RELATED_OFF_EQ_PHASE_2_3;
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
val |= GEN3_CTRL_EQ_PHASE_2_3;
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
dw_pcie_dbi_ro_wr_dis(pci);

View File

@ -15,9 +15,9 @@ void qcom_pcie_common_set_equalization(struct dw_pcie *pci)
u16 speed;
/*
* GEN3_RELATED_OFF register is repurposed to apply equalization
* GEN3_CTRL register is repurposed to apply equalization
* settings at various data transmission rates through registers namely
* GEN3_EQ_*. The RATE_SHADOW_SEL bit field of GEN3_RELATED_OFF
* GEN3_EQ_*. The RATE_SHADOW_SEL bit field of GEN3_CTRL
* determines the data rate for which these equalization settings are
* applied.
*/
@ -28,25 +28,25 @@ void qcom_pcie_common_set_equalization(struct dw_pcie *pci)
break;
}
reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
FIELD_MODIFY(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, &reg,
reg = dw_pcie_readl_dbi(pci, GEN3_CTRL);
reg &= ~GEN3_CTRL_GEN3_ZRXDC_NONCOMPL;
FIELD_MODIFY(GEN3_CTRL_RATE_SHADOW_SEL_MASK, &reg,
speed - PCIE_SPEED_8_0GT);
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg);
dw_pcie_writel_dbi(pci, GEN3_CTRL, reg);
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF);
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE);
FIELD_MODIFY(GEN3_EQ_FMDC_T_MIN_PHASE23, &reg, 0x1);
FIELD_MODIFY(GEN3_EQ_FMDC_N_EVALS, &reg, 0xd);
FIELD_MODIFY(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, &reg, 0x5);
FIELD_MODIFY(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, &reg, 0x5);
dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg);
dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE, 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);
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CTRL);
reg &= ~(GEN3_EQ_CTRL_FB_MODE |
GEN3_EQ_CTRL_PHASE23_EXIT_MODE |
GEN3_EQ_CTRL_FOM_INC_INITIAL_EVAL |
GEN3_EQ_CTRL_PSET_REQ_VEC);
dw_pcie_writel_dbi(pci, GEN3_EQ_CTRL, reg);
}
}
EXPORT_SYMBOL_GPL(qcom_pcie_common_set_equalization);

View File

@ -19,6 +19,7 @@
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/module.h>
@ -944,6 +945,8 @@ static void qcom_pcie_ep_remove(struct platform_device *pdev)
if (pcie_ep->link_status == QCOM_PCIE_EP_LINK_DISABLED)
return;
pci_epc_deinit_notify(pcie_ep->pci.ep.epc);
dw_pcie_ep_deinit(&pcie_ep->pci.ep);
qcom_pcie_disable_resources(pcie_ep);
}

View File

@ -20,6 +20,7 @@
#include <linux/kernel.h>
#include <linux/limits.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
@ -73,6 +74,23 @@
#define PARF_BDF_TO_SID_TABLE_N 0x2000
#define PARF_BDF_TO_SID_CFG 0x2c00
/*
* Outbound blocker range registers. The blocked range has a write pair
* (WR_BASE/WR_LIMIT) and a read pair (RD_BASE/RD_LIMIT); each address is
* split into a low (32-bit) and a HI (upper 32-bit) register.
*/
#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
/* ELBI registers */
#define ELBI_SYS_CTRL 0x04
#define ELBI_SYS_STTS 0x08
@ -90,6 +108,7 @@
/* PARF_SYS_CTRL register fields */
#define MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN BIT(29)
#define OUTBOUND_BLOCKER_EN BIT(26)
#define MST_WAKEUP_EN BIT(13)
#define SLV_WAKEUP_EN BIT(12)
#define MSTR_ACLK_CGC_DIS BIT(10)
@ -308,6 +327,7 @@ struct qcom_pcie {
struct gpio_desc *reset;
int global_irq;
bool use_pm_opp;
struct mutex reset_lock;
};
#define to_qcom_pcie(x) dev_get_drvdata((x)->dev)
@ -445,6 +465,25 @@ static void qcom_pcie_configure_dbi_atu_base(struct qcom_pcie *pcie)
}
}
static void qcom_pcie_init_outbound_blocker(struct qcom_pcie *pcie)
{
struct dw_pcie *pci = pcie->pci;
/* ECAM base must match the DBI base address */
writel(lower_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE);
writel(upper_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE_HI);
writel(0, pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE);
writel(0, pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE_HI);
writel(U32_MAX, pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT);
writel(U32_MAX, pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT_HI);
writel(0, pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE);
writel(0, pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE_HI);
writel(U32_MAX, pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT);
writel(U32_MAX, pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT_HI);
}
static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie)
{
struct dw_pcie *pci = pcie->pci;
@ -990,6 +1029,8 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie)
dw_pcie_dbi_ro_wr_dis(pci);
qcom_pcie_init_outbound_blocker(pcie);
return 0;
}
@ -1104,6 +1145,8 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie)
qcom_pcie_set_slot_cap(pcie->pci);
qcom_pcie_init_outbound_blocker(pcie);
return 0;
}
@ -1143,50 +1186,90 @@ static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie)
static int qcom_pcie_config_sid_1_9_0(struct qcom_pcie *pcie)
{
/* iommu map structure */
struct {
u32 bdf;
u32 phandle;
u32 smmu_sid;
u32 smmu_sid_len;
} *map;
void __iomem *bdf_to_sid_base = pcie->parf + PARF_BDF_TO_SID_TABLE_N;
struct device *dev = pcie->pci->dev;
struct device_node *iommu_np;
u8 qcom_pcie_crc8_table[CRC8_TABLE_SIZE];
int i, nr_map, size = 0;
u32 smmu_sid_base;
const __be32 *map;
u32 iommu_cells, entry_cells, phandle, smmu_sid_base;
int i, nr_cells, nr_map, size = 0;
u32 val;
of_get_property(dev->of_node, "iommu-map", &size);
if (!size)
map = of_get_property(dev->of_node, "iommu-map", &size);
if (!map || !size)
return 0;
if (size % sizeof(*map)) {
dev_err(dev, "Malformed iommu-map property\n");
return -EINVAL;
}
nr_cells = size / sizeof(*map);
/*
* Each iommu-map entry is: rid-base (1 cell), phandle (1 cell),
* IOMMU specifier (#iommu-cells cells), length (1 cell). Read
* #iommu-cells from the IOMMU provider referenced by the first
* entry to compute the per-entry stride.
*/
phandle = be32_to_cpu(map[1]);
iommu_np = of_find_node_by_phandle(phandle);
if (!iommu_np) {
dev_err(dev, "Failed to find IOMMU node in iommu-map\n");
return -ENODEV;
}
if (of_property_read_u32(iommu_np, "#iommu-cells", &iommu_cells))
iommu_cells = 1;
of_node_put(iommu_np);
entry_cells = 3 + iommu_cells;
/*
* Retain backward compatibility with DTs that describe iommu-map
* with 4-cell entries against an IOMMU declaring #iommu-cells = 2,
* matching the fallback in drivers/of/base.c::of_check_bad_map().
*/
if (iommu_cells == 2 && !(nr_cells % 4)) {
bool legacy = true;
for (i = 0; i < nr_cells; i += 4) {
if (be32_to_cpu(map[i + 1]) != phandle ||
be32_to_cpu(map[i + 3]) != 1) {
legacy = false;
break;
}
}
if (legacy) {
dev_warn_once(dev, "iommu-map has 1-cell entries with #iommu-cells=2, using 1-cell\n");
entry_cells = 4;
}
}
if (nr_cells % entry_cells) {
dev_err(dev, "Malformed iommu-map property\n");
return -EINVAL;
}
nr_map = nr_cells / entry_cells;
/* Enable BDF to SID translation by disabling bypass mode (default) */
val = readl(pcie->parf + PARF_BDF_TO_SID_CFG);
val &= ~BDF_TO_SID_BYPASS;
writel(val, pcie->parf + PARF_BDF_TO_SID_CFG);
map = kzalloc(size, GFP_KERNEL);
if (!map)
return -ENOMEM;
of_property_read_u32_array(dev->of_node, "iommu-map", (u32 *)map,
size / sizeof(u32));
nr_map = size / (sizeof(*map));
crc8_populate_msb(qcom_pcie_crc8_table, QCOM_PCIE_CRC8_POLYNOMIAL);
/* 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;
smmu_sid_base = be32_to_cpu(map[2]);
/* 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;
u32 bdf = be32_to_cpu(map[i * entry_cells]);
u32 sid = be32_to_cpu(map[i * entry_cells + 2]);
__be16 bdf_be = cpu_to_be16(bdf);
u8 hash;
hash = crc8(qcom_pcie_crc8_table, (u8 *)&bdf_be, sizeof(bdf_be), 0);
@ -1208,12 +1291,10 @@ static int qcom_pcie_config_sid_1_9_0(struct qcom_pcie *pcie)
}
/* BDF [31:16] | SID [15:8] | NEXT [7:0] */
val = map[i].bdf << 16 | (map[i].smmu_sid - smmu_sid_base) << 8 | 0;
val = bdf << 16 | (sid - smmu_sid_base) << 8 | 0;
writel(val, bdf_to_sid_base + hash * sizeof(u32));
}
kfree(map);
return 0;
}
@ -1295,9 +1376,9 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
writel(DEVICE_TYPE_RC, pcie->parf + PARF_DEVICE_TYPE);
writel(BYPASS | MSTR_AXI_CLK_EN | AHB_CLK_EN,
pcie->parf + PARF_MHI_CLOCK_RESET_CTRL);
writel(GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS |
GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL,
pci->dbi_base + GEN3_RELATED_OFF);
writel(GEN3_CTRL_RXEQ_RGRDLESS_RXTS |
GEN3_CTRL_GEN3_ZRXDC_NONCOMPL,
pci->dbi_base + GEN3_CTRL);
writel(MST_WAKEUP_EN | SLV_WAKEUP_EN | MSTR_ACLK_CGC_DIS |
SLV_ACLK_CGC_DIS | CORE_CLK_CGC_DIS |
@ -1322,6 +1403,8 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
for (i = 0; i < 256; i++)
writel(0, pcie->parf + PARF_BDF_TO_SID_TABLE_N + (4 * i));
qcom_pcie_init_outbound_blocker(pcie);
return 0;
}
@ -1382,6 +1465,18 @@ static void qcom_pcie_configure_ports(struct qcom_pcie *pcie)
dw_pcie_program_t_power_on(pcie->pci, port->l1ss_t_power_on);
}
static void qcom_pcie_enable_outbound_blocker(struct qcom_pcie *pcie)
{
u32 sys_ctrl;
sys_ctrl = readl(pcie->parf + PARF_SYS_CTRL);
sys_ctrl |= OUTBOUND_BLOCKER_EN;
writel(sys_ctrl, pcie->parf + PARF_SYS_CTRL);
/* Flush the write so the blocker is enabled before this function returns */
readl(pcie->parf + PARF_SYS_CTRL);
}
static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@ -1775,6 +1870,8 @@ static int qcom_pcie_reset_root_port(struct pci_host_bridge *bridge,
u32 val;
int ret;
guard(mutex)(&pcie->reset_lock);
/* Wait for the pending transactions to be completed */
ret = readl_relaxed_poll_timeout(pcie->parf + PARF_STATUS, val,
val & FLUSH_COMPLETED, 10,
@ -1821,6 +1918,9 @@ static int qcom_pcie_reset_root_port(struct pci_host_bridge *bridge,
qcom_pcie_start_link(pci);
ret = dw_pcie_wait_for_link(pci);
/* This linkdown might have been a hot-unplug */
if (ret == -ENODEV)
ret = 0;
if (ret)
return ret;
@ -1878,6 +1978,11 @@ static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data)
if (test_and_clear_bit(INT_ALL_LINK_DOWN, &status)) {
dev_dbg(dev, "Received Link down event\n");
mutex_lock(&pcie->reset_lock);
qcom_pcie_enable_outbound_blocker(pcie);
mutex_unlock(&pcie->reset_lock);
for_each_pci_bridge(port, pp->bridge->bus) {
if (pci_pcie_type(port) == PCI_EXP_TYPE_ROOT_PORT)
pci_host_handle_link_down(port);
@ -1917,11 +2022,13 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg)
*/
raw_spin_lock_init(&pp->lock);
ret = dw_pcie_msi_host_init(pp);
if (ret)
return ret;
pp->use_imsi_rx = !pci_host_of_has_msi_map(dev);
if (pp->use_imsi_rx) {
ret = dw_pcie_msi_host_init(pp);
if (ret)
return ret;
}
pp->use_imsi_rx = true;
dw_pcie_msi_init(pp);
return devm_add_action_or_reset(dev, qcom_pci_free_msi, pp);
@ -1953,7 +2060,7 @@ static bool qcom_pcie_is_child_node(struct device *dev,
return false;
}
/* Parse PERST# from all nodes in depth first manner starting from @np */
/* Collect PERST# GPIOs from PCI bridge nodes depth-first, starting at @np */
static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
struct qcom_pcie_port *port,
struct device_node *np)
@ -2019,6 +2126,9 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
parse_child_node:
for_each_available_child_of_node_scoped(np, child) {
if (!of_node_is_type(child, "pci"))
continue;
ret = qcom_pcie_parse_perst(pcie, port, child);
if (ret)
return ret;
@ -2204,6 +2314,10 @@ static int qcom_pcie_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&pcie->ports);
ret = devm_mutex_init(dev, &pcie->reset_lock);
if (ret)
goto err_pm_runtime_put;
pci->dev = dev;
pci->ops = &dw_pcie_ops;
pp = &pci->pp;
@ -2341,6 +2455,10 @@ static int qcom_pcie_suspend_noirq(struct device *dev)
{
struct qcom_pcie *pcie;
int ret = 0;
const struct qcom_pcie_cfg *pcie_cfg = of_device_get_match_data(dev);
if (pcie_cfg && pcie_cfg->firmware_managed)
return 0;
pcie = dev_get_drvdata(dev);
if (!pcie)
@ -2399,6 +2517,10 @@ static int qcom_pcie_resume_noirq(struct device *dev)
{
struct qcom_pcie *pcie;
int ret;
const struct qcom_pcie_cfg *pcie_cfg = of_device_get_match_data(dev);
if (pcie_cfg && pcie_cfg->firmware_managed)
return 0;
pcie = dev_get_drvdata(dev);
if (!pcie)

View File

@ -19,6 +19,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
@ -36,6 +37,7 @@
/* MSI Capability */
#define MSICAP0 0x0050
#define MSICAP0_MMESCAP_MASK GENMASK(19, 17)
#define MSICAP0_MSIE BIT(16)
/* PCIe Interrupt Status 0 */
@ -74,6 +76,11 @@
#define PCIEPWRMNGCTRL 0x0070
#define APP_CLK_REQ_N BIT(11)
#define APP_CLK_PM_EN BIT(10)
#define APP_READY_ENTR_L23 BIT(6)
#define APP_REQ_ENTR_L1 BIT(5)
/* PCI Express capability */
#define EXPCAP(x) (0x0070 + (x))
#define RCAR_NUM_SPEED_CHANGE_RETRIES 10
#define RCAR_MAX_LINK_SPEED 4
@ -87,8 +94,10 @@ MODULE_FIRMWARE(RCAR_GEN4_PCIE_FIRMWARE_NAME);
struct rcar_gen4_pcie;
struct rcar_gen4_pcie_drvdata {
void (*additional_common_init)(struct rcar_gen4_pcie *rcar);
int (*init)(struct rcar_gen4_pcie *rcar);
void (*deinit)(struct rcar_gen4_pcie *rcar);
int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable);
int (*speed_control)(struct rcar_gen4_pcie *rcar);
enum dw_pcie_device_mode mode;
};
@ -96,7 +105,9 @@ struct rcar_gen4_pcie {
struct dw_pcie dw;
void __iomem *base;
void __iomem *phy_base;
struct phy *phy;
struct platform_device *pdev;
struct reset_control *perst;
const struct rcar_gen4_pcie_drvdata *drvdata;
};
#define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw)
@ -122,17 +133,17 @@ static int rcar_gen4_pcie_speed_change(struct dw_pcie *dw)
u32 val;
int i;
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
val = dw_pcie_readl_dbi(dw, LINK_WIDTH_SPEED_CTRL);
val &= ~SPEED_CHANGE;
dw_pcie_writel_dbi(dw, LINK_WIDTH_SPEED_CTRL, val);
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
val = dw_pcie_readl_dbi(dw, LINK_WIDTH_SPEED_CTRL);
val |= SPEED_CHANGE;
dw_pcie_writel_dbi(dw, LINK_WIDTH_SPEED_CTRL, val);
for (i = 0; i < RCAR_NUM_SPEED_CHANGE_RETRIES; i++) {
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
if (!(val & PORT_LOGIC_SPEED_CHANGE))
val = dw_pcie_readl_dbi(dw, LINK_WIDTH_SPEED_CTRL);
if (!(val & SPEED_CHANGE))
return 0;
usleep_range(10000, 11000);
}
@ -140,20 +151,10 @@ static int rcar_gen4_pcie_speed_change(struct dw_pcie *dw)
return -ETIMEDOUT;
}
/*
* Enable LTSSM of this controller and manually initiate the speed change.
* Always return 0.
*/
static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
static int rcar_gen4_pcie_speed_control(struct rcar_gen4_pcie *rcar)
{
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int i, changes, ret;
if (rcar->drvdata->ltssm_control) {
ret = rcar->drvdata->ltssm_control(rcar, true);
if (ret)
return ret;
}
struct dw_pcie *dw = &rcar->dw;
int i, changes;
/*
* Require direct speed change with retrying here if the max_link_speed
@ -177,6 +178,45 @@ static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
return 0;
}
static int rcar_gen5_pcie_speed_control(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
u32 lnkcap = dw_pcie_readl_dbi(dw, EXPCAP(PCI_EXP_LNKCAP));
u32 lnksta = dw_pcie_readw_dbi(dw, EXPCAP(PCI_EXP_LNKSTA));
u32 val;
if ((lnksta & PCI_EXP_LNKSTA_CLS) == (lnkcap & PCI_EXP_LNKCAP_SLS))
return 0;
/* Retrain link */
val = dw_pcie_readw_dbi(dw, EXPCAP(PCI_EXP_LNKCTL));
val |= PCI_EXP_LNKCTL_RL;
dw_pcie_writew_dbi(dw, EXPCAP(PCI_EXP_LNKCTL), val);
/* Wait for link retrain, 500ms must be enough for all link rates. */
return read_poll_timeout(dw_pcie_readw_dbi, lnksta, !(lnksta & PCI_EXP_LNKSTA_LT),
1000, 5 * PCIE_RESET_CONFIG_WAIT_MS * USEC_PER_MSEC,
false, dw, EXPCAP(PCI_EXP_LNKSTA));
}
/*
* Enable LTSSM of this controller and manually initiate the speed change.
* Always return 0.
*/
static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
{
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
if (rcar->drvdata->ltssm_control) {
ret = rcar->drvdata->ltssm_control(rcar, true);
if (ret)
return ret;
}
return rcar->drvdata->speed_control(rcar);
}
static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw)
{
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
@ -197,6 +237,8 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
return ret;
}
reset_control_deassert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) {
reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
/*
@ -241,8 +283,74 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
fsleep(1000);
if (rcar->drvdata->additional_common_init)
rcar->drvdata->additional_common_init(rcar);
return 0;
err_unprepare:
reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
return ret;
}
static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
u32 val;
int ret;
/* R-Car Gen4 common initialization. */
ret = rcar_gen4_pcie_common_init(rcar);
if (ret)
return ret;
/* R-Car V4H and V4M specific additional initialization. */
val = dw_pcie_readl_dbi(dw, PORT_LANE_SKEW);
val &= ~PORT_LANE_SKEW_INSERT_MASK;
if (dw->num_lanes < 4)
val |= BIT(6);
dw_pcie_writel_dbi(dw, PORT_LANE_SKEW, val);
val = readl(rcar->base + PCIEPWRMNGCTRL);
val |= APP_CLK_REQ_N | APP_CLK_PM_EN;
writel(val, rcar->base + PCIEPWRMNGCTRL);
return 0;
}
static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
{
struct dw_pcie *dw = &rcar->dw;
int ret;
u32 val;
/* R-Car Gen4 and Gen5 common initialization. */
ret = rcar_gen4_pcie_common_init(rcar);
if (ret)
return ret;
/* R-Car Gen5 specific additional initialization. */
ret = phy_init(rcar->phy);
if (ret)
goto err_unprepare;
dw_pcie_dbi_ro_wr_en(dw);
val = dw_pcie_readl_dbi(dw, PORT_LANE_SKEW);
val &= ~PORT_LANE_SKEW_INSERT_MASK;
if (dw->num_lanes < 8)
val |= BIT(6);
dw_pcie_writel_dbi(dw, PORT_LANE_SKEW, val);
val = dw_pcie_readl_dbi(dw, MSICAP0);
FIELD_MODIFY(MSICAP0_MMESCAP_MASK, &val, 4);
dw_pcie_writel_dbi(dw, MSICAP0, val);
dw_pcie_dbi_ro_wr_dis(dw);
val = readl(rcar->base + PCIEPWRMNGCTRL);
val |= APP_CLK_REQ_N | APP_CLK_PM_EN |
APP_READY_ENTR_L23 | APP_REQ_ENTR_L1;
writel(val, rcar->base + PCIEPWRMNGCTRL);
return 0;
@ -257,9 +365,16 @@ static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
struct dw_pcie *dw = &rcar->dw;
reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
}
static void rcar_gen5_pcie_deinit(struct rcar_gen4_pcie *rcar)
{
phy_exit(rcar->phy);
rcar_gen4_pcie_common_deinit(rcar);
}
static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar)
{
struct device *dev = rcar->dw.dev;
@ -285,12 +400,29 @@ static void rcar_gen4_pcie_unprepare(struct rcar_gen4_pcie *rcar)
static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar)
{
struct device *dev = rcar->dw.dev;
struct device_node *root_port;
rcar->phy_base = devm_platform_ioremap_resource_byname(rcar->pdev, "phy");
if (IS_ERR(rcar->phy_base))
return PTR_ERR(rcar->phy_base);
if (IS_ERR(rcar->phy_base)) {
rcar->phy_base = NULL;
rcar->phy = devm_phy_get(dev, NULL);
if (IS_ERR(rcar->phy))
return PTR_ERR(rcar->phy);
}
root_port = of_get_next_available_child(dev->of_node, NULL);
if (root_port) {
rcar->perst = of_reset_control_get_optional_exclusive(root_port, "perst");
of_node_put(root_port);
if (IS_ERR(rcar->perst))
return dev_err_probe(dev, PTR_ERR(rcar->perst), "Failed to get PERST#\n");
}
/* Renesas-specific registers */
rcar->base = devm_platform_ioremap_resource_byname(rcar->pdev, "app");
if (IS_ERR(rcar->base))
reset_control_put(rcar->perst);
return PTR_ERR_OR_ZERO(rcar->base);
}
@ -411,6 +543,72 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
return ret;
}
static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
struct pci_dev *dev)
{
/*
* R-Car Gen4 PCIe controller has a hardware limitation of 256 Bytes
* Max_Payload_Size (MPS). PCIe specification indicates that the MPS
* must not exceed minimum MPS of any element along the packet path.
* The controller reports Max_Payload_Size_Supported (MPSS) 256 Bytes
* for header type 0 and 128 Bytes for header type 1. The PCIe core
* will not allow MPS to be set higher than MPSS; warn here in case
* something went very wrong in the core.
*
* For details, refer to chapter "104.1.1 Features" in either of:
* R-Car S4 R19UH0161EJ0140 Rev.1.40 Jul. 31, 2026 or
* R-Car V4H R19UH0186EJ0140 Rev.1.40 Aug. 7, 2026 or
* R-Car V4M R19UH0217EJ0110 Rev.1.10 Jun. 30, 2026.
*/
WARN_ON(pcie_get_mps(dev) > 256);
/*
* R-Car Gen4 Reference Manual, chapter 104.4.8 Usage notes for
* MRRS (Max Read Request Size) states:
*
* Please set "Max Read Request Size" to 128 bytes or 256 bytes.
* If "Max Read Request Size" is set to anything other than the
* above, the transferred data will not match the expected value.
*
* This limitation also seems to apply to devices issuing MRd TLPs.
* This limitation can be triggered by using non-HMB NVMe SSD with
* Max_Read_Request_Size 512 Bytes, for example Crucial P5 Plus.
* Any write into the SSD (MRd TLP issued by the SSD) longer than
* 256 Bytes wraps around at 256 Byte boundary, and the same data
* are written into the SSD starting at offset 0 and at 256 Bytes.
*
* Limit Max_Read_Request_Size to at most 256 Bytes for each
* device connected to this PCIe controller to avoid this behavior.
*
* For details, refer to aforementioned chapter in either of:
* R-Car S4 R19UH0161EJ0140 Rev.1.40 Jul. 31, 2026 or
* R-Car V4H R19UH0186EJ0140 Rev.1.40 Aug. 7, 2026 or
* R-Car V4M R19UH0217EJ0110 Rev.1.10 Jun. 30, 2026.
*/
bridge->no_inc_mrrs = 1;
if (pcie_get_readrq(dev) > 256) {
pci_info(dev, "Limiting MRRS to 256 bytes\n");
pcie_set_readrq(dev, 256);
}
return 0;
}
static void rcar_gen4_pcie_host_perst_assert(struct dw_pcie_rp *pp, bool assert)
{
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
if (dw->pe_rst) {
gpiod_set_value_cansleep(dw->pe_rst, assert);
} else {
if (assert)
reset_control_assert(rcar->perst);
else
reset_control_deassert(rcar->perst);
}
}
/* Host mode */
static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
{
@ -418,9 +616,12 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
gpiod_set_value_cansleep(dw->pe_rst, 1);
if (pp->bridge)
pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
ret = rcar_gen4_pcie_common_init(rcar);
rcar_gen4_pcie_host_perst_assert(pp, true);
ret = rcar->drvdata->init(rcar);
if (ret)
return ret;
@ -439,12 +640,12 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
msleep(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */
gpiod_set_value_cansleep(dw->pe_rst, 0);
rcar_gen4_pcie_host_perst_assert(pp, false);
return 0;
err:
rcar_gen4_pcie_common_deinit(rcar);
rcar->drvdata->deinit(rcar);
return ret;
}
@ -453,8 +654,8 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
gpiod_set_value_cansleep(dw->pe_rst, 1);
rcar_gen4_pcie_common_deinit(rcar);
rcar_gen4_pcie_host_perst_assert(pp, true);
rcar->drvdata->deinit(rcar);
}
static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = {
@ -470,6 +671,8 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
return -ENODEV;
pp->num_vectors = MAX_MSI_IRQS;
/* Reserve an iATU window for the generic PME_Turn_Off implementation. */
pp->use_atu_msg = true;
pp->ops = &rcar_gen4_pcie_host_ops;
return dw_pcie_host_init(pp);
@ -487,7 +690,9 @@ static int rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep)
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
int ret;
ret = rcar_gen4_pcie_common_init(rcar);
writel(0, rcar->base + PCIEDMAINTSTSEN);
ret = rcar->drvdata->init(rcar);
if (ret)
return ret;
@ -496,10 +701,13 @@ static int rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep)
return 0;
}
static void rcar_gen4_pcie_ep_deinit(struct rcar_gen4_pcie *rcar)
static void rcar_gen4_pcie_ep_post_deinit(struct dw_pcie_ep *ep)
{
struct dw_pcie *dw = to_dw_pcie_from_ep(ep);
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
writel(0, rcar->base + PCIEDMAINTSTSEN);
rcar_gen4_pcie_common_deinit(rcar);
rcar->drvdata->deinit(rcar);
}
static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
@ -552,6 +760,7 @@ static unsigned int rcar_gen4_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep,
static const struct dw_pcie_ep_ops pcie_ep_ops = {
.pre_init = rcar_gen4_pcie_ep_pre_init,
.post_deinit = rcar_gen4_pcie_ep_post_deinit,
.raise_irq = rcar_gen4_pcie_ep_raise_irq,
.get_features = rcar_gen4_pcie_ep_get_features,
.get_dbi_offset = rcar_gen4_pcie_ep_get_dbi_offset,
@ -570,16 +779,13 @@ static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
ep->ops = &pcie_ep_ops;
ret = dw_pcie_ep_init(ep);
if (ret) {
rcar_gen4_pcie_ep_deinit(rcar);
if (ret)
return ret;
}
ret = dw_pcie_ep_init_registers(ep);
if (ret) {
dev_err(dev, "Failed to initialize DWC endpoint registers\n");
dw_pcie_ep_deinit(ep);
rcar_gen4_pcie_ep_deinit(rcar);
}
pci_epc_init_notify(ep->epc);
@ -590,7 +796,6 @@ static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
static void rcar_gen4_remove_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
{
dw_pcie_ep_deinit(&rcar->dw.ep);
rcar_gen4_pcie_ep_deinit(rcar);
}
/* Common */
@ -625,7 +830,7 @@ static int rcar_gen4_pcie_probe(struct platform_device *pdev)
err = rcar_gen4_pcie_prepare(rcar);
if (err)
return err;
goto err_prepare;
err = rcar_gen4_add_dw_pcie(rcar);
if (err)
@ -636,6 +841,9 @@ static int rcar_gen4_pcie_probe(struct platform_device *pdev)
err_unprepare:
rcar_gen4_pcie_unprepare(rcar);
err_prepare:
reset_control_put(rcar->perst);
return err;
}
@ -659,6 +867,7 @@ static void rcar_gen4_pcie_remove(struct platform_device *pdev)
rcar_gen4_remove_dw_pcie(rcar);
rcar_gen4_pcie_unprepare(rcar);
reset_control_put(rcar->perst);
}
static int r8a779f0_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable)
@ -683,20 +892,26 @@ static int r8a779f0_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable)
return 0;
}
static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar)
static int rcar_gen5_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable)
{
struct dw_pcie *dw = &rcar->dw;
u32 val;
val = dw_pcie_readl_dbi(dw, PCIE_PORT_LANE_SKEW);
val &= ~PORT_LANE_SKEW_INSERT_MASK;
if (dw->num_lanes < 4)
val |= BIT(6);
dw_pcie_writel_dbi(dw, PCIE_PORT_LANE_SKEW, val);
val = readl(rcar->base + PCIERSTCTRL1);
if (enable) {
val |= APP_LTSSM_ENABLE;
val &= ~APP_HOLD_PHY_RST;
} else {
val &= ~APP_LTSSM_ENABLE;
val |= APP_HOLD_PHY_RST;
}
writel(val, rcar->base + PCIERSTCTRL1);
val = readl(rcar->base + PCIEPWRMNGCTRL);
val |= APP_CLK_REQ_N | APP_CLK_PM_EN;
writel(val, rcar->base + PCIEPWRMNGCTRL);
if (enable)
phy_power_on(rcar->phy);
else
phy_power_off(rcar->phy);
return 0;
}
static void rcar_gen4_pcie_phy_reg_update_bits(struct rcar_gen4_pcie *rcar,
@ -803,9 +1018,9 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable
return 0;
}
val = dw_pcie_readl_dbi(dw, PCIE_PORT_FORCE);
val |= PORT_FORCE_DO_DESKEW_FOR_SRIS;
dw_pcie_writel_dbi(dw, PCIE_PORT_FORCE, val);
val = dw_pcie_readl_dbi(dw, PORT_FORCE_LINK);
val |= PORT_FORCE_LINK_DDFS;
dw_pcie_writel_dbi(dw, PORT_FORCE_LINK, val);
val = readl(rcar->base + PCIEMSR0);
val |= APP_SRIS_MODE;
@ -849,28 +1064,66 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable
return 0;
}
static int rcar_gen4_pcie_suspend_noirq(struct device *dev)
{
struct rcar_gen4_pcie *rcar = dev_get_drvdata(dev);
if (rcar->drvdata->mode != DW_PCIE_RC_TYPE)
return 0;
return dw_pcie_suspend_noirq(&rcar->dw);
}
static int rcar_gen4_pcie_resume_noirq(struct device *dev)
{
struct rcar_gen4_pcie *rcar = dev_get_drvdata(dev);
if (rcar->drvdata->mode != DW_PCIE_RC_TYPE)
return 0;
return dw_pcie_resume_noirq(&rcar->dw);
}
static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = {
.init = rcar_gen4_pcie_common_init,
.deinit = rcar_gen4_pcie_common_deinit,
.ltssm_control = r8a779f0_pcie_ltssm_control,
.speed_control = rcar_gen4_pcie_speed_control,
.mode = DW_PCIE_RC_TYPE,
};
static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = {
.init = rcar_gen4_pcie_common_init,
.deinit = rcar_gen4_pcie_common_deinit,
.ltssm_control = r8a779f0_pcie_ltssm_control,
.speed_control = rcar_gen4_pcie_speed_control,
.mode = DW_PCIE_EP_TYPE,
};
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = {
.additional_common_init = rcar_gen4_pcie_additional_common_init,
.init = rcar_gen4_v4h_v4m_pcie_init,
.deinit = rcar_gen4_pcie_common_deinit,
.ltssm_control = rcar_gen4_pcie_ltssm_control,
.speed_control = rcar_gen4_pcie_speed_control,
.mode = DW_PCIE_RC_TYPE,
};
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = {
.additional_common_init = rcar_gen4_pcie_additional_common_init,
.init = rcar_gen4_v4h_v4m_pcie_init,
.deinit = rcar_gen4_pcie_common_deinit,
.ltssm_control = rcar_gen4_pcie_ltssm_control,
.speed_control = rcar_gen4_pcie_speed_control,
.mode = DW_PCIE_EP_TYPE,
};
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen5_pcie = {
.init = rcar_gen5_pcie_init,
.deinit = rcar_gen5_pcie_deinit,
.ltssm_control = rcar_gen5_pcie_ltssm_control,
.speed_control = rcar_gen5_pcie_speed_control,
.mode = DW_PCIE_RC_TYPE,
};
static const struct of_device_id rcar_gen4_pcie_of_match[] = {
{
.compatible = "renesas,r8a779f0-pcie",
@ -888,14 +1141,23 @@ static const struct of_device_id rcar_gen4_pcie_of_match[] = {
.compatible = "renesas,rcar-gen4-pcie-ep",
.data = &drvdata_rcar_gen4_pcie_ep,
},
{
.compatible = "renesas,rcar-gen5-pcie4",
.data = &drvdata_rcar_gen5_pcie,
},
{},
};
MODULE_DEVICE_TABLE(of, rcar_gen4_pcie_of_match);
static DEFINE_NOIRQ_DEV_PM_OPS(rcar_gen4_pcie_pm_ops,
rcar_gen4_pcie_suspend_noirq,
rcar_gen4_pcie_resume_noirq);
static struct platform_driver rcar_gen4_pcie_driver = {
.driver = {
.name = "pcie-rcar-gen4",
.of_match_table = rcar_gen4_pcie_of_match,
.pm = pm_sleep_ptr(&rcar_gen4_pcie_pm_ops),
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = rcar_gen4_pcie_probe,

View File

@ -12,9 +12,12 @@
#include <linux/err.h>
#include <linux/gfp.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/types.h>

View File

@ -39,8 +39,8 @@ static int tegra194_acpi_init(struct pci_config_window *cfg)
static void atu_reg_write(struct tegra194_pcie_ecam *pcie_ecam, int index,
u32 val, u32 reg)
{
u32 offset = PCIE_ATU_UNROLL_BASE(PCIE_ATU_REGION_DIR_OB, index) +
PCIE_ATU_VIEWPORT_BASE;
u32 offset = ATU_UNROLL_BASE(ATU_REGION_DIR_OB, index) +
ATU_VIEWPORT_BASE;
writel(val, pcie_ecam->iatu_base + offset + reg);
}
@ -50,17 +50,17 @@ static void program_outbound_atu(struct tegra194_pcie_ecam *pcie_ecam,
u64 pci_addr, u64 size)
{
atu_reg_write(pcie_ecam, index, lower_32_bits(cpu_addr),
PCIE_ATU_LOWER_BASE);
ATU_LOWER_BASE);
atu_reg_write(pcie_ecam, index, upper_32_bits(cpu_addr),
PCIE_ATU_UPPER_BASE);
ATU_UPPER_BASE);
atu_reg_write(pcie_ecam, index, lower_32_bits(pci_addr),
PCIE_ATU_LOWER_TARGET);
ATU_LOWER_TARGET);
atu_reg_write(pcie_ecam, index, lower_32_bits(cpu_addr + size - 1),
PCIE_ATU_LIMIT);
ATU_LIMIT);
atu_reg_write(pcie_ecam, index, upper_32_bits(pci_addr),
PCIE_ATU_UPPER_TARGET);
atu_reg_write(pcie_ecam, index, type, PCIE_ATU_REGION_CTRL1);
atu_reg_write(pcie_ecam, index, PCIE_ATU_ENABLE, PCIE_ATU_REGION_CTRL2);
ATU_UPPER_TARGET);
atu_reg_write(pcie_ecam, index, type, ATU_REGION_CTRL1);
atu_reg_write(pcie_ecam, index, ATU_ENABLE, ATU_REGION_CTRL2);
}
static void __iomem *tegra194_map_bus(struct pci_bus *bus,
@ -81,8 +81,8 @@ static void __iomem *tegra194_map_bus(struct pci_bus *bus,
return NULL;
}
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
PCIE_ATU_FUNC(PCI_FUNC(devfn));
busdev = ATU_BUS(bus->number) | ATU_DEV(PCI_SLOT(devfn)) |
ATU_FUNC(PCI_FUNC(devfn));
if (bus->parent->number == cfg->busr.start) {
if (PCI_SLOT(devfn) == 0)

View File

@ -27,6 +27,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/resource.h>
#include <linux/types.h>
@ -385,9 +386,9 @@ static irqreturn_t tegra_pcie_rp_irq_handler(int irq, void *arg)
val |= APPL_CAR_RESET_OVRD_CYA_OVERRIDE_CORE_RST_N;
appl_writel(pcie, val, APPL_CAR_RESET_OVRD);
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
val = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
val |= SPEED_CHANGE;
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, val);
}
}
@ -621,15 +622,15 @@ static inline u32 event_counter_prog(struct tegra_pcie_dw *pcie, u32 event)
u32 val;
val = dw_pcie_readl_dbi(&pcie->pci, pcie->ras_des_cap +
PCIE_RAS_DES_EVENT_COUNTER_CONTROL);
val &= ~(EVENT_COUNTER_EVENT_SEL_MASK << EVENT_COUNTER_EVENT_SEL_SHIFT);
val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT;
val |= event << EVENT_COUNTER_EVENT_SEL_SHIFT;
val |= EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT;
RAS_DES_EVENT_COUNTER_CONTROL);
val &= ~EVENT_COUNTER_EVENT_SEL_MASK;
val |= FIELD_PREP(EVENT_COUNTER_GROUP_SEL_MASK, EVENT_COUNTER_GROUP_5);
val |= FIELD_PREP(EVENT_COUNTER_EVENT_SEL_MASK, event);
val |= FIELD_PREP(EVENT_COUNTER_ENABLE_MASK, EVENT_COUNTER_ENABLE_ALL);
dw_pcie_writel_dbi(&pcie->pci, pcie->ras_des_cap +
PCIE_RAS_DES_EVENT_COUNTER_CONTROL, val);
RAS_DES_EVENT_COUNTER_CONTROL, val);
val = dw_pcie_readl_dbi(&pcie->pci, pcie->ras_des_cap +
PCIE_RAS_DES_EVENT_COUNTER_DATA);
RAS_DES_EVENT_COUNTER_DATA);
return val;
}
@ -657,14 +658,14 @@ static int aspm_state_cnt(struct seq_file *s, void *data)
/* Clear all counters */
dw_pcie_writel_dbi(&pcie->pci, pcie->ras_des_cap +
PCIE_RAS_DES_EVENT_COUNTER_CONTROL,
RAS_DES_EVENT_COUNTER_CONTROL,
EVENT_COUNTER_ALL_CLEAR);
/* Re-enable counting */
val = EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT;
val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT;
val = FIELD_PREP(EVENT_COUNTER_ENABLE_MASK, EVENT_COUNTER_ENABLE_ALL);
val |= FIELD_PREP(EVENT_COUNTER_GROUP_SEL_MASK, EVENT_COUNTER_GROUP_5);
dw_pcie_writel_dbi(&pcie->pci, pcie->ras_des_cap +
PCIE_RAS_DES_EVENT_COUNTER_CONTROL, val);
RAS_DES_EVENT_COUNTER_CONTROL, val);
return 0;
}
@ -680,10 +681,10 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie)
PCI_EXT_CAP_ID_VNDR);
/* Enable ASPM counters */
val = EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT;
val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT;
val = FIELD_PREP(EVENT_COUNTER_ENABLE_MASK, EVENT_COUNTER_ENABLE_ALL);
val |= FIELD_PREP(EVENT_COUNTER_GROUP_SEL_MASK, EVENT_COUNTER_GROUP_5);
dw_pcie_writel_dbi(pci, pcie->ras_des_cap +
PCIE_RAS_DES_EVENT_COUNTER_CONTROL, val);
RAS_DES_EVENT_COUNTER_CONTROL, val);
/* Program T_cmrt and T_pwr_on values */
val = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
@ -713,13 +714,13 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie)
}
/* Program L0s and L1 entrance latencies */
val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR);
val &= ~PORT_AFR_L0S_ENTRANCE_LAT_MASK;
val |= (pcie->aspm_l0s_enter_lat << PORT_AFR_L0S_ENTRANCE_LAT_SHIFT);
val &= ~PORT_AFR_L1_ENTRANCE_LAT_MASK;
val |= (pcie->aspm_l1_enter_lat << PORT_AFR_L1_ENTRANCE_LAT_SHIFT);
val |= PORT_AFR_ENTER_ASPM;
dw_pcie_writel_dbi(pci, PCIE_PORT_AFR, val);
val = dw_pcie_readl_dbi(pci, PORT_AFAC);
val &= ~PORT_AFAC_L0S_ELM;
val |= FIELD_PREP(PORT_AFAC_L0S_ELM, pcie->aspm_l0s_enter_lat);
val &= ~PORT_AFAC_L1_ELM;
val |= FIELD_PREP(PORT_AFAC_L1_ELM, pcie->aspm_l1_enter_lat);
val |= PORT_AFAC_ENTER_ASPM;
dw_pcie_writel_dbi(pci, PORT_AFAC, val);
}
static void init_debugfs(struct tegra_pcie_dw *pcie)
@ -870,29 +871,29 @@ static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie)
dw_pcie_writeb_dbi(pci, offset + i, val);
}
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
val &= ~GEN3_CTRL_RATE_SHADOW_SEL_MASK;
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
FIELD_MODIFY(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, &val, 0x3ff);
val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE;
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val);
val = dw_pcie_readl_dbi(pci, GEN3_EQ_CTRL);
FIELD_MODIFY(GEN3_EQ_CTRL_PSET_REQ_VEC, &val, 0x3ff);
val &= ~GEN3_EQ_CTRL_FB_MODE;
dw_pcie_writel_dbi(pci, GEN3_EQ_CTRL, val);
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
val |= (0x1 << GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT);
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
val &= ~GEN3_CTRL_RATE_SHADOW_SEL_MASK;
val |= FIELD_PREP(GEN3_CTRL_RATE_SHADOW_SEL_MASK, 0x1);
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
FIELD_MODIFY(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, &val,
val = dw_pcie_readl_dbi(pci, GEN3_EQ_CTRL);
FIELD_MODIFY(GEN3_EQ_CTRL_PSET_REQ_VEC, &val,
pcie->of_data->gen4_preset_vec);
val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE;
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val);
val &= ~GEN3_EQ_CTRL_FB_MODE;
dw_pcie_writel_dbi(pci, GEN3_EQ_CTRL, val);
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
val &= ~GEN3_CTRL_RATE_SHADOW_SEL_MASK;
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
}
static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
@ -940,9 +941,9 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
init_host_aspm(pcie);
if (!pcie->of_data->has_l1ss_exit_fix) {
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
val &= ~GEN3_CTRL_GEN3_ZRXDC_NONCOMPL;
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
}
if (pcie->update_fc_fixup) {
@ -1869,9 +1870,9 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
pci_epc_deinit_notify(pcie->pci.ep.epc);
dw_pcie_ep_cleanup(&pcie->pci.ep);
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_SPEED_CHANGE;
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
val = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
val &= ~SPEED_CHANGE;
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, val);
if (pcie->update_fc_fixup) {
val = dw_pcie_readl_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF);
@ -1884,9 +1885,9 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
init_host_aspm(pcie);
if (!pcie->of_data->has_l1ss_exit_fix) {
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
val &= ~GEN3_CTRL_GEN3_ZRXDC_NONCOMPL;
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
}
pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci,

View File

@ -52,18 +52,18 @@ static int ultrarisc_pcie_host_init(struct dw_pcie_rp *pp)
val &= ~FAST_LINK_MODE;
dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val);
val = dw_pcie_readl_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM);
val = dw_pcie_readl_dbi(pci, TIMER_CTRL_MAX_FUNC_NUM);
FIELD_MODIFY(PORT_FLT_SF_MASK, &val, PORT_FLT_SF_VAL_64);
dw_pcie_writel_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM, val);
dw_pcie_writel_dbi(pci, TIMER_CTRL_MAX_FUNC_NUM, val);
cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCTL2);
FIELD_MODIFY(PCI_EXP_LNKCTL2_TLS, &val, PCI_EXP_LNKCTL2_TLS_16_0GT);
dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCTL2, val);
val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE);
FIELD_MODIFY(PORT_LINK_NUM_MASK, &val, 0);
dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val);
val = dw_pcie_readl_dbi(pci, PORT_FORCE_LINK);
FIELD_MODIFY(PORT_FORCE_LINK_NUM_MASK, &val, 0);
dw_pcie_writel_dbi(pci, PORT_FORCE_LINK, val);
val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_DEVCTL2);
FIELD_MODIFY(PCI_EXP_DEVCTL2_COMP_TIMEOUT, &val,
@ -154,6 +154,12 @@ static int ultrarisc_pcie_probe(struct platform_device *pdev)
pp = &pci->pp;
/*
* DP1000 routes MEM transactions without outbound iATU windows, which
* avoids the 16-window/4GB-per-window aperture limit.
*/
pp->bypass_ob_mem_iatu = true;
platform_set_drvdata(pdev, ultra);
ret = ultrarisc_pcie_init_clks(ultra);

View File

@ -1864,28 +1864,25 @@ static int advk_pcie_probe(struct platform_device *pdev)
ret = advk_sw_pci_bridge_init(pcie);
if (ret) {
dev_err(dev, "Failed to register emulated root PCI bridge\n");
return ret;
goto err_disable_phy;
}
ret = advk_pcie_init_irq_domain(pcie);
if (ret) {
dev_err(dev, "Failed to initialize irq\n");
return ret;
goto err_cleanup_bridge;
}
ret = advk_pcie_init_msi_irq_domain(pcie);
if (ret) {
dev_err(dev, "Failed to initialize irq\n");
advk_pcie_remove_irq_domain(pcie);
return ret;
goto err_remove_irq_domain;
}
ret = advk_pcie_init_rp_irq_domain(pcie);
if (ret) {
dev_err(dev, "Failed to initialize irq\n");
advk_pcie_remove_msi_irq_domain(pcie);
advk_pcie_remove_irq_domain(pcie);
return ret;
goto err_remove_msi_irq_domain;
}
bridge->sysdata = pcie;
@ -1893,14 +1890,22 @@ static int advk_pcie_probe(struct platform_device *pdev)
bridge->map_irq = advk_pcie_map_irq;
ret = pci_host_probe(bridge);
if (ret < 0) {
advk_pcie_remove_rp_irq_domain(pcie);
advk_pcie_remove_msi_irq_domain(pcie);
advk_pcie_remove_irq_domain(pcie);
return ret;
}
if (ret < 0)
goto err_remove_rp_irq_domain;
return 0;
err_remove_rp_irq_domain:
advk_pcie_remove_rp_irq_domain(pcie);
err_remove_msi_irq_domain:
advk_pcie_remove_msi_irq_domain(pcie);
err_remove_irq_domain:
advk_pcie_remove_irq_domain(pcie);
err_cleanup_bridge:
pci_bridge_emul_cleanup(&pcie->bridge);
err_disable_phy:
advk_pcie_disable_phy(pcie);
return ret;
}
static void advk_pcie_remove(struct platform_device *pdev)

View File

@ -1235,6 +1235,11 @@ static void mvebu_pcie_port_clk_put(void *data)
clk_put(port->clk);
}
static void mvebu_pcie_port_of_node_put(void *data)
{
of_node_put(data);
}
static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
struct mvebu_pcie_port *port, struct device_node *child)
{
@ -1451,7 +1456,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
struct mvebu_pcie *pcie;
struct pci_host_bridge *bridge;
struct device_node *np = dev->of_node;
struct device_node *child;
int num, i, ret;
bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct mvebu_pcie));
@ -1473,18 +1477,20 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
return -ENOMEM;
i = 0;
for_each_available_child_of_node(np, child) {
for_each_available_child_of_node_scoped(np, child) {
struct mvebu_pcie_port *port = &pcie->ports[i];
ret = mvebu_pcie_parse_port(pcie, port, child);
if (ret < 0) {
of_node_put(child);
if (ret < 0)
return ret;
} else if (ret == 0) {
else if (ret == 0)
continue;
}
port->dn = child;
port->dn = of_node_get(child);
ret = devm_add_action_or_reset(dev, mvebu_pcie_port_of_node_put,
child);
if (ret)
return ret;
i++;
}
pcie->nports = i;
@ -1492,6 +1498,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
for (i = 0; i < pcie->nports; i++) {
struct mvebu_pcie_port *port = &pcie->ports[i];
int irq = port->intx_irq;
struct device_node *child;
child = port->dn;
if (!child)

View File

@ -36,6 +36,7 @@
#include <linux/phy/phy.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/slab.h>

View File

@ -320,18 +320,11 @@ static void xgene_pcie_linkup(struct xgene_pcie *port,
static int xgene_pcie_init_port(struct xgene_pcie *port)
{
struct device *dev = port->dev;
int rc;
port->clk = clk_get(dev, NULL);
port->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(port->clk)) {
dev_err(dev, "clock not available\n");
return -ENODEV;
}
rc = clk_prepare_enable(port->clk);
if (rc) {
dev_err(dev, "clock enable failed\n");
return rc;
return PTR_ERR(port->clk);
}
return 0;

View File

@ -761,16 +761,21 @@ static int aspeed_pcie_port_init(struct aspeed_pcie_port *port)
port->slot);
ret = phy_init(port->phy);
if (ret)
if (ret) {
clk_disable_unprepare(port->clk);
return dev_err_probe(dev, ret,
"failed to init phy pcie for slot (%d)\n",
port->slot);
}
ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
if (ret)
if (ret) {
phy_exit(port->phy);
clk_disable_unprepare(port->clk);
return dev_err_probe(dev, ret,
"failed to set phy mode for slot (%d)\n",
port->slot);
}
reset_control_deassert(port->perst);
msleep(PCIE_RESET_CONFIG_WAIT_MS);

View File

@ -277,6 +277,9 @@ struct inbound_win {
u64 cpu_addr;
};
/* QUIRKS */
#define BQUIRK(pcie, quirk) ((pcie)->cfg->quirks & CFG_QUIRK_ ## quirk)
/*
* The RESCAL block is tied to PCIe controller #1, regardless of the number of
* controllers, and turning off PCIe controller #1 prevents access to the RESCAL
@ -285,17 +288,38 @@ struct inbound_win {
* or a hang (AXI).
*/
#define CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN BIT(0)
/* The outbound windows must be within the 0-4GB region */
#define CFG_QUIRK_OB_WIN_32BIT_ADDR BIT(1)
/* Each of the outbound windows size must be <= SZ_128M */
#define CFG_QUIRK_OB_WIN_MAXSZ_128MB BIT(2)
/* PCIe HW can only access config space with 32bit R/W */
#define CFG_QUIRK_32BIT_PCI_OPS BIT(3)
/* PCIe HW does not have an internal bus timer */
#define CFG_QUIRK_NO_RGR1_TIMER BIT(4)
/* PCIe PERST# must be asserted before internal bridge turned on */
#define CFG_QUIRK_EARLY_PERST_ASSERT BIT(5)
/* PCIe SW PERST behavior only works up to a specific HW version */
#define CFG_QUIRK_PERST_PCIE_REV_CUTOFF BIT(6)
/* FLAGS */
#define BFLAG(pcie, flag) ((pcie)->cfg->flags & CFG_FLG_ ## flag)
/* The PCIe HW registers have phy control of a "RESCAL" reset block */
#define CFG_FLG_HAS_PHY BIT(0)
/* The SoC PCIe HW can dump to the console PCIe error info */
#define CFG_FLG_HAS_ERR_REPORT BIT(1)
/* SoC uses a Broadcom variant of the MIPS-1 ISA */
#define CFG_FLG_IS_BMIPS BIT(2)
struct pcie_cfg_data {
const int *offsets;
const enum pcie_soc_base soc_base;
const bool has_phy;
const u32 quirks;
enum pcie_soc_base soc_base;
u32 flags;
u32 quirks;
u8 num_inbound_wins;
int (*perst_set)(struct brcm_pcie *pcie, u32 val);
int (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
int (*post_setup)(struct brcm_pcie *pcie);
bool has_err_report;
};
struct subdev_regulators {
@ -346,24 +370,19 @@ struct brcm_pcie {
spinlock_t bridge_lock;
};
static inline bool is_bmips(const struct brcm_pcie *pcie)
{
return pcie->cfg->soc_base == BCM7435 || pcie->cfg->soc_base == BCM7425;
}
static int brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val)
{
unsigned long flags;
int ret;
if (pcie->cfg->has_err_report)
if (BFLAG(pcie, HAS_ERR_REPORT))
spin_lock_irqsave(&pcie->bridge_lock, flags);
ret = pcie->cfg->bridge_sw_init_set(pcie, val);
/* If we fail, assume the bridge is in reset (off) */
pcie->bridge_in_reset = ret ? true : val;
if (pcie->cfg->has_err_report)
if (BFLAG(pcie, HAS_ERR_REPORT))
spin_unlock_irqrestore(&pcie->bridge_lock, flags);
return ret;
@ -508,7 +527,7 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK);
writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
if (is_bmips(pcie))
if (BQUIRK(pcie, OB_WIN_32BIT_ADDR))
return;
/* Write the cpu & limit addr upper bits */
@ -1144,8 +1163,8 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
if (ret)
return ret;
/* Ensure that PERST# is asserted; some bootloaders may deassert it. */
if (pcie->cfg->soc_base == BCM2711) {
if (BQUIRK(pcie, EARLY_PERST_ASSERT)) {
/* Ensure that PERST# is asserted; some bootloaders may deassert it. */
ret = pcie->cfg->perst_set(pcie, 1);
if (ret) {
pcie->cfg->bridge_sw_init_set(pcie, 0);
@ -1161,7 +1180,7 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
return ret;
tmp = readl(base + HARD_DEBUG(pcie));
if (is_bmips(pcie))
if (BFLAG(pcie, IS_BMIPS))
tmp &= ~PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
else
tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
@ -1174,7 +1193,7 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
* is encoded as 0=128, 1=256, 2=512, 3=Rsvd, for BCM7278 it
* is encoded as 0=Rsvd, 1=128, 2=256, 3=512.
*/
if (is_bmips(pcie))
if (BFLAG(pcie, IS_BMIPS))
burst = 0x1; /* 256 bytes */
else if (pcie->cfg->soc_base == BCM2711)
burst = 0x0; /* 128 bytes */
@ -1281,11 +1300,11 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
return -EINVAL;
}
if (is_bmips(pcie)) {
if (BQUIRK(pcie, OB_WIN_MAXSZ_128MB)) {
u64 start = res->start;
unsigned int j, nwins = resource_size(res) / SZ_128M;
/* bmips PCIe outbound windows have a 128MB max size */
/* PCIe outbound windows have a 128MB max size */
if (nwins > BRCM_NUM_PCIE_OUT_WINS)
nwins = BRCM_NUM_PCIE_OUT_WINS;
for (j = 0; j < nwins; j++, start += SZ_128M)
@ -1326,8 +1345,8 @@ static void brcm_extend_rbus_timeout(struct brcm_pcie *pcie)
const unsigned int REG_OFFSET = PCIE_RGR1_SW_INIT_1(pcie) - 8;
u32 timeout_us = 4000000; /* 4 seconds, our setting for L1SS */
/* 7712 does not have this (RGR1) timer */
if (pcie->cfg->soc_base == BCM7712)
/* Don't do access if there is no timer */
if (BQUIRK(pcie, NO_RGR1_TIMER))
return;
/* Each unit in timeout register is 1/216,000,000 seconds */
@ -1589,12 +1608,12 @@ static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start)
static inline int brcm_phy_start(struct brcm_pcie *pcie)
{
return pcie->cfg->has_phy ? brcm_phy_cntl(pcie, 1) : 0;
return BFLAG(pcie, HAS_PHY) ? brcm_phy_cntl(pcie, 1) : 0;
}
static inline int brcm_phy_stop(struct brcm_pcie *pcie)
{
return pcie->cfg->has_phy ? brcm_phy_cntl(pcie, 0) : 0;
return BFLAG(pcie, HAS_PHY) ? brcm_phy_cntl(pcie, 0) : 0;
}
static int brcm_pcie_turn_off(struct brcm_pcie *pcie)
@ -1619,7 +1638,7 @@ static int brcm_pcie_turn_off(struct brcm_pcie *pcie)
u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
writel(tmp, base + HARD_DEBUG(pcie));
if (!(pcie->cfg->quirks & CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN))
if (!(BQUIRK(pcie, AVOID_BRIDGE_SHUTDOWN)))
/* Shutdown PCIe bridge */
ret = brcm_pcie_bridge_sw_init_set(pcie, 1);
@ -1898,7 +1917,7 @@ static void brcm_pcie_remove(struct platform_device *pdev)
pci_stop_root_bus(bridge->bus);
pci_remove_root_bus(bridge->bus);
pci_unlock_rescan_remove();
if (pcie->cfg->has_err_report)
if (BFLAG(pcie, HAS_ERR_REPORT))
brcm_unregister_die_notifiers(pcie);
__brcm_pcie_remove(pcie);
@ -1950,6 +1969,7 @@ static const struct pcie_cfg_data bcm2711_cfg = {
.perst_set = brcm_pcie_perst_set_generic,
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
.num_inbound_wins = 3,
.quirks = CFG_QUIRK_EARLY_PERST_ASSERT,
};
static const struct pcie_cfg_data bcm2712_cfg = {
@ -1958,7 +1978,8 @@ static const struct pcie_cfg_data bcm2712_cfg = {
.perst_set = brcm_pcie_perst_set_7278,
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
.post_setup = brcm_pcie_post_setup_bcm2712,
.quirks = CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN,
.quirks = CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN |
CFG_QUIRK_NO_RGR1_TIMER,
.num_inbound_wins = 10,
};
@ -1968,6 +1989,7 @@ static const struct pcie_cfg_data bcm4908_cfg = {
.perst_set = brcm_pcie_perst_set_4908,
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
.num_inbound_wins = 3,
.quirks = CFG_QUIRK_PERST_PCIE_REV_CUTOFF,
};
static const struct pcie_cfg_data bcm7278_cfg = {
@ -1984,6 +2006,9 @@ static const struct pcie_cfg_data bcm7425_cfg = {
.perst_set = brcm_pcie_perst_set_generic,
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
.num_inbound_wins = 3,
.quirks = CFG_QUIRK_OB_WIN_32BIT_ADDR
| CFG_QUIRK_OB_WIN_MAXSZ_128MB | CFG_QUIRK_32BIT_PCI_OPS,
.flags = CFG_FLG_IS_BMIPS,
};
static const struct pcie_cfg_data bcm7435_cfg = {
@ -1992,6 +2017,9 @@ static const struct pcie_cfg_data bcm7435_cfg = {
.perst_set = brcm_pcie_perst_set_generic,
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
.num_inbound_wins = 3,
.quirks = CFG_QUIRK_OB_WIN_32BIT_ADDR
| CFG_QUIRK_OB_WIN_MAXSZ_128MB,
.flags = CFG_FLG_IS_BMIPS,
};
static const struct pcie_cfg_data bcm7216_cfg = {
@ -1999,9 +2027,8 @@ static const struct pcie_cfg_data bcm7216_cfg = {
.soc_base = BCM7278,
.perst_set = brcm_pcie_perst_set_7278,
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_7278,
.has_phy = true,
.flags = CFG_FLG_HAS_PHY | CFG_FLG_HAS_ERR_REPORT,
.num_inbound_wins = 3,
.has_err_report = true,
};
static const struct pcie_cfg_data bcm7712_cfg = {
@ -2010,6 +2037,7 @@ static const struct pcie_cfg_data bcm7712_cfg = {
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
.soc_base = BCM7712,
.num_inbound_wins = 10,
.quirks = CFG_QUIRK_NO_RGR1_TIMER,
};
static const struct of_device_id brcm_pcie_match[] = {
@ -2034,7 +2062,7 @@ static struct pci_ops brcm_pcie_ops = {
.remove_bus = brcm_pcie_remove_bus,
};
static struct pci_ops brcm7425_pcie_ops = {
static struct pci_ops brcm_pcie_ops32 = {
.map_bus = brcm7425_pcie_map_bus,
.read = pci_generic_config_read32,
.write = pci_generic_config_write32,
@ -2143,7 +2171,7 @@ static int brcm_pcie_probe(struct platform_device *pdev)
goto fail;
pcie->hw_rev = readl(pcie->base + PCIE_MISC_REVISION);
if (pcie->cfg->soc_base == BCM4908 &&
if (BQUIRK(pcie, PERST_PCIE_REV_CUTOFF) &&
pcie->hw_rev >= BRCM_PCIE_HW_REV_3_20) {
dev_err(pcie->dev, "hardware revision with unsupported PERST# setup\n");
ret = -ENODEV;
@ -2164,8 +2192,8 @@ static int brcm_pcie_probe(struct platform_device *pdev)
}
}
bridge->ops = pcie->cfg->soc_base == BCM7425 ?
&brcm7425_pcie_ops : &brcm_pcie_ops;
bridge->ops = BQUIRK(pcie, 32BIT_PCI_OPS)
? &brcm_pcie_ops32 : &brcm_pcie_ops;
bridge->sysdata = pcie;
platform_set_drvdata(pdev, pcie);
@ -2179,7 +2207,7 @@ static int brcm_pcie_probe(struct platform_device *pdev)
return ret;
}
if (pcie->cfg->has_err_report) {
if (BFLAG(pcie, HAS_ERR_REPORT)) {
spin_lock_init(&pcie->bridge_lock);
brcm_register_die_notifiers(pcie);
}

View File

@ -11,7 +11,9 @@
#include <linux/phy/phy.h>
#include <linux/bcma/bcma.h>
#include <linux/ioport.h>
#include <linux/of_address.h>
#include "../pci.h"
#include "pcie-iproc.h"
@ -31,6 +33,29 @@ static int iproc_bcma_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
return bcma_core_irq(bdev, 5);
}
static void iproc_bcma_pcie_check_dt_window(struct iproc_pcie *pcie)
{
struct device_node *np = pcie->dev->of_node;
struct of_pci_range_parser parser;
struct of_pci_range range;
struct resource res;
if (!np || of_pci_range_parser_init(&parser, np))
return;
for_each_of_pci_range(&parser, &range) {
if ((range.flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM)
continue;
if (of_pci_range_to_resource(&range, np, &res))
continue;
if (res.start != pcie->mem.start || res.end != pcie->mem.end)
dev_warn(pcie->dev, "DT window %pR does not match EROM window %pR, using EROM\n",
&res, &pcie->mem);
}
}
static int iproc_bcma_pcie_probe(struct bcma_device *bdev)
{
struct device *dev = &bdev->dev;
@ -38,10 +63,20 @@ static int iproc_bcma_pcie_probe(struct bcma_device *bdev)
struct pci_host_bridge *bridge;
int ret;
bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie));
bridge = pci_alloc_host_bridge(sizeof(*pcie));
if (!bridge)
return -ENOMEM;
bridge->dev.parent = dev;
ret = devm_add_action_or_reset(dev, devm_pci_alloc_host_bridge_release,
bridge);
if (ret)
return -ENOMEM;
bridge->swizzle_irq = pci_common_swizzle;
bridge->map_irq = iproc_bcma_pcie_map_irq;
pcie = pci_host_bridge_priv(bridge);
pcie->dev = dev;
@ -59,13 +94,12 @@ static int iproc_bcma_pcie_probe(struct bcma_device *bdev)
pcie->mem.end = bdev->addr_s[0] + SZ_128M - 1;
pcie->mem.name = "PCIe MEM space";
pcie->mem.flags = IORESOURCE_MEM;
iproc_bcma_pcie_check_dt_window(pcie);
pci_add_resource(&bridge->windows, &pcie->mem);
ret = devm_request_pci_bus_resources(dev, &bridge->windows);
if (ret)
return ret;
bridge->map_irq = iproc_bcma_pcie_map_irq;
bcma_set_drvdata(bdev, pcie);
return iproc_pcie_setup(pcie, &bridge->windows);

View File

@ -7,6 +7,8 @@
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
@ -334,10 +336,10 @@ static int mtk_pcie_set_trans_table(struct mtk_gen3_pcie *pcie,
while (remaining && (*num < PCIE_MAX_TRANS_TABLES)) {
/* Table size needs to be a power of 2 */
table_size = BIT(fls(remaining) - 1);
table_size = BIT_ULL(fls64(remaining) - 1);
if (cpu_addr > 0) {
addr_align = BIT(ffs(cpu_addr) - 1);
addr_align = BIT_ULL(__ffs64(cpu_addr));
table_size = min(table_size, addr_align);
}
@ -349,7 +351,8 @@ static int mtk_pcie_set_trans_table(struct mtk_gen3_pcie *pcie,
}
table = pcie->base + PCIE_TRANS_TABLE_BASE_REG + *num * PCIE_ATR_TLB_SET_OFFSET;
writel_relaxed(lower_32_bits(cpu_addr) | PCIE_ATR_SIZE(fls(table_size) - 1), table);
writel_relaxed(lower_32_bits(cpu_addr) |
PCIE_ATR_SIZE(fls64(table_size) - 1), table);
writel_relaxed(upper_32_bits(cpu_addr), table + PCIE_ATR_SRC_ADDR_MSB_OFFSET);
writel_relaxed(lower_32_bits(pci_addr), table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET);
writel_relaxed(upper_32_bits(pci_addr), table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET);
@ -375,9 +378,11 @@ static int mtk_pcie_set_trans_table(struct mtk_gen3_pcie *pcie,
(*num)++;
}
if (remaining)
dev_warn(pcie->dev, "not enough translate table for addr: %#llx, limited to [%d]\n",
if (remaining) {
dev_err(pcie->dev, "not enough translate table for addr: %#llx, limited to [%d]\n",
(unsigned long long)cpu_addr, PCIE_MAX_TRANS_TABLES);
return -ENOMEM;
}
return 0;
}

View File

@ -583,9 +583,9 @@ static int mtk_pcie_init_irq_domain(struct mtk_pcie_port *port,
int ret;
/* Setup INTx */
pcie_intc_node = of_get_next_child(node, NULL);
pcie_intc_node = of_get_child_by_name(node, "interrupt-controller");
if (!pcie_intc_node) {
dev_err(dev, "no PCIe Intc node found\n");
dev_err(dev, "no PCIe INTx interrupt-controller node found\n");
return -ENODEV;
}

View File

@ -553,19 +553,6 @@ static void rockchip_pcie_ep_link_training(struct work_struct *work)
if (ret)
goto again;
/*
* Check the current speed: if gen2 speed was requested and we are not
* at gen2 speed yet, retrain again for gen2.
*/
val = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
if (!PCIE_LINK_IS_GEN2(val) && rockchip->link_gen == 2) {
/* Enable retrain for gen2 */
rockchip_pcie_ep_retrain_link(rockchip);
readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL,
val, PCIE_LINK_IS_GEN2(val), 50,
LINK_TRAIN_TIMEOUT);
}
/* Check again that the link is up */
if (!rockchip_pcie_ep_link_up(rockchip))
goto again;

View File

@ -24,6 +24,7 @@
#include <linux/of_pci.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include "../pci.h"
#include "pcie-rockchip.h"
@ -328,26 +329,6 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip)
goto err_power_off_phy;
}
if (rockchip->link_gen == 2) {
/*
* Enable retrain for gen2. This should be configured only after
* gen1 finished.
*/
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL2);
status &= ~PCI_EXP_LNKCTL2_TLS;
status |= PCI_EXP_LNKCTL2_TLS_5_0GT;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL2);
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
status |= PCI_EXP_LNKCTL_RL;
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
err = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL,
status, PCIE_LINK_IS_GEN2(status), 20,
500 * USEC_PER_MSEC);
if (err)
dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
}
/* Check the final link width from negotiated lane counter from MGMT */
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >>

View File

@ -66,8 +66,10 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip)
}
rockchip->link_gen = of_pci_get_max_link_speed(node);
if (rockchip->link_gen < 0 || rockchip->link_gen > 2)
rockchip->link_gen = 2;
if (rockchip->link_gen < 0 || rockchip->link_gen >= 2) {
rockchip->link_gen = 1;
dev_warn(dev, "invalid max-link-speed, limited to 2.5 GT/s\n");
}
for (i = 0; i < ROCKCHIP_NUM_PM_RSTS; i++)
rockchip->pm_rsts[i].id = rockchip_pci_pm_rsts[i];
@ -147,12 +149,8 @@ int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
goto err_exit_phy;
}
if (rockchip->link_gen == 2)
rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_2,
PCIE_CLIENT_CONFIG);
else
rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1,
PCIE_CLIENT_CONFIG);
rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1,
PCIE_CLIENT_CONFIG);
regs = PCIE_CLIENT_ARI_ENABLE |
PCIE_CLIENT_CONF_LANE_NUM(rockchip->lanes);

View File

@ -42,7 +42,6 @@
#define PCIE_CLIENT_MODE_RC HWORD_SET_BIT(0x0040)
#define PCIE_CLIENT_MODE_EP HWORD_CLR_BIT(0x0040)
#define PCIE_CLIENT_GEN_SEL_1 HWORD_CLR_BIT(0x0080)
#define PCIE_CLIENT_GEN_SEL_2 HWORD_SET_BIT(0x0080)
#define PCIE_CLIENT_LEGACY_INT_CTRL (PCIE_CLIENT_BASE + 0x0c)
#define PCIE_CLIENT_INT_IN_ASSERT HWORD_SET_BIT(0x0002)
#define PCIE_CLIENT_INT_IN_DEASSERT HWORD_CLR_BIT(0x0002)
@ -197,8 +196,6 @@
(((x) & PCIE_CORE_PL_CONF_LS_MASK) == PCIE_CORE_PL_CONF_LS_READY)
#define PCIE_LINK_UP(x) \
(((x) & PCIE_CLIENT_LINK_STATUS_MASK) == PCIE_CLIENT_LINK_STATUS_UP)
#define PCIE_LINK_IS_GEN2(x) \
(((x) & PCIE_CORE_PL_CONF_SPEED_MASK) == PCIE_CORE_PL_CONF_SPEED_5G)
#define RC_REGION_0_ADDR_TRANS_H 0x00000000
#define RC_REGION_0_ADDR_TRANS_L 0x00000000

View File

@ -1006,22 +1006,38 @@ static const struct irq_domain_ops rzg3s_pcie_intx_domain_ops = {
.xlate = irq_domain_xlate_onetwocell,
};
static void rzg3s_pcie_teardown_intx(struct rzg3s_pcie_host *host,
int count)
{
while (--count >= 0) {
irq_set_chained_handler_and_data(host->intx_irqs[count], NULL,
NULL);
}
if (host->intx_domain)
irq_domain_remove(host->intx_domain);
}
static int rzg3s_pcie_init_irqdomain(struct rzg3s_pcie_host *host)
{
struct device *dev = host->dev;
struct platform_device *pdev = to_platform_device(dev);
int i, ret;
for (int i = 0; i < PCI_NUM_INTX; i++) {
for (i = 0; i < PCI_NUM_INTX; i++) {
char irq_name[5] = {0};
int irq;
scnprintf(irq_name, ARRAY_SIZE(irq_name), "int%c", 'a' + i);
irq = platform_get_irq_byname(pdev, irq_name);
if (irq < 0)
return dev_err_probe(dev, -EINVAL,
"Failed to parse and map INT%c IRQ\n",
'A' + i);
if (irq < 0) {
ret = irq;
dev_err_probe(dev, ret,
"Failed to parse and map INT%c IRQ\n",
'A' + i);
goto teardown_intx;
}
host->intx_irqs[i] = irq;
irq_set_chained_handler_and_data(irq,
@ -1033,21 +1049,27 @@ static int rzg3s_pcie_init_irqdomain(struct rzg3s_pcie_host *host)
PCI_NUM_INTX,
&rzg3s_pcie_intx_domain_ops,
host);
if (!host->intx_domain)
return dev_err_probe(dev, -EINVAL,
"Failed to add irq domain for INTx IRQs\n");
if (!host->intx_domain) {
ret = -EINVAL;
dev_err_probe(dev, ret,
"Failed to add irq domain for INTx IRQs\n");
goto teardown_intx;
}
irq_domain_update_bus_token(host->intx_domain, DOMAIN_BUS_WIRED);
if (IS_ENABLED(CONFIG_PCI_MSI)) {
int ret = rzg3s_pcie_init_msi(host);
ret = rzg3s_pcie_init_msi(host);
if (ret) {
irq_domain_remove(host->intx_domain);
return ret;
}
if (ret)
goto teardown_intx;
}
return 0;
teardown_intx:
rzg3s_pcie_teardown_intx(host, i);
return ret;
}
static void rzg3s_pcie_teardown_irqdomain(struct rzg3s_pcie_host *host)
@ -1055,7 +1077,7 @@ static void rzg3s_pcie_teardown_irqdomain(struct rzg3s_pcie_host *host)
if (IS_ENABLED(CONFIG_PCI_MSI))
rzg3s_pcie_teardown_msi(host);
irq_domain_remove(host->intx_domain);
rzg3s_pcie_teardown_intx(host, PCI_NUM_INTX);
}
static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
@ -1077,7 +1099,7 @@ static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp) == ltssm_state_l0,
PCIE_LINK_WAIT_SLEEP_MS * MILLI,
PCIE_LINK_WAIT_SLEEP_MS * MILLI *
PCIE_LINK_WAIT_MAX_RETRIES);
PCIE_LINK_WAIT_MAX_RETRIES * 10);
if (ret)
return ret;
@ -1897,6 +1919,7 @@ static int rzg3s_pcie_probe(struct platform_device *pdev)
return 0;
host_probe_teardown:
clk_disable_unprepare(host->port.refclk);
rzg3s_pcie_teardown_irqdomain(host);
host->data->config_deinit(host);
rpm_put:
@ -2074,6 +2097,25 @@ static const struct rzg3s_pcie_soc_data rzg3e_soc_data = {
},
};
static const struct rzg3s_pcie_soc_data rzg3l_soc_data = {
.power_resets = rzg3e_soc_power_resets,
.num_power_resets = ARRAY_SIZE(rzg3e_soc_power_resets),
.num_pcie_controllers = 1,
.config_pre_init = rzg3e_pcie_config_pre_init,
.config_post_init = rzg3e_pcie_config_post_init,
.config_deinit = rzg3e_pcie_config_deinit,
.sysc_info = {
[RZG3S_PCIE_CONTROLLER_ID_0] = {
.functions = {
[RZG3S_SYSC_FUNC_ID_L1_ALLOW] = {
.offset = 0x3a0,
.mask = BIT(8),
},
},
},
},
};
static const struct rzg3s_pcie_soc_data rzv2h_soc_data = {
.power_resets = rzg3e_soc_power_resets,
.num_power_resets = ARRAY_SIZE(rzg3e_soc_power_resets),
@ -2123,6 +2165,10 @@ static const struct of_device_id rzg3s_pcie_of_match[] = {
.compatible = "renesas,r9a08g045-pcie",
.data = &rzg3s_soc_data,
},
{
.compatible = "renesas,r9a08g046-pcie",
.data = &rzg3l_soc_data,
},
{
.compatible = "renesas,r9a09g047-pcie",
.data = &rzg3e_soc_data,

View File

@ -49,8 +49,7 @@
#define XAL_RC_BAR_CNTL_STANDARD_64B_BAR_EN BIT(2)
/* XTL registers */
#define XTL_RC_PCIE_CFG_LINK_CAPS 0x56
#define XTL_RC_PCIE_CFG_LINK_STATUS 0x5a
#define XTL_RC_PCIE_CAP 0x48 /* PCIe Capability */
#define XTL_RC_MGMT_PERST_CONTROL 0x218
#define XTL_RC_MGMT_PERST_CONTROL_PERST_O_N BIT(0)
@ -118,11 +117,11 @@ static void tegra264_pcie_icc_set(struct tegra264_pcie *pcie)
* possible, so this is as good as it gets for now.
*/
if (pcie->link_up) {
value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_STATUS);
value = readw(pcie->ecam + XTL_RC_PCIE_CAP + PCI_EXP_LNKSTA);
speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, value);
width = FIELD_GET(PCI_EXP_LNKSTA_NLW, value);
} else {
value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_CAPS);
value = readl(pcie->ecam + XTL_RC_PCIE_CAP + PCI_EXP_LNKCAP);
speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, value);
width = FIELD_GET(PCI_EXP_LNKCAP_MLW, value);
}
@ -263,7 +262,7 @@ static bool tegra264_pcie_supports_hotplug(struct tegra264_pcie *pcie)
static bool tegra264_pcie_link_up(struct tegra264_pcie *pcie,
enum pci_bus_speed *speed)
{
u16 value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_STATUS);
u16 value = readw(pcie->ecam + XTL_RC_PCIE_CAP + PCI_EXP_LNKSTA);
if (value & PCI_EXP_LNKSTA_DLLLA) {
if (speed)

View File

@ -6,6 +6,8 @@
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
@ -16,6 +18,7 @@
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/of_platform.h>
#include <linux/reset.h>
#include "../pci.h"
#include "pcie-xilinx-common.h"
@ -113,6 +116,8 @@ struct xilinx_cpm_variant {
* @irq: Error interrupt number
* @lock: lock protecting shared register access
* @variant: CPM version check pointer
* @perst_gpio: GPIO descriptor for PERST# signal handling
* @rstc: Pointer to the PCIe controller reset
*/
struct xilinx_cpm_pcie {
struct device *dev;
@ -125,6 +130,8 @@ struct xilinx_cpm_pcie {
int irq;
raw_spinlock_t lock;
const struct xilinx_cpm_variant *variant;
struct gpio_desc *perst_gpio;
struct reset_control *rstc;
};
static u32 pcie_read(struct xilinx_cpm_pcie *port, u32 reg)
@ -389,7 +396,7 @@ static int xilinx_cpm_pcie_init_irq_domain(struct xilinx_cpm_pcie *port)
struct device_node *pcie_intc_node;
/* Setup INTx */
pcie_intc_node = of_get_next_child(node, NULL);
pcie_intc_node = of_get_child_by_name(node, "interrupt-controller");
if (!pcie_intc_node) {
dev_err(dev, "No PCIe Intc node found\n");
return -EINVAL;
@ -469,6 +476,22 @@ static int xilinx_cpm_setup_irq(struct xilinx_cpm_pcie *port)
return 0;
}
/**
* xilinx_cpm_pcie_reset - Reset the PCIe controller and deassert PERST#
* @port: PCIe port information
*
* Reset the PCIe controller and then release the PERST# signal so that the
* link can train once the bridge is enabled.
*/
static void xilinx_cpm_pcie_reset(struct xilinx_cpm_pcie *port)
{
reset_control_assert(port->rstc);
udelay(PCIE_T_PERST_US);
reset_control_deassert(port->rstc);
gpiod_set_value_cansleep(port->perst_gpio, 0);
msleep(PCIE_RESET_CONFIG_WAIT_MS);
}
/**
* xilinx_cpm_pcie_init_port - Initialize hardware
* @port: PCIe port information
@ -480,6 +503,9 @@ static void xilinx_cpm_pcie_init_port(struct xilinx_cpm_pcie *port)
if (variant->version == CPM5NC_HOST)
return;
if (port->perst_gpio || port->rstc)
xilinx_cpm_pcie_reset(port);
if (cpm_pcie_link_up(port))
dev_info(port->dev, "PCIe Link is UP\n");
else
@ -512,6 +538,42 @@ static void xilinx_cpm_pcie_init_port(struct xilinx_cpm_pcie *port)
XILINX_CPM_PCIE_REG_RPSC);
}
/**
* xilinx_cpm_pcie_parse_port - Parse the PCIe Root Port child node
* @port: PCIe port information
*
* Read the PERST# GPIO from the Root Port child node.
*
* Return: '0' on success and error value on failure
*/
static int xilinx_cpm_pcie_parse_port(struct xilinx_cpm_pcie *port)
{
struct device *dev = port->dev;
/*
* This platform currently supports only one Root Port, so the loop
* will execute only once.
* TODO: Enhance the driver to handle multiple Root Ports in the future.
*/
for_each_child_of_node_with_prefix(dev->of_node, pcie_port_node, "pcie") {
port->perst_gpio = devm_fwnode_gpiod_get(dev,
of_fwnode_handle(pcie_port_node),
"reset", GPIOD_OUT_HIGH,
NULL);
if (IS_ERR(port->perst_gpio)) {
if (PTR_ERR(port->perst_gpio) == -ENOENT) {
port->perst_gpio = NULL;
return 0;
}
return dev_err_probe(dev, PTR_ERR(port->perst_gpio),
"Failed to request reset GPIO\n");
}
return 0;
}
return 0;
}
/**
* xilinx_cpm_pcie_parse_dt - Parse Device tree
* @port: PCIe port information
@ -525,6 +587,19 @@ static int xilinx_cpm_pcie_parse_dt(struct xilinx_cpm_pcie *port,
struct device *dev = port->dev;
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
int ret;
/* CPM5NC does not support PERST# handling yet */
if (port->variant->version != CPM5NC_HOST) {
port->rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(port->rstc))
return dev_err_probe(dev, PTR_ERR(port->rstc),
"Failed to request reset\n");
ret = xilinx_cpm_pcie_parse_port(port);
if (ret)
return ret;
}
port->cpm_base = devm_platform_ioremap_resource_byname(pdev,
"cpm_slcr");

View File

@ -581,7 +581,7 @@ static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port)
port->pldma_domain = irq_domain_create_linear(of_fwnode_handle(pcie_intc_node), 32,
&event_domain_ops, port);
if (!port->pldma_domain)
return -ENOMEM;
goto err_put_node;
irq_domain_update_bus_token(port->pldma_domain, DOMAIN_BUS_NEXUS);
@ -589,7 +589,7 @@ static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port)
&intx_domain_ops, port);
if (!port->intx_domain) {
dev_err(dev, "Failed to get a INTx IRQ domain\n");
return -ENOMEM;
goto err_put_node;
}
irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
@ -597,13 +597,17 @@ static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port)
ret = xilinx_pl_dma_pcie_init_msi_irq_domain(port);
if (ret != 0) {
irq_domain_remove(port->intx_domain);
return -ENOMEM;
goto err_put_node;
}
of_node_put(pcie_intc_node);
raw_spin_lock_init(&port->lock);
return 0;
err_put_node:
of_node_put(pcie_intc_node);
return -ENOMEM;
}
static int xilinx_pl_dma_pcie_setup_irq(struct pl_dma_pcie *port)

View File

@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include "../../pci.h"

View File

@ -92,6 +92,23 @@ enum vmd_features {
* referred to as MEMBAR2 or MSI-X BAR.
*/
VMD_FEAT_USE_BIOS_INFO = (1 << 6),
/*
* Some Intel VMD devices are affected by interrupt ordering errata,
* including MTL016 (0x7d0b) and ARL004 (0xad0b):
* the VMD may signal its MSI before the posted writes that carry the
* child device's DMA data have landed in memory. The demuxed handler
* then runs against a not-yet-coherent completion queue and misses the
* completion entirely, so the I/O is only recovered when the block
* layer timeout fires and polls the queue ("timeout, completion
* polled"). Intel's documented workaround is to issue a dummy read
* to the MSI initiator (the child device) before handling the
* interrupt: the read completion cannot pass the device's earlier
* posted writes, so it pulls them into memory per PCIe ordering
* rules. A read that terminates at the VMD itself does not order
* against the child's writes and is not sufficient (measured).
*/
VMD_FEAT_INTERRUPT_QUIRK = (1 << 7),
};
#define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */
@ -114,6 +131,9 @@ static DEFINE_RAW_SPINLOCK(list_lock);
* @irq: back pointer to parent.
* @enabled: true if driver enabled IRQ
* @virq: the virtual IRQ value provided to the requesting driver.
* @flush_addr: config space address of the initiating device, read before
* demuxing to flush its posted writes; NULL if the
* VMD is not affected.
*
* Every MSI/MSI-X IRQ requested for a device in a VMD domain will be mapped to
* a VMD IRQ using this structure.
@ -123,8 +143,14 @@ struct vmd_irq {
struct vmd_irq_list *irq;
bool enabled;
unsigned int virq;
void __iomem *flush_addr;
};
struct vmd_dev;
static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
unsigned int devfn, int reg, int len);
/**
* struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector
* @irq_list: the list of irq's the VMD one demuxes to.
@ -132,12 +158,15 @@ struct vmd_irq {
* @count: number of child IRQs assigned to this vector; used to track
* sharing.
* @virq: The underlying VMD Linux interrupt number
* @vmd: back pointer to the owning VMD device; serializes the MTL016
* flush read against other config space access.
*/
struct vmd_irq_list {
struct list_head irq_list;
struct srcu_struct srcu;
unsigned int count;
unsigned int virq;
struct vmd_dev *vmd;
};
struct vmd_dev {
@ -295,6 +324,13 @@ static int vmd_msi_alloc(struct irq_domain *domain, unsigned int virq,
INIT_LIST_HEAD(&vmdirq->node);
vmdirq->irq = vmd_next_irq(vmd, desc);
vmdirq->virq = virq + i;
if (vmd->features & VMD_FEAT_INTERRUPT_QUIRK) {
struct pci_dev *pdev = msi_desc_to_pci_dev(desc);
vmdirq->flush_addr = vmd_cfg_addr(vmd, pdev->bus,
pdev->devfn,
PCI_VENDOR_ID, 2);
}
irq_domain_set_info(domain, virq + i, vmdirq->irq->virq,
&vmd_msi_controller, vmdirq,
@ -756,8 +792,20 @@ static irqreturn_t vmd_irq(int irq, void *data)
int idx;
idx = srcu_read_lock(&irqs->srcu);
list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node)
list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) {
/*
* MTL016: the MSI may have outrun the initiating device's
* posted writes (e.g. its NVMe completion entry). A read
* that completes at the initiator flushes them, so the
* demuxed handler observes a coherent completion queue.
* The value is discarded; only the ordering matters.
*/
if (vmdirq->flush_addr) {
guard(raw_spinlock)(&irqs->vmd->cfg_lock);
readw(vmdirq->flush_addr);
}
generic_handle_irq(vmdirq->virq);
}
srcu_read_unlock(&irqs->srcu, idx);
return IRQ_HANDLED;
@ -788,6 +836,7 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd)
return err;
INIT_LIST_HEAD(&vmd->irqs[i].irq_list);
vmd->irqs[i].vmd = vmd;
vmd->irqs[i].virq = pci_irq_vector(dev, i);
err = devm_request_irq(&dev->dev, vmd->irqs[i].virq,
vmd_irq, IRQF_NO_THREAD,
@ -1250,9 +1299,9 @@ static const struct pci_device_id vmd_ids[] = {
{PCI_VDEVICE(INTEL, 0xa77f),
.driver_data = VMD_FEATS_CLIENT,},
{PCI_VDEVICE(INTEL, 0x7d0b),
.driver_data = VMD_FEATS_CLIENT,},
.driver_data = VMD_FEATS_CLIENT | VMD_FEAT_INTERRUPT_QUIRK,},
{PCI_VDEVICE(INTEL, 0xad0b),
.driver_data = VMD_FEATS_CLIENT,},
.driver_data = VMD_FEATS_CLIENT | VMD_FEAT_INTERRUPT_QUIRK,},
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B),
.driver_data = VMD_FEATS_CLIENT,},
{PCI_VDEVICE(INTEL, 0xb60b),

View File

@ -208,8 +208,9 @@ static int pci_doe_sysfs_feature_populate(struct pci_dev *pdev,
pci_warn(pdev, "Failed adding %s to sysfs group\n",
attrs[i].attr.name);
goto fail;
} else
kfree(attrs[i].attr.name);
}
kfree(attrs[i].attr.name);
attrs[i].attr.name = NULL;
}
}

View File

@ -41,6 +41,7 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/pci-ep-msi.h>
@ -132,7 +133,7 @@ struct epf_ntb_ctrl {
} __packed;
struct epf_ntb {
struct ntb_dev ntb;
struct ntb_dev *ntb;
struct pci_epf *epf;
struct config_group group;
@ -147,7 +148,11 @@ struct epf_ntb {
u16 vntb_pid;
u16 vntb_vid;
bool linkup;
/* Serialize HOST and VHOST link state changes. */
struct mutex link_lock;
bool host_link_enabled;
bool vhost_link_enabled;
bool peer_msix;
/*
* True when doorbells are interrupt-driven (MSI or embedded), false
@ -155,6 +160,7 @@ struct epf_ntb {
*/
bool msi_doorbell;
u32 spad_size;
u32 msix_table_offset;
enum pci_barno epf_ntb_bar[VNTB_BAR_NUM];
@ -166,10 +172,15 @@ struct epf_ntb {
void __iomem *vpci_mw_addr[MAX_MW];
struct delayed_work cmd_handler;
struct pci_bus *vpci_bus;
};
#define to_epf_ntb(epf_group) container_of((epf_group), struct epf_ntb, group)
#define ntb_ndev(__ntb) container_of(__ntb, struct epf_ntb, ntb)
static struct epf_ntb *ntb_ndev(struct ntb_dev *ntb)
{
return ntb->pdev->sysdata;
}
static struct pci_epf_header epf_ntb_header = {
.vendorid = PCI_ANY_ID,
@ -178,24 +189,58 @@ static struct pci_epf_header epf_ntb_header = {
.interrupt_pin = PCI_INTERRUPT_INTA,
};
static void epf_ntb_update_link(struct epf_ntb *ntb)
{
u16 link_status = READ_ONCE(ntb->reg->link_status);
/* The link is usable only after both sides have enabled it. */
if (ntb->host_link_enabled && ntb->vhost_link_enabled)
link_status |= LINK_STATUS_UP;
else
link_status &= ~LINK_STATUS_UP;
WRITE_ONCE(ntb->reg->link_status, link_status);
}
static int epf_ntb_notify_link(struct ntb_dev *ndev)
{
struct epf_ntb *ntb = ntb_ndev(ndev);
struct pci_epf *epf = ntb->epf;
unsigned int irq_type;
int ret;
ntb_link_event(ndev);
irq_type = ntb->peer_msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI;
ret = pci_epc_raise_irq(epf->epc, epf->func_no, epf->vfunc_no,
irq_type, EPF_IRQ_LINK + 1);
if (ret)
dev_err(&epf->dev, "Failed to raise link event IRQ: %d\n", ret);
return ret;
}
/**
* epf_ntb_link_up() - Raise link_up interrupt to Virtual Host (VHOST)
* epf_ntb_set_link_enabled() - Set HOST link enablement
* @ntb: NTB device that facilitates communication between HOST and VHOST
* @link_up: true or false indicating Link is UP or Down
*
* Once NTB function in HOST invoke ntb_link_enable(),
* this NTB function driver will trigger a link event to VHOST.
* @enabled: true when the HOST has enabled the link
*
* Returns: Zero for success, or an error code in case of failure
*/
static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up)
static int epf_ntb_set_link_enabled(struct epf_ntb *ntb, bool enabled)
{
if (link_up)
ntb->reg->link_status |= LINK_STATUS_UP;
else
ntb->reg->link_status &= ~LINK_STATUS_UP;
bool notify;
scoped_guard(mutex, &ntb->link_lock) {
notify = ntb->host_link_enabled != enabled &&
ntb->vhost_link_enabled;
ntb->host_link_enabled = enabled;
epf_ntb_update_link(ntb);
}
if (notify)
return epf_ntb_notify_link(ntb->ntb);
ntb_link_event(&ntb->ntb);
return 0;
}
@ -271,6 +316,7 @@ static void epf_ntb_teardown_mw(struct epf_ntb *ntb, u32 mw)
*/
static void epf_ntb_cmd_handler(struct work_struct *work)
{
const struct pci_epc_features *epc_features;
struct epf_ntb_ctrl *ctrl;
u32 command, argument;
struct epf_ntb *ntb;
@ -284,7 +330,7 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
i++) {
if (ntb->epf_db[i]) {
atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db);
ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START);
ntb_db_event(ntb->ntb, i - EPF_IRQ_DB_START);
ntb->epf_db[i] = 0;
}
}
@ -303,6 +349,15 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
switch (command) {
case COMMAND_CONFIGURE_DOORBELL:
epc_features = pci_epc_get_features(ntb->epf->epc,
ntb->epf->func_no,
ntb->epf->vfunc_no);
if (((argument & MSIX_ENABLE) && !epc_features->msix_capable) ||
(!(argument & MSIX_ENABLE) && !epc_features->msi_capable)) {
ctrl->command_status = COMMAND_STATUS_ERROR;
break;
}
ntb->peer_msix = argument & MSIX_ENABLE;
ctrl->command_status = COMMAND_STATUS_OK;
break;
case COMMAND_TEARDOWN_DOORBELL:
@ -320,16 +375,14 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
ctrl->command_status = COMMAND_STATUS_OK;
break;
case COMMAND_LINK_UP:
ntb->linkup = true;
ret = epf_ntb_link_up(ntb, true);
ret = epf_ntb_set_link_enabled(ntb, true);
if (ret < 0)
ctrl->command_status = COMMAND_STATUS_ERROR;
else
ctrl->command_status = COMMAND_STATUS_OK;
goto reset_handler;
case COMMAND_LINK_DOWN:
ntb->linkup = false;
ret = epf_ntb_link_up(ntb, false);
ret = epf_ntb_set_link_enabled(ntb, false);
if (ret < 0)
ctrl->command_status = COMMAND_STATUS_ERROR;
else
@ -348,12 +401,18 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
static irqreturn_t epf_ntb_doorbell_handler(int irq, void *data)
{
struct epf_ntb *ntb = data;
struct ntb_dev *ndev;
int i;
/* Pair with smp_store_release() in pci_vntb_probe() */
ndev = smp_load_acquire(&ntb->ntb);
if (!ndev)
return IRQ_HANDLED;
for (i = EPF_IRQ_DB_START; i < ntb->db_count; i++)
if (irq == ntb->epf->db_msg[i].virq) {
atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db);
ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START);
ntb_db_event(ndev, i - EPF_IRQ_DB_START);
}
return IRQ_HANDLED;
@ -440,8 +499,9 @@ static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
* @ntb: NTB device that facilitates communication between HOST and VHOST
*
* Allocate the Local Memory mentioned in the above diagram. The size of
* CONFIG REGION is sizeof(struct epf_ntb_ctrl) and size of SCRATCHPAD REGION
* is obtained from "spad-count" configfs entry.
* CONFIG REGION is sizeof(struct epf_ntb_ctrl), plus space for the MSI-X
* Table and PBA if supported. The size of SCRATCHPAD REGION is obtained
* from "spad-count" configfs entry.
*
* Returns: Zero for success, or an error code in case of failure
*/
@ -462,6 +522,12 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
spad_count = ntb->spad_count;
ctrl_size = ALIGN(sizeof(struct epf_ntb_ctrl), sizeof(u32));
if (epc_features->msix_capable) {
ntb->msix_table_offset = ALIGN(ctrl_size, 8);
ctrl_size = ntb->msix_table_offset +
ntb->db_count * PCI_MSIX_ENTRY_SIZE +
BITS_TO_U64(ntb->db_count) * sizeof(u64);
}
spad_size = 2 * spad_count * sizeof(u32);
base = pci_epf_alloc_space(epf, ctrl_size + spad_size,
@ -502,12 +568,14 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
{
const struct pci_epc_features *epc_features;
struct pci_epf *epf = ntb->epf;
struct device *dev;
int ret;
dev = &ntb->epf->dev;
dev = &epf->dev;
epc_features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no);
epc_features = pci_epc_get_features(epf->epc, epf->func_no,
epf->vfunc_no);
if (!(epc_features->msix_capable || epc_features->msi_capable)) {
dev_err(dev, "MSI or MSI-X is required for doorbell\n");
@ -521,16 +589,24 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
}
if (epc_features->msi_capable) {
ret = pci_epc_set_msi(ntb->epf->epc,
ntb->epf->func_no,
ntb->epf->vfunc_no,
16);
ret = pci_epc_set_msi(epf->epc, epf->func_no, epf->vfunc_no, 16);
if (ret) {
dev_err(dev, "MSI configuration failed\n");
return ret;
}
}
if (epc_features->msix_capable) {
ret = pci_epc_set_msix(epf->epc, epf->func_no, epf->vfunc_no,
ntb->db_count,
ntb->epf_ntb_bar[BAR_CONFIG],
ntb->msix_table_offset);
if (ret) {
dev_err(dev, "MSI-X configuration failed\n");
return ret;
}
}
return 0;
}
@ -985,7 +1061,6 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
}
INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler);
queue_work(kpcintb_workqueue, &ntb->cmd_handler.work);
atomic64_set(&ntb->peer_db_pending, 0);
enable_work(&ntb->peer_db_work);
@ -1332,14 +1407,20 @@ static int vpci_scan_bus(void *sysdata)
struct pci_bus *vpci_bus;
struct epf_ntb *ndev = sysdata;
pci_lock_rescan_remove();
vpci_bus = pci_scan_bus(ndev->vbus_number, &vpci_ops, sysdata);
if (!vpci_bus) {
pr_err("create pci bus failed\n");
pci_unlock_rescan_remove();
return -EINVAL;
}
ndev->vpci_bus = vpci_bus;
pci_bus_add_devices(vpci_bus);
pci_unlock_rescan_remove();
return 0;
}
@ -1420,14 +1501,15 @@ static int vntb_epf_mw_set_trans(struct ntb_dev *ndev, int pidx, int idx,
int ret;
struct device *dev;
dev = &ntb->ntb.dev;
dev = &ndev->dev;
barno = ntb->epf_ntb_bar[BAR_MW1 + idx];
epf_bar = &ntb->epf->bar[barno];
epf_bar->phys_addr = addr;
epf_bar->barno = barno;
epf_bar->size = size;
ret = pci_epc_set_bar(ntb->epf->epc, 0, 0, epf_bar);
ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
ntb->epf->vfunc_no, epf_bar);
if (ret) {
dev_err(dev, "failure set mw trans\n");
return ret;
@ -1455,11 +1537,29 @@ static int vntb_epf_peer_mw_get_addr(struct ntb_dev *ndev, int idx,
return 0;
}
static int vntb_epf_set_link_enabled(struct ntb_dev *ndev, bool enabled)
{
struct epf_ntb *ntb = ntb_ndev(ndev);
bool notify;
scoped_guard(mutex, &ntb->link_lock) {
notify = ntb->vhost_link_enabled != enabled &&
ntb->host_link_enabled;
ntb->vhost_link_enabled = enabled;
epf_ntb_update_link(ntb);
}
if (notify)
return epf_ntb_notify_link(ndev);
return 0;
}
static int vntb_epf_link_enable(struct ntb_dev *ntb,
enum ntb_speed max_speed,
enum ntb_width max_width)
{
return 0;
return vntb_epf_set_link_enabled(ntb, true);
}
static u32 vntb_epf_spad_read(struct ntb_dev *ndev, int idx)
@ -1512,6 +1612,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work)
struct epf_ntb *ntb = container_of(work, struct epf_ntb, peer_db_work);
struct pci_epf *epf = ntb->epf;
unsigned int budget = VNTB_PEER_DB_WORK_BUDGET;
unsigned int irq_type;
u8 func_no, vfunc_no;
unsigned int db_bit;
u32 interrupt_num;
@ -1523,6 +1624,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work)
func_no = epf->func_no;
vfunc_no = epf->vfunc_no;
irq_type = ntb->peer_msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI;
/*
* Drain doorbells from peer_db_pending in snapshots (atomic64_xchg()).
@ -1536,7 +1638,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work)
while (db_bits) {
/*
* pci_epc_raise_irq() for MSI expects a 1-based
* pci_epc_raise_irq() for MSI/MSI-X expects a 1-based
* interrupt number. The first usable doorbell starts
* at EPF_IRQ_DB_START in the legacy slot layout.
*
@ -1556,9 +1658,9 @@ static void vntb_epf_peer_db_work(struct work_struct *work)
db_bits &= ~BIT_ULL(db_bit);
ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no,
PCI_IRQ_MSI, interrupt_num);
irq_type, interrupt_num);
if (ret)
dev_err(&ntb->ntb.dev,
dev_err(&epf->dev,
"Failed to raise IRQ for interrupt_num %u: %d\n",
interrupt_num, ret);
}
@ -1619,7 +1721,7 @@ static u64 vntb_epf_link_is_up(struct ntb_dev *ndev,
{
struct epf_ntb *ntb = ntb_ndev(ndev);
return ntb->reg->link_status;
return READ_ONCE(ntb->reg->link_status);
}
static int vntb_epf_db_clear_mask(struct ntb_dev *ndev, u64 db_bits)
@ -1637,7 +1739,7 @@ static int vntb_epf_db_clear(struct ntb_dev *ndev, u64 db_bits)
static int vntb_epf_link_disable(struct ntb_dev *ntb)
{
return 0;
return vntb_epf_set_link_enabled(ntb, false);
}
static struct device *vntb_epf_get_dma_dev(struct ntb_dev *ndev)
@ -1676,13 +1778,18 @@ static const struct ntb_dev_ops vntb_epf_ops = {
static int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int ret;
struct epf_ntb *ndev = (struct epf_ntb *)pdev->sysdata;
struct device *dev = &pdev->dev;
struct ntb_dev *ntb;
int ret;
ndev->ntb.pdev = pdev;
ndev->ntb.topo = NTB_TOPO_NONE;
ndev->ntb.ops = &vntb_epf_ops;
ntb = devm_kzalloc(dev, sizeof(*ntb), GFP_KERNEL);
if (!ntb)
return -ENOMEM;
ntb->pdev = pdev;
ntb->topo = NTB_TOPO_NONE;
ntb->ops = &vntb_epf_ops;
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
@ -1690,16 +1797,41 @@ static int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return ret;
}
ret = ntb_register_device(&ndev->ntb);
ret = ntb_register_device(ntb);
if (ret) {
dev_err(dev, "Failed to register NTB device\n");
return ret;
}
/* Publish after ntb_register_device() succeeds */
smp_store_release(&ndev->ntb, ntb);
queue_delayed_work(kpcintb_workqueue, &ndev->cmd_handler, 0);
dev_dbg(dev, "PCI Virtual NTB driver loaded\n");
return 0;
}
static void pci_vntb_remove(struct pci_dev *pdev)
{
struct epf_ntb *ndev = pdev->sysdata;
struct ntb_dev *ntb;
unsigned int i;
/* Stop the work reader, then close and drain the IRQ gate */
cancel_delayed_work_sync(&ndev->cmd_handler);
ntb = xchg(&ndev->ntb, NULL);
if (ndev->msi_doorbell)
for (i = 0; i < ndev->db_count; i++) {
if (epf_ntb_db_irq_is_duplicated(ndev->epf, i))
continue;
synchronize_irq(ndev->epf->db_msg[i].virq);
}
ntb_unregister_device(ntb);
}
static struct pci_device_id pci_vntb_table[] = {
{
PCI_DEVICE(0xffff, 0xffff),
@ -1711,6 +1843,7 @@ static struct pci_driver vntb_pci_driver = {
.name = "pci-vntb",
.id_table = pci_vntb_table,
.probe = pci_vntb_probe,
.remove = pci_vntb_remove,
};
/* ============ PCIe EPF Driver Bind ====================*/
@ -1749,6 +1882,10 @@ static int epf_ntb_bind(struct pci_epf *epf)
goto err_bar_alloc;
}
ntb->host_link_enabled = false;
ntb->vhost_link_enabled = false;
ntb->reg->link_status = 0;
ret = epf_ntb_epc_init(ntb);
if (ret) {
dev_err(dev, "Failed to initialize EPC\n");
@ -1791,12 +1928,25 @@ static int epf_ntb_bind(struct pci_epf *epf)
*/
static void epf_ntb_unbind(struct pci_epf *epf)
{
struct pci_host_bridge *bridge;
struct epf_ntb *ntb = epf_get_drvdata(epf);
pci_unregister_driver(&vntb_pci_driver);
if (ntb->vpci_bus) {
bridge = to_pci_host_bridge(ntb->vpci_bus->bridge);
pci_lock_rescan_remove();
pci_stop_root_bus(ntb->vpci_bus);
pci_remove_root_bus(ntb->vpci_bus);
ntb->vpci_bus = NULL;
pci_unlock_rescan_remove();
pci_free_host_bridge(bridge);
}
epf_ntb_epc_cleanup(ntb);
epf_ntb_config_spad_bar_free(ntb);
pci_unregister_driver(&vntb_pci_driver);
}
// EPF driver probe
@ -1821,7 +1971,7 @@ static int epf_ntb_probe(struct pci_epf *epf,
{
struct epf_ntb *ntb;
struct device *dev;
int i;
int ret, i;
dev = &epf->dev;
@ -1832,6 +1982,9 @@ static int epf_ntb_probe(struct pci_epf *epf,
epf->header = &epf_ntb_header;
ntb->epf = epf;
ntb->vbus_number = 0xff;
ret = devm_mutex_init(dev, &ntb->link_lock);
if (ret)
return ret;
INIT_WORK(&ntb->peer_db_work, vntb_epf_peer_db_work);
disable_work(&ntb->peer_db_work);

View File

@ -10,7 +10,6 @@
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/msi.h>
@ -112,8 +111,11 @@ static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db)
*
* Still, pci_epf_alloc_doorbell() allows requesting multiple doorbells.
* For such backends we replicate the same address/data for each entry
* and mark the IRQ as shared (IRQF_SHARED). Consumers must treat them
* as equivalent "kick" doorbells.
* as equivalent "kick" doorbells. Consumers must request each distinct
* IRQ only once.
*
* Keep the IRQ exclusive because the source cannot identify which EPF
* rang the doorbell.
*/
for (i = 0; i < num_db; i++)
msg[i] = (struct pci_epf_doorbell_msg) {
@ -121,7 +123,6 @@ static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db)
.msg.address_hi = (u32)(addr >> 32),
.msg.data = doorbell->u.db_mmio.data,
.virq = doorbell->u.db_mmio.irq,
.irq_flags = IRQF_SHARED,
.type = PCI_EPF_DOORBELL_EMBEDDED,
.bar = doorbell->bar,
.offset = (doorbell->bar == NO_BAR) ? 0 :
@ -198,28 +199,31 @@ static int pci_epf_alloc_doorbell_msi(struct pci_epf *epf, u16 num_db)
int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
{
struct pci_epc *epc = epf->epc;
struct pci_epf *first_epf;
struct device *dev = &epf->dev;
int ret;
/* TODO: Multi-EPF support */
if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) {
dev_err(dev, "Doorbell doesn't support multiple EPF\n");
return -EINVAL;
}
if (epf->db_msg)
return -EBUSY;
ret = pci_epf_alloc_doorbell_msi(epf, num_db);
if (!ret)
return 0;
/*
* Fall back to embedded doorbell only when platform MSI is unavailable
* for this EPC.
* The MSI-backed doorbell path currently targets the first EPF attached
* to the EPC. Let non-first EPFs try the embedded doorbell instead.
*/
if (ret != -ENODEV)
return ret;
first_epf = list_first_entry_or_null(&epc->pci_epf, struct pci_epf,
list);
if (first_epf == epf) {
ret = pci_epf_alloc_doorbell_msi(epf, num_db);
if (!ret)
return 0;
/*
* Fall back to embedded doorbell only when platform MSI is
* unavailable for this EPC.
*/
if (ret != -ENODEV)
return ret;
}
ret = pci_epf_alloc_doorbell_embedded(epf, num_db);
if (ret) {

View File

@ -38,7 +38,7 @@ int pciehp_poll_time;
* not really modular, but the easiest way to keep compat with existing
* bootargs behaviour is to continue using module_param here.
*/
module_param(pciehp_poll_mode, bool, 0644);
module_param(pciehp_poll_mode, bool, 0444);
module_param(pciehp_poll_time, int, 0644);
MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
@ -258,7 +258,7 @@ static bool pme_is_native(struct pcie_device *dev)
const struct pci_host_bridge *host;
host = pci_find_host_bridge(dev->port->bus);
return pcie_ports_native || host->native_pme;
return host->native_pme;
}
static void pciehp_disable_interrupt(struct pcie_device *dev)

View File

@ -179,29 +179,6 @@ bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
return cmd & PCI_SRIOV_CTRL_MSE;
}
static void pci_read_vf_config_common(struct pci_dev *virtfn)
{
struct pci_dev *physfn = virtfn->physfn;
/*
* Some config registers are the same across all associated VFs.
* Read them once from VF0 so we can skip reading them from the
* other VFs.
*
* PCIe r4.0, sec 9.3.4.1, technically doesn't require all VFs to
* have the same Revision ID and Subsystem ID, but we assume they
* do.
*/
pci_read_config_dword(virtfn, PCI_CLASS_REVISION,
&physfn->sriov->class);
pci_read_config_byte(virtfn, PCI_HEADER_TYPE,
&physfn->sriov->hdr_type);
pci_read_config_word(virtfn, PCI_SUBSYSTEM_VENDOR_ID,
&physfn->sriov->subsystem_vendor);
pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID,
&physfn->sriov->subsystem_device);
}
int pci_iov_sysfs_link(struct pci_dev *dev,
struct pci_dev *virtfn, int id)
{
@ -329,8 +306,6 @@ static struct pci_dev *pci_iov_scan_device(struct pci_dev *dev, int id,
virtfn->physfn = pci_dev_get(dev);
virtfn->no_command_memory = 1;
if (id == 0)
pci_read_vf_config_common(virtfn);
rc = pci_setup_device(virtfn);
if (rc) {

View File

@ -236,9 +236,8 @@ static void pci_p2pdma_release(void *data)
return;
/* Flush and disable pci_alloc_p2p_mem() */
pdev->p2pdma = NULL;
if (p2pdma->pool)
synchronize_rcu();
RCU_INIT_POINTER(pdev->p2pdma, NULL);
synchronize_rcu();
xa_destroy(&p2pdma->map_types);
if (!p2pdma->pool)
@ -440,8 +439,8 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
goto pgmap_free;
}
error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_unmap_mappings,
p2p_pgmap);
error = devm_add_action(&pdev->dev, pci_p2pdma_unmap_mappings,
p2p_pgmap);
if (error)
goto pages_free;
@ -451,13 +450,15 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
range_len(&pgmap->range), dev_to_node(&pdev->dev),
&pgmap->ref);
if (error)
goto pages_free;
goto mappings_remove;
pci_info(pdev, "added peer-to-peer DMA memory %#llx-%#llx\n",
pgmap->range.start, pgmap->range.end);
return 0;
mappings_remove:
devm_remove_action(&pdev->dev, pci_p2pdma_unmap_mappings, p2p_pgmap);
pages_free:
devm_memunmap_pages(&pdev->dev, pgmap);
pgmap_free:
@ -542,9 +543,13 @@ static const struct pci_p2pdma_whitelist_entry {
/* Intel Xeon E5/Core i7 */
{PCI_VENDOR_ID_INTEL, 0x3c00, REQ_SAME_HOST_BRIDGE},
{PCI_VENDOR_ID_INTEL, 0x3c01, REQ_SAME_HOST_BRIDGE},
/* Intel Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D */
{PCI_VENDOR_ID_INTEL, 0x6f00, REQ_SAME_HOST_BRIDGE},
/* Intel Xeon E7 v3/Xeon E5 v3/Core i7 */
{PCI_VENDOR_ID_INTEL, 0x2f00, REQ_SAME_HOST_BRIDGE},
{PCI_VENDOR_ID_INTEL, 0x2f01, REQ_SAME_HOST_BRIDGE},
/* Intel Haswell (client) */
{PCI_VENDOR_ID_INTEL, 0x0c08, REQ_SAME_HOST_BRIDGE},
/* Intel Skylake-E */
{PCI_VENDOR_ID_INTEL, 0x2030, 0},
{PCI_VENDOR_ID_INTEL, 0x2031, 0},
@ -569,6 +574,14 @@ static const struct pci_p2pdma_whitelist_entry {
{PCI_VENDOR_ID_NVIDIA, 0x2f96, 0},
{PCI_VENDOR_ID_NVIDIA, 0x2f97, 0},
{PCI_VENDOR_ID_NVIDIA, 0x2f98, 0},
/* Zhaoxin KX-6000/KH-40000/KX-6000G/KX-7000/KH-50000 */
{PCI_VENDOR_ID_ZHAOXIN, 0x1003, REQ_SAME_HOST_BRIDGE},
{PCI_VENDOR_ID_ZHAOXIN, 0x1005, REQ_SAME_HOST_BRIDGE},
{PCI_VENDOR_ID_ZHAOXIN, 0x1006, REQ_SAME_HOST_BRIDGE},
{PCI_VENDOR_ID_ZHAOXIN, 0x1007, REQ_SAME_HOST_BRIDGE},
{PCI_VENDOR_ID_ZHAOXIN, 0x1008, 0},
/* Alibaba T-HEAD Yitian 710 CPU */
{PCI_VENDOR_ID_ALIBABA, 0x8000, 0},
{}
};
@ -708,7 +721,6 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
{
enum pci_p2pdma_map_type map_type = PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
struct pci_dev *a = provider, *b = client, *bb;
bool acs_redirects = false;
struct pci_p2pdma *p2pdma;
struct seq_buf acs_list;
int acs_cnt = 0;
@ -771,17 +783,18 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
}
if (verbose) {
acs_list.buffer[acs_list.len-1] = 0; /* drop final semicolon */
/* Drop the final semicolon; the list is not empty here */
if (!seq_buf_has_overflowed(&acs_list))
acs_list.buffer[acs_list.len - 1] = '\0';
pci_warn(client, "ACS redirect is set between the client and provider (%s)\n",
pci_name(provider));
pci_warn(client, "to disable ACS redirect for this path, add the kernel parameter: pci=disable_acs_redir=%s\n",
acs_list.buffer);
seq_buf_str(&acs_list));
}
acs_redirects = true;
map_through_host_bridge:
if (!cpu_supports_p2pdma() &&
!host_bridge_whitelist(provider, client, acs_redirects)) {
!host_bridge_whitelist(provider, client, verbose)) {
if (verbose)
pci_warn(client, "cannot be used for peer-to-peer DMA as the client and provider (%s) do not share an upstream bridge or whitelisted host bridge\n",
pci_name(provider));
@ -867,7 +880,13 @@ static bool pci_has_p2pmem(struct pci_dev *pdev)
rcu_read_lock();
p2pdma = rcu_dereference(pdev->p2pdma);
res = p2pdma && p2pdma->p2pmem_published;
/*
* The callers hand the result to pci_alloc_p2pmem(), so only a
* provider backed by a pool is of any use here. pcim_p2pdma_init()
* creates providers without one.
*/
res = p2pdma && p2pdma->pool && p2pdma->p2pmem_published;
rcu_read_unlock();
return res;

View File

@ -812,9 +812,6 @@ bool pciehp_is_native(struct pci_dev *bridge)
if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
return false;
if (pcie_ports_native)
return true;
host = pci_find_host_bridge(bridge->bus);
return host->native_pcie_hotplug;
}

View File

@ -818,6 +818,52 @@ static void pci_pm_complete(struct device *dev)
#endif /* !CONFIG_PM_SLEEP */
#if defined(CONFIG_SUSPEND) || defined(CONFIG_HIBERNATE_CALLBACKS)
/**
* pci_pm_suspend_noirq_common - prepare a device to enter a low-power state
* @pci_dev: pci device
*
* Save the device state and decide whether bus-level power management should
* skipped. Returns true if bus-level power management should be skipped,
* false otherwise.
*/
static bool pci_pm_suspend_noirq_common(struct pci_dev *pci_dev)
{
if (!pci_dev->state_saved) {
pci_save_state(pci_dev);
/*
* If the device is a bridge with a child in D0 below it,
* it needs to stay in D0, so check skip_bus_pm to avoid
* putting it into a low-power state in that case.
*/
if (!pci_dev->skip_bus_pm && pci_power_manageable(pci_dev))
pci_prepare_to_sleep(pci_dev);
}
pci_dbg(pci_dev, "PCI PM: Sleep power state: %s\n",
pci_power_name(pci_dev->current_state));
if (pci_dev->current_state == PCI_D0) {
pci_dev->skip_bus_pm = true;
/*
* Per PCI PM r1.2, table 6-1, a bridge must be in D0 if any
* downstream device is in D0, so avoid changing the power state
* of the parent bridge by setting the skip_bus_pm flag for it.
*/
if (pci_dev->bus->self)
pci_dev->bus->self->skip_bus_pm = true;
}
if (pci_dev->skip_bus_pm && pm_suspend_no_platform()) {
pci_dbg(pci_dev, "PCI PM: Skipped\n");
return true;
}
return false;
}
#endif /* CONFIG_SUSPEND || CONFIG_HIBERNATE_CALLBACKS */
#ifdef CONFIG_SUSPEND
static void pcie_pme_root_status_cleanup(struct pci_dev *pci_dev)
{
@ -907,6 +953,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
bool skip_bus_pm;
if (dev_pm_skip_suspend(dev))
return 0;
@ -937,36 +984,10 @@ static int pci_pm_suspend_noirq(struct device *dev)
}
}
if (!pci_dev->state_saved) {
pci_save_state(pci_dev);
skip_bus_pm = pci_pm_suspend_noirq_common(pci_dev);
/*
* If the device is a bridge with a child in D0 below it,
* it needs to stay in D0, so check skip_bus_pm to avoid
* putting it into a low-power state in that case.
*/
if (!pci_dev->skip_bus_pm && pci_power_manageable(pci_dev))
pci_prepare_to_sleep(pci_dev);
}
pci_dbg(pci_dev, "PCI PM: Suspend power state: %s\n",
pci_power_name(pci_dev->current_state));
if (pci_dev->current_state == PCI_D0) {
pci_dev->skip_bus_pm = true;
/*
* Per PCI PM r1.2, table 6-1, a bridge must be in D0 if any
* downstream device is in D0, so avoid changing the power state
* of the parent bridge by setting the skip_bus_pm flag for it.
*/
if (pci_dev->bus->self)
pci_dev->bus->self->skip_bus_pm = true;
}
if (pci_dev->skip_bus_pm && pm_suspend_no_platform()) {
pci_dbg(pci_dev, "PCI PM: Skipped\n");
if (skip_bus_pm)
goto Fixup;
}
set_unknown:
pci_pm_set_unknown_state(pci_dev);
@ -1196,6 +1217,8 @@ static int pci_pm_poweroff(struct device *dev)
struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
pci_dev->skip_bus_pm = false;
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_suspend(dev, PMSG_HIBERNATE);
@ -1238,6 +1261,7 @@ static int pci_pm_poweroff_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
bool skip_bus_pm;
if (dev_pm_skip_suspend(dev))
return 0;
@ -1251,16 +1275,26 @@ static int pci_pm_poweroff_noirq(struct device *dev)
}
if (pm->poweroff_noirq) {
pci_power_t prev = pci_dev->current_state;
int error;
error = pm->poweroff_noirq(dev);
suspend_report_result(dev, pm->poweroff_noirq, error);
if (error)
return error;
if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
&& pci_dev->current_state != PCI_UNKNOWN) {
pci_WARN_ONCE(pci_dev, pci_dev->current_state != prev,
"PCI PM: State of device not saved by %pS\n",
pm->poweroff_noirq);
goto Fixup;
}
}
if (!pci_dev->state_saved && !pci_has_subordinate(pci_dev))
pci_prepare_to_sleep(pci_dev);
skip_bus_pm = pci_pm_suspend_noirq_common(pci_dev);
if (skip_bus_pm)
goto Fixup;
/*
* The reason for doing this here is the same as for the analogous code
@ -1269,6 +1303,7 @@ static int pci_pm_poweroff_noirq(struct device *dev)
if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)
pci_write_config_word(pci_dev, PCI_COMMAND, 0);
Fixup:
pci_fixup_device(pci_fixup_suspend_late, pci_dev);
return 0;
@ -1278,10 +1313,15 @@ static int pci_pm_restore_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
pci_power_t prev_state = pci_dev->current_state;
bool skip_bus_pm = pci_dev->skip_bus_pm;
pci_pm_default_resume_early(pci_dev);
pci_fixup_device(pci_fixup_resume_early, pci_dev);
if (!skip_bus_pm && prev_state == PCI_D3cold)
pci_pm_bridge_power_up_actions(pci_dev);
if (pci_has_legacy_pm_support(pci_dev))
return 0;

View File

@ -144,7 +144,7 @@ static int dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
int len;
len = utf16s_to_utf8s((const wchar_t *)obj->buffer.pointer,
obj->buffer.length,
obj->buffer.length / sizeof(wchar_t),
UTF16_LITTLE_ENDIAN,
buf, PAGE_SIZE - 1);
buf[len++] = '\n';
@ -157,40 +157,53 @@ static int dsm_get_label(struct device *dev, char *buf,
{
acpi_handle handle = ACPI_HANDLE(dev);
union acpi_object *obj, *tmp;
int len = 0;
int len;
if (!handle)
return -1;
return -ENODEV;
obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 0x2,
DSM_PCI_DEVICE_NAME, NULL);
if (!obj)
return -1;
return -EIO;
tmp = obj->package.elements;
if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 2 &&
tmp[0].type == ACPI_TYPE_INTEGER &&
(tmp[1].type == ACPI_TYPE_STRING ||
tmp[1].type == ACPI_TYPE_BUFFER)) {
/*
* The second string element is optional even when
* this _DSM is implemented; when not implemented,
* this entry must return a null string.
*/
if (attr == ACPI_ATTR_INDEX_SHOW) {
len = sysfs_emit(buf, "%llu\n", tmp->integer.value);
} else if (attr == ACPI_ATTR_LABEL_SHOW) {
if (tmp[1].type == ACPI_TYPE_STRING)
len = sysfs_emit(buf, "%s\n",
tmp[1].string.pointer);
else if (tmp[1].type == ACPI_TYPE_BUFFER)
len = dsm_label_utf16s_to_utf8s(tmp + 1, buf);
}
if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 2) {
len = -EIO;
goto out;
}
tmp = obj->package.elements;
if (tmp[0].type != ACPI_TYPE_INTEGER) {
len = -EIO;
goto out;
}
if (attr == ACPI_ATTR_INDEX_SHOW) {
len = sysfs_emit(buf, "%llu\n", tmp[0].integer.value);
goto out;
}
/*
* Per PCI Firmware r3.3, sec 4.6.7, the device name is optional
* even when this _DSM is implemented. When not implemented, this
* entry must return a NULL string.
*/
switch (tmp[1].type) {
case ACPI_TYPE_STRING:
len = sysfs_emit(buf, "%s\n", tmp[1].string.pointer);
break;
case ACPI_TYPE_BUFFER:
len = dsm_label_utf16s_to_utf8s(&tmp[1], buf);
break;
default:
len = -EIO;
break;
}
out:
ACPI_FREE(obj);
return len > 0 ? len : -1;
return len;
}
static ssize_t label_show(struct device *dev, struct device_attribute *attr,
@ -217,11 +230,42 @@ static umode_t acpi_attr_is_visible(struct kobject *kobj, struct attribute *a,
int n)
{
struct device *dev = kobj_to_dev(kobj);
union acpi_object *obj, *tmp;
umode_t mode = 0;
if (!device_has_acpi_name(dev))
return 0;
return a->mode;
/*
* The bitmap from _DSM function 0 only advertises function 7,
* and whether the returned object can be parsed is a separate
* question. Evaluate it and expose each attribute only if the
* element it exports has one of the types the read path
* accepts, mirroring the checks in dsm_get_label().
*/
obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &pci_acpi_dsm_guid, 0x2,
DSM_PCI_DEVICE_NAME, NULL);
if (!obj)
return 0;
if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 2)
goto out;
tmp = obj->package.elements;
if (tmp[0].type != ACPI_TYPE_INTEGER)
goto out;
if (a == &dev_attr_acpi_index.attr)
mode = a->mode;
else if (a == &dev_attr_label.attr &&
(tmp[1].type == ACPI_TYPE_STRING ||
tmp[1].type == ACPI_TYPE_BUFFER))
mode = a->mode;
out:
ACPI_FREE(obj);
return mode;
}
const struct attribute_group pci_dev_acpi_attr_group = {

View File

@ -1212,6 +1212,9 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
if (port + count - 1 > pci_resource_end(pdev, bar))
return -EINVAL;
if (!IS_ALIGNED(port, count))
return -EINVAL;
switch (count) {
case 1:
if (write)

View File

@ -1743,6 +1743,24 @@ static void pci_restore_pcie_state(struct pci_dev *dev)
pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]);
}
/*
* Update the saved copy of the Device Control register after changing it, so
* that pci_restore_state() doesn't put back a stale value. Device Control is
* the first register saved by pci_save_pcie_state().
*/
static void pcie_update_saved_devctl(struct pci_dev *dev, u16 clear, u16 set)
{
struct pci_cap_saved_state *save_state;
u16 *devctl;
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
if (!save_state)
return;
devctl = (u16 *)&save_state->cap.data[0];
*devctl = (*devctl & ~clear) | set;
}
static int pci_save_pcix_state(struct pci_dev *dev)
{
int pos;
@ -3262,7 +3280,15 @@ void pci_pm_init(struct pci_dev *dev)
}
poweron:
pci_pm_power_up_and_verify_state(dev);
pci_update_current_state(dev, PCI_UNKNOWN);
if (dev->current_state == PCI_D0) {
/* No need for full power-up, just sync-up platform state */
platform_pci_set_power_state(dev, PCI_D0);
} else {
/* Power up the device and restore BARs if necessary */
pci_set_full_power_state(dev, false);
}
pci_update_current_state(dev, PCI_D0);
pm_runtime_forbid(&dev->dev);
/*
@ -5990,8 +6016,12 @@ int pcie_set_mps(struct pci_dev *dev, int mps)
ret = pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_PAYLOAD, v);
if (ret)
return pcibios_err_to_errno(ret);
return pcibios_err_to_errno(ret);
pcie_update_saved_devctl(dev, PCI_EXP_DEVCTL_PAYLOAD, v);
return 0;
}
EXPORT_SYMBOL(pcie_set_mps);

View File

@ -40,6 +40,15 @@ struct pcie_tlp_log;
*/
#define PCIE_T_PERST_CLK_US 100
/*
* PERST# active time.
*
* See the "Power Sequencing and Reset Signal Timings" table of the PCI Express
* Card Electromechanical Specification, Revision 6.0, Section 2.11.2, Symbol
* "T_PERST".
*/
#define PCIE_T_PERST_US 100
/*
* PCIe r6.0, sec 5.3.3.2.1 <PME Synchronization>
* Recommends 1ms to 10ms timeout to check L2 ready.
@ -502,6 +511,7 @@ enum pci_bar_type {
pci_bar_mem64, /* A 64-bit memory BAR */
};
void devm_pci_alloc_host_bridge_release(void *data);
struct device *pci_get_host_bridge_device(struct pci_dev *dev);
void pci_put_host_bridge_device(struct device *dev);
@ -711,10 +721,6 @@ struct pci_sriov {
u16 driver_max_VFs; /* Max num VFs driver supports */
struct pci_dev *dev; /* Lowest numbered PF */
struct pci_dev *self; /* This PF */
u32 class; /* VF device */
u8 hdr_type; /* VF header type */
u16 subsystem_vendor; /* VF subsystem vendor */
u16 subsystem_device; /* VF subsystem device */
resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */
u16 vf_rebar_cap; /* VF Resizable BAR capability offset */
bool drivers_autoprobe; /* Auto probing of VFs by driver */

View File

@ -260,7 +260,7 @@ int pcie_aer_is_native(struct pci_dev *dev)
if (!dev->aer_cap)
return 0;
return pcie_ports_native || host->native_aer;
return host->native_aer;
}
EXPORT_SYMBOL_NS_GPL(pcie_aer_is_native, "CXL");
@ -1341,7 +1341,6 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
{
cxl_rch_handle_error(dev, info);
pci_aer_handle_error(dev, info);
pci_dev_put(dev);
}
#ifdef CONFIG_ACPI_APEI_PCIEAER
@ -1369,14 +1368,13 @@ static void aer_recover_work_func(struct work_struct *work)
while (kfifo_get(&aer_recover_ring, &entry)) {
pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus,
entry.devfn);
if (!pdev) {
if (!pdev)
pr_err_ratelimited("%04x:%02x:%02x.%x: no pci_dev found\n",
entry.domain, entry.bus,
PCI_SLOT(entry.devfn),
PCI_FUNC(entry.devfn));
continue;
}
pci_print_aer(pdev, entry.severity, entry.regs);
else
pci_print_aer(pdev, entry.severity, entry.regs);
regs = entry.regs;
if (entry.severity == AER_CORRECTABLE)
@ -1394,6 +1392,9 @@ static void aer_recover_work_func(struct work_struct *work)
ghes_estatus_pool_region_free((unsigned long)entry.regs,
sizeof(struct aer_capability_regs));
if (!pdev)
continue;
if (err) {
if (entry.severity == AER_NONFATAL)
pcie_do_recovery(pdev, pci_channel_io_normal,
@ -1414,6 +1415,24 @@ static void aer_recover_work_func(struct work_struct *work)
static DEFINE_SPINLOCK(aer_recover_ring_lock);
static DECLARE_WORK(aer_recover_work, aer_recover_work_func);
/**
* aer_recover_queue - queue an AER error record reported by firmware
* @domain: PCI domain (segment) of the device that reported the error
* @bus: bus number of the device that reported the error
* @devfn: encoded device and function number, as returned by PCI_DEVFN()
* @severity: AER_CORRECTABLE, AER_NONFATAL or AER_FATAL
* @aer_regs: snapshot of the device's AER Capability registers
*
* Queue an error record received from firmware through APEI GHES. The
* record is processed later from a workqueue, which logs the error and,
* for uncorrectable errors, attempts recovery of the device.
*
* Takes ownership of @aer_regs, which must have been allocated from
* ghes_estatus_pool with a size of sizeof(struct aer_capability_regs).
* The buffer is freed with ghes_estatus_pool_region_free() by the work
* item that processes the record, or immediately if the queue is full.
* The caller must not access or free @aer_regs after this call.
*/
void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
int severity, struct aer_capability_regs *aer_regs)
{
@ -1426,11 +1445,14 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
};
if (kfifo_in_spinlocked(&aer_recover_ring, &entry, 1,
&aer_recover_ring_lock))
&aer_recover_ring_lock)) {
schedule_work(&aer_recover_work);
else
} else {
pr_err("buffer overflow in recovery for %04x:%02x:%02x.%x\n",
domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
ghes_estatus_pool_region_free((unsigned long)aer_regs,
sizeof(struct aer_capability_regs));
}
}
EXPORT_SYMBOL_GPL(aer_recover_queue);
#endif
@ -1529,6 +1551,7 @@ static inline void aer_process_err_devices(struct aer_err_info *e_info)
for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
if (aer_get_device_error_info(e_info, i))
handle_error_source(e_info->dev[i], e_info);
pci_dev_put(e_info->dev[i]);
}
}
@ -1858,7 +1881,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
*/
aer = root ? root->aer_cap : 0;
if ((host->native_aer || pcie_ports_native) && aer)
if (host->native_aer && aer)
aer_disable_irq(root);
if (type == PCI_EXP_TYPE_RC_EC || type == PCI_EXP_TYPE_RC_END) {
@ -1873,7 +1896,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
pci_is_root_bus(dev->bus) ? "Root" : "Downstream", rc);
}
if ((host->native_aer || pcie_ports_native) && aer) {
if (host->native_aer && aer) {
/* Clear Root Error Status */
pci_read_config_dword(root, aer + PCI_ERR_ROOT_STATUS, &reg32);
pci_write_config_dword(root, aer + PCI_ERR_ROOT_STATUS, reg32);

View File

@ -31,7 +31,7 @@ static bool cxl_error_is_native(struct pci_dev *dev)
{
struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
return (pcie_ports_native || host->native_aer);
return host->native_aer;
}
static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data)

View File

@ -1504,16 +1504,23 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool locked
return -EINVAL;
/*
* A driver requested that ASPM be disabled on this device, but
* if we don't have permission to manage ASPM (e.g., on ACPI
* if we might not have permission to manage ASPM (e.g., on ACPI
* systems we have to observe the FADT ACPI_FADT_NO_ASPM bit and
* the _OSC method), we can't honor that request. Windows has
* a similar mechanism using "PciASPMOptOut", which is also
* ignored in this situation.
* the _OSC method), previously we chose to not honor disable
* request in that case. Windows has a similar mechanism using
* "PciASPMOptOut", which is also ignored in this situation.
*
* Not honoring the requests to disable ASPM, however, led to
* drivers forcing ASPM off on their own. As such changes of ASPM
* state are not tracked by this service driver, the state kept here
* became out of sync.
*
* Therefore, honor ASPM disable requests even when OS does not have
* ASPM control. Plain disable for ASPM is assumed to be slightly
* safer than fully managing it.
*/
if (aspm_disabled) {
pci_warn(pdev, "can't disable ASPM; OS doesn't have ASPM control\n");
return -EPERM;
}
if (aspm_disabled)
pci_warn(pdev, "OS doesn't have ASPM control, disabling ASPM anyway\n");
if (!locked)
down_read(&pci_bus_sem);
@ -1540,13 +1547,13 @@ int pci_disable_link_state_locked(struct pci_dev *pdev, int state)
EXPORT_SYMBOL(pci_disable_link_state_locked);
/**
* pci_disable_link_state - Disable device's link state, so the link will
* never enter specific states. Note that if the BIOS didn't grant ASPM
* control to the OS, this does nothing because we can't touch the LNKCTL
* register. Returns 0 or a negative errno.
*
* pci_disable_link_state - Disable device's link state
* @pdev: PCI device
* @state: ASPM link state to disable
*
* Disable device's link state so the link will never enter specific states.
*
* Return: 0 or a negative errno
*/
int pci_disable_link_state(struct pci_dev *pdev, int state)
{

View File

@ -273,7 +273,7 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
* it is responsible for clearing this status. In that case, the
* signaling device may not even be visible to the OS.
*/
if (host->native_aer || pcie_ports_native) {
if (host->native_aer) {
pcie_clear_device_status(dev);
pci_aer_clear_nonfatal_status(dev);
}

View File

@ -223,7 +223,7 @@ static int get_port_device_capability(struct pci_dev *dev)
if (dev->is_pciehp &&
(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) &&
(pcie_ports_native || host->native_pcie_hotplug)) {
host->native_pcie_hotplug) {
services |= PCIE_PORT_SERVICE_HP;
/*
@ -240,14 +240,14 @@ static int get_port_device_capability(struct pci_dev *dev)
if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) &&
dev->aer_cap && pci_aer_available() &&
(pcie_ports_native || host->native_aer))
host->native_aer)
services |= PCIE_PORT_SERVICE_AER;
#endif
/* Root Ports and Root Complex Event Collectors may generate PMEs */
if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) &&
(pcie_ports_native || host->native_pme)) {
host->native_pme) {
services |= PCIE_PORT_SERVICE_PME;
/*

View File

@ -25,8 +25,6 @@
#define PCIE_PORT_DEVICE_MAXSERVICES 5
extern bool pcie_ports_dpc_native;
#ifdef CONFIG_PCIEAER
int pcie_aer_init(void);
#else

View File

@ -658,6 +658,8 @@ static const struct device_type pci_host_bridge_type = {
static void pci_init_host_bridge(struct pci_host_bridge *bridge)
{
bool port_services = IS_ENABLED(CONFIG_PCIEPORTBUS);
INIT_LIST_HEAD(&bridge->windows);
INIT_LIST_HEAD(&bridge->dma_ranges);
INIT_LIST_HEAD(&bridge->ports);
@ -668,12 +670,12 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge)
* may implement its own AER handling and use _OSC to prevent the
* OS from interfering.
*/
bridge->native_aer = 1;
bridge->native_pcie_hotplug = 1;
bridge->native_aer = port_services;
bridge->native_pcie_hotplug = port_services;
bridge->native_shpc_hotplug = 1;
bridge->native_pme = 1;
bridge->native_pme = port_services;
bridge->native_ltr = 1;
bridge->native_dpc = 1;
bridge->native_dpc = port_services;
bridge->domain_nr = PCI_DOMAIN_NR_NOT_SET;
bridge->native_cxl_error = 1;
bridge->dev.type = &pci_host_bridge_type;
@ -696,10 +698,11 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
}
EXPORT_SYMBOL(pci_alloc_host_bridge);
static void devm_pci_alloc_host_bridge_release(void *data)
void devm_pci_alloc_host_bridge_release(void *data)
{
pci_free_host_bridge(data);
}
EXPORT_SYMBOL(devm_pci_alloc_host_bridge_release);
struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev,
size_t priv)
@ -1651,6 +1654,8 @@ void set_pcie_port_type(struct pci_dev *pdev)
pdev->aspm_l1_support = 1;
#endif
pdev->supported_speeds = pcie_get_supported_speeds(pdev);
parent = pci_upstream_bridge(pdev);
if (!parent)
return;
@ -1890,8 +1895,13 @@ static u32 pci_class(struct pci_dev *dev)
u32 class;
#ifdef CONFIG_PCI_IOV
if (dev->is_virtfn)
return dev->physfn->sriov->class;
if (dev->is_virtfn) {
u8 rev;
if (pci_read_config_byte(dev, PCI_REVISION_ID, &rev))
rev = 0;
return (dev->physfn->class << 8) | rev;
}
#endif
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
return class;
@ -1901,8 +1911,8 @@ static void pci_subsystem_ids(struct pci_dev *dev, u16 *vendor, u16 *device)
{
#ifdef CONFIG_PCI_IOV
if (dev->is_virtfn) {
*vendor = dev->physfn->sriov->subsystem_vendor;
*device = dev->physfn->sriov->subsystem_device;
*vendor = dev->physfn->subsystem_vendor;
pci_read_config_word(dev, PCI_SUBSYSTEM_ID, device);
return;
}
#endif
@ -1916,7 +1926,7 @@ static u8 pci_hdr_type(struct pci_dev *dev)
#ifdef CONFIG_PCI_IOV
if (dev->is_virtfn)
return dev->physfn->sriov->hdr_type;
return 0;
#endif
pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);
return hdr_type;
@ -2074,9 +2084,6 @@ int pci_setup_device(struct pci_dev *dev)
set_pcie_untrusted(dev);
if (pci_is_pcie(dev))
dev->supported_speeds = pcie_get_supported_speeds(dev);
/* "Unknown power state" */
dev->current_state = PCI_UNKNOWN;

View File

@ -29,6 +29,8 @@ config PCI_PWRCTRL_TC9563
select PCI_PWRCTRL
default m if ARCH_QCOM
depends on I2C
select AUXILIARY_BUS
select REGMAP_I2C
help
Say Y here to enable the PCI Power Control driver of TC9563 PCIe
switch.

View File

@ -252,14 +252,16 @@ static int __pci_pwrctrl_power_on_device(struct device *dev)
static int pci_pwrctrl_power_on_device(struct device_node *np)
{
struct platform_device *pdev;
struct device_node *child;
int ret = 0;
for_each_available_child_of_node_scoped(np, child) {
for_each_available_child_of_node(np, child) {
ret = pci_pwrctrl_power_on_device(child);
if (ret)
return ret;
goto err_power_off;
}
child = NULL;
if (!pci_pwrctrl_is_required(np))
return 0;
@ -279,6 +281,19 @@ static int pci_pwrctrl_power_on_device(struct device_node *np)
platform_device_put(pdev);
if (ret)
goto err_power_off;
return 0;
err_power_off:
for_each_available_child_of_node_scoped(np, tmp) {
if (tmp == child)
break;
pci_pwrctrl_power_off_device(tmp);
}
of_node_put(child);
return ret;
}
@ -388,6 +403,9 @@ static void pci_pwrctrl_destroy_device(struct device_node *np)
for_each_available_child_of_node_scoped(np, child)
pci_pwrctrl_destroy_device(child);
if (!pci_pwrctrl_is_required(np))
return;
pdev = of_find_device_by_node(np);
if (!pdev)
return;

View File

@ -63,6 +63,7 @@ static void devm_slot_pwrctrl_release(void *data)
struct slot_pwrctrl *slot = data;
regulator_bulk_free(slot->num_supplies, slot->supplies);
kfree(slot->supplies);
}
static int slot_pwrctrl_probe(struct platform_device *pdev)

View File

@ -4,28 +4,28 @@
*/
#include <linux/array_size.h>
#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/pci-pwrctrl.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/soc/qcom/tc9563.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#include "../pci.h"
#define TC9563_GPIO_CONFIG 0x801208
#define TC9563_RESET_GPIO 0x801210
#define TC9563_PORT_L0S_DELAY 0x82496c
#define TC9563_PORT_L1_DELAY 0x824970
@ -57,9 +57,6 @@
#define TC9563_POWER_CONTROL 0x82b09c
#define TC9563_POWER_CONTROL_OVREN 0x82b2c8
#define TC9563_GPIO_MASK 0xfffffff3
#define TC9563_GPIO_DEASSERT_BITS 0xc /* Clear to deassert GPIO */
#define TC9563_TX_MARGIN_MIN_UA 400000
/*
@ -70,11 +67,6 @@
#define TC9563_L0S_L1_DELAY_UNIT_NS 256 /* Each unit represents 256 ns */
struct tc9563_pwrctrl_reg_setting {
unsigned int offset;
unsigned int val;
};
enum tc9563_pwrctrl_ports {
TC9563_USP,
TC9563_DSP1,
@ -90,6 +82,7 @@ struct tc9563_pwrctrl_cfg {
u8 nfts[2]; /* GEN1 & GEN2 */
bool disable_dfe;
bool disable_port;
struct gpio_desc *reset;
};
#define TC9563_PWRCTL_MAX_SUPPLY 6
@ -110,14 +103,14 @@ struct tc9563_pwrctrl {
struct tc9563_pwrctrl_cfg ep_cfg;
struct gpio_desc *reset_gpio;
struct i2c_adapter *adapter;
struct i2c_client *client;
struct regmap *regmap;
};
/*
* downstream port power off sequence, hardcoding the address
* as we don't know register names for these register offsets.
*/
static const struct tc9563_pwrctrl_reg_setting common_pwroff_seq[] = {
static const struct reg_sequence common_pwroff_seq[] = {
{0x82900c, 0x1},
{0x829010, 0x1},
{0x829018, 0x0},
@ -140,7 +133,7 @@ static const struct tc9563_pwrctrl_reg_setting common_pwroff_seq[] = {
{0x829114, 0x1},
};
static const struct tc9563_pwrctrl_reg_setting dsp1_pwroff_seq[] = {
static const struct reg_sequence dsp1_pwroff_seq[] = {
{TC9563_PORT_ACCESS_ENABLE, 0x2},
{TC9563_PORT_LANE_ACCESS_ENABLE, 0x3},
{TC9563_POWER_CONTROL, 0x014f4804},
@ -148,7 +141,7 @@ static const struct tc9563_pwrctrl_reg_setting dsp1_pwroff_seq[] = {
{TC9563_PORT_ACCESS_ENABLE, 0x4},
};
static const struct tc9563_pwrctrl_reg_setting dsp2_pwroff_seq[] = {
static const struct reg_sequence dsp2_pwroff_seq[] = {
{TC9563_PORT_ACCESS_ENABLE, 0x8},
{TC9563_PORT_LANE_ACCESS_ENABLE, 0x1},
{TC9563_POWER_CONTROL, 0x014f4804},
@ -156,85 +149,13 @@ static const struct tc9563_pwrctrl_reg_setting dsp2_pwroff_seq[] = {
{TC9563_PORT_ACCESS_ENABLE, 0x8},
};
/*
* Since all transfers are initiated by the probe, no locks are necessary,
* as there are no concurrent calls.
*/
static int tc9563_pwrctrl_i2c_write(struct i2c_client *client,
u32 reg_addr, u32 reg_val)
{
struct i2c_msg msg;
u8 msg_buf[7];
int ret;
msg.addr = client->addr;
msg.len = 7;
msg.flags = 0;
/* Big Endian for reg addr */
put_unaligned_be24(reg_addr, &msg_buf[0]);
/* Little Endian for reg val */
put_unaligned_le32(reg_val, &msg_buf[3]);
msg.buf = msg_buf;
ret = i2c_transfer(client->adapter, &msg, 1);
return ret == 1 ? 0 : ret;
}
static int tc9563_pwrctrl_i2c_read(struct i2c_client *client,
u32 reg_addr, u32 *reg_val)
{
struct i2c_msg msg[2];
u8 wr_data[3];
u32 rd_data;
int ret;
msg[0].addr = client->addr;
msg[0].len = 3;
msg[0].flags = 0;
/* Big Endian for reg addr */
put_unaligned_be24(reg_addr, &wr_data[0]);
msg[0].buf = wr_data;
msg[1].addr = client->addr;
msg[1].len = 4;
msg[1].flags = I2C_M_RD;
msg[1].buf = (u8 *)&rd_data;
ret = i2c_transfer(client->adapter, &msg[0], 2);
if (ret == 2) {
*reg_val = get_unaligned_le32(&rd_data);
return 0;
}
/* If only one message successfully completed, return -EIO */
return ret == 1 ? -EIO : ret;
}
static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client,
const struct tc9563_pwrctrl_reg_setting *seq,
int len)
{
int ret, i;
for (i = 0; i < len; i++) {
ret = tc9563_pwrctrl_i2c_write(client, seq[i].offset, seq[i].val);
if (ret)
return ret;
}
return 0;
}
static DEFINE_IDA(tc9563_pwrctrl_ida);
static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port];
const struct tc9563_pwrctrl_reg_setting *seq;
const struct reg_sequence *seq;
int ret, len;
if (!cfg->disable_port)
@ -254,12 +175,12 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563,
return 0;
}
ret = tc9563_pwrctrl_i2c_bulk_write(tc9563->client, seq, len);
ret = regmap_multi_reg_write(tc9563->regmap, seq, len);
if (ret)
return ret;
return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, common_pwroff_seq,
ARRAY_SIZE(common_pwroff_seq));
return regmap_multi_reg_write(tc9563->regmap, common_pwroff_seq,
ARRAY_SIZE(common_pwroff_seq));
}
static int tc9563_pwrctrl_set_port_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
@ -275,14 +196,13 @@ static int tc9563_pwrctrl_set_port_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9
/* convert to units of 256ns */
units = ns / TC9563_L0S_L1_DELAY_UNIT_NS;
ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT,
BIT(port));
ret = regmap_write(tc9563->regmap, TC9563_PORT_SELECT, BIT(port));
if (ret)
return ret;
return tc9563_pwrctrl_i2c_write(tc9563->client,
is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY,
units);
return regmap_write(tc9563->regmap,
is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY,
units);
}
static int tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
@ -297,8 +217,7 @@ static int tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc95
/* convert to units of 256ns */
units = ns / TC9563_L0S_L1_DELAY_UNIT_NS;
ret = tc9563_pwrctrl_i2c_read(tc9563->client, TC9563_EMBEDDED_ETH_DELAY,
&rd_val);
ret = regmap_read(tc9563->regmap, TC9563_EMBEDDED_ETH_DELAY, &rd_val);
if (ret)
return ret;
@ -309,8 +228,7 @@ static int tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc95
rd_val = u32_replace_bits(rd_val, units,
TC9563_ETH_L0S_DELAY_MASK);
return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_EMBEDDED_ETH_DELAY,
rd_val);
return regmap_write(tc9563->regmap, TC9563_EMBEDDED_ETH_DELAY, rd_val);
}
static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563,
@ -343,14 +261,14 @@ static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563,
return -EINVAL;
}
struct tc9563_pwrctrl_reg_setting tx_amp_seq[] = {
struct reg_sequence tx_amp_seq[] = {
{TC9563_PORT_ACCESS_ENABLE, port_access},
{TC9563_PORT_LANE_ACCESS_ENABLE, 0x3},
{TC9563_TX_MARGIN, amp},
};
return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, tx_amp_seq,
ARRAY_SIZE(tx_amp_seq));
return regmap_multi_reg_write(tc9563->regmap, tx_amp_seq,
ARRAY_SIZE(tx_amp_seq));
}
static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563,
@ -383,7 +301,7 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563,
return -EINVAL;
}
struct tc9563_pwrctrl_reg_setting disable_dfe_seq[] = {
struct reg_sequence disable_dfe_seq[] = {
{TC9563_PORT_ACCESS_ENABLE, port_access},
{TC9563_PORT_LANE_ACCESS_ENABLE, lane_access},
{TC9563_DFE_ENABLE, 0x0},
@ -397,15 +315,15 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563,
{TC9563_PHY_RATE_CHANGE_OVERRIDE, 0x0},
};
return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, disable_dfe_seq,
ARRAY_SIZE(disable_dfe_seq));
return regmap_multi_reg_write(tc9563->regmap, disable_dfe_seq,
ARRAY_SIZE(disable_dfe_seq));
}
static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
u8 *nfts = tc9563->cfg[port].nfts;
struct tc9563_pwrctrl_reg_setting nfts_seq[] = {
struct reg_sequence nfts_seq[] = {
{TC9563_NFTS_2_5_GT, nfts[0]},
{TC9563_NFTS_5_GT, nfts[1]},
};
@ -420,28 +338,51 @@ static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl *tc9563,
return 0;
}
ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT,
BIT(port));
ret = regmap_write(tc9563->regmap, TC9563_PORT_SELECT, BIT(port));
if (ret)
return ret;
return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, nfts_seq,
ARRAY_SIZE(nfts_seq));
return regmap_multi_reg_write(tc9563->regmap, nfts_seq,
ARRAY_SIZE(nfts_seq));
}
static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl *tc9563,
bool deassert)
{
int ret, val;
int i;
ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_GPIO_CONFIG,
TC9563_GPIO_MASK);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(tc9563->cfg); i++) {
int err;
val = deassert ? TC9563_GPIO_DEASSERT_BITS : 0;
if (tc9563->cfg[i].reset) {
err = gpiod_direction_output(tc9563->cfg[i].reset,
!deassert);
if (err)
return err;
} else {
/* Fallback: legacy DTS without reset-gpios */
switch (i) {
case TC9563_DSP1:
case TC9563_DSP2:
err = regmap_clear_bits(tc9563->regmap,
TC9563_GPIO_CONFIG,
BIT(i + 1));
if (err)
return err;
return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_RESET_GPIO, val);
err = regmap_assign_bits(tc9563->regmap,
TC9563_RESET_GPIO,
BIT(i + 1), deassert);
if (err)
return err;
break;
default:
break;
}
}
}
return 0;
}
static int tc9563_pwrctrl_parse_device_dt(struct device_node *node,
@ -476,12 +417,115 @@ static int tc9563_pwrctrl_parse_device_dt(struct device_node *node,
return 0;
}
static int tc9563_pwrctrl_parse_reset_line(struct tc9563_pwrctrl *tc9563)
{
enum tc9563_pwrctrl_ports port = TC9563_USP;
struct device *dev = tc9563->pwrctrl.dev;
struct device_node *node = dev->of_node;
for_each_child_of_node_scoped(node, child) {
struct tc9563_pwrctrl_cfg *cfg;
if (++port >= TC9563_MAX)
break;
cfg = &tc9563->cfg[port];
if (cfg->reset) /* Already discovered */
continue;
cfg->reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(child),
"reset", GPIOD_ASIS,
NULL);
if (IS_ERR(cfg->reset)) {
int err = PTR_ERR(cfg->reset);
cfg->reset = NULL;
if (err != -ENOENT)
return dev_err_probe(dev, err,
"failed to get reset\n");
}
}
return 0;
}
static void tc9563_pwrctrl_adev_release(struct device *dev)
{
struct auxiliary_device *adev = to_auxiliary_dev(dev);
ida_free(&tc9563_pwrctrl_ida, adev->id);
of_node_put(adev->dev.of_node);
kfree(adev);
}
static void tc9563_pwrctrl_adev_remove(void *data)
{
struct auxiliary_device *adev = data;
auxiliary_device_delete(adev);
auxiliary_device_uninit(adev);
}
static int tc9563_pwrctrl_adev_add(struct device *dev, const char *name,
struct device_node *of_node,
void *priv_data)
{
struct auxiliary_device *adev;
int id, ret;
adev = kzalloc_obj(*adev);
if (!adev)
return -ENOMEM;
id = ida_alloc(&tc9563_pwrctrl_ida, GFP_KERNEL);
if (id < 0) {
kfree(adev);
return id;
}
adev->id = id;
adev->name = name;
adev->dev.parent = dev;
adev->dev.platform_data = priv_data;
adev->dev.release = tc9563_pwrctrl_adev_release;
adev->dev.of_node = of_node_get(of_node);
dev_set_of_node_reused(&adev->dev);
ret = auxiliary_device_init(adev);
if (ret) {
ida_free(&tc9563_pwrctrl_ida, id);
of_node_put(adev->dev.of_node);
kfree(adev);
return ret;
}
ret = auxiliary_device_add(adev);
if (ret) {
auxiliary_device_uninit(adev);
return ret;
}
return devm_add_action_or_reset(dev, tc9563_pwrctrl_adev_remove, adev);
}
static int tc9563_pwrctrl_add_gpio_adev(struct tc9563_pwrctrl *tc9563)
{
struct device *dev = tc9563->pwrctrl.dev;
if (!of_property_read_bool(dev->of_node, "gpio-controller") ||
!of_property_present(dev->of_node, "#gpio-cells"))
return 0;
return tc9563_pwrctrl_adev_add(dev, TC9563_GPIO_DEV_NAME, dev->of_node,
tc9563->regmap);
}
static int tc9563_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl)
{
struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl,
struct tc9563_pwrctrl, pwrctrl);
gpiod_set_value(tc9563->reset_gpio, 1);
gpiod_set_value_cansleep(tc9563->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(tc9563->supplies), tc9563->supplies);
@ -496,12 +540,16 @@ static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
struct tc9563_pwrctrl_cfg *cfg;
int ret, i;
ret = tc9563_pwrctrl_parse_reset_line(tc9563);
if (ret)
return ret;
ret = regulator_bulk_enable(ARRAY_SIZE(tc9563->supplies),
tc9563->supplies);
if (ret < 0)
return dev_err_probe(dev, ret, "cannot enable regulators\n");
gpiod_set_value(tc9563->reset_gpio, 0);
gpiod_set_value_cansleep(tc9563->reset_gpio, 0);
fsleep(TC9563_OSC_STAB_DELAY_US);
@ -572,6 +620,13 @@ static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
return ret;
}
static const struct regmap_config tc9563_regmap_config = {
.reg_bits = 24,
.val_bits = 32,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
};
static int tc9563_pwrctrl_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@ -579,6 +634,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
enum tc9563_pwrctrl_ports port;
struct tc9563_pwrctrl *tc9563;
struct device_node *i2c_node;
struct i2c_client *client;
int ret, addr;
tc9563 = devm_kzalloc(dev, sizeof(*tc9563), GFP_KERNEL);
@ -595,11 +651,18 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
if (!tc9563->adapter)
return dev_err_probe(dev, -EPROBE_DEFER, "Failed to find I2C adapter\n");
tc9563->client = i2c_new_dummy_device(tc9563->adapter, addr);
if (IS_ERR(tc9563->client)) {
client = devm_i2c_new_dummy_device(dev, tc9563->adapter, addr);
if (IS_ERR(client)) {
dev_err(dev, "Failed to create I2C client\n");
i2c_put_adapter(tc9563->adapter);
return PTR_ERR(tc9563->client);
ret = PTR_ERR(client);
goto remove_i2c;
}
tc9563->regmap = devm_regmap_init_i2c(client, &tc9563_regmap_config);
if (IS_ERR(tc9563->regmap)) {
ret = dev_err_probe(dev, PTR_ERR(tc9563->regmap),
"Failed to allocate register map\n");
goto remove_i2c;
}
for (int i = 0; i < ARRAY_SIZE(tc9563_supply_names); i++)
@ -664,6 +727,10 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
tc9563->pwrctrl.power_on = tc9563_pwrctrl_power_on;
tc9563->pwrctrl.power_off = tc9563_pwrctrl_power_off;
ret = tc9563_pwrctrl_add_gpio_adev(tc9563);
if (ret)
goto remove_i2c;
ret = devm_pci_pwrctrl_device_set_ready(dev, &tc9563->pwrctrl);
if (ret)
goto power_off;
@ -673,7 +740,6 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
power_off:
tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
remove_i2c:
i2c_unregister_device(tc9563->client);
i2c_put_adapter(tc9563->adapter);
return ret;
}
@ -685,7 +751,6 @@ static void tc9563_pwrctrl_remove(struct platform_device *pdev)
struct tc9563_pwrctrl, pwrctrl);
tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
i2c_unregister_device(tc9563->client);
i2c_put_adapter(tc9563->adapter);
}

View File

@ -22,6 +22,7 @@
#include <linux/isa-dma.h> /* isa_dma_bridge_buggy */
#include <linux/init.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
@ -108,7 +109,11 @@ int pcie_failed_link_retrain(struct pci_dev *dev)
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2);
if (!(lnksta & PCI_EXP_LNKSTA_DLLLA) && pcie_lbms_seen(dev, lnksta)) {
if (lnksta & PCI_EXP_LNKSTA_DLLLA) {
;
} else if (PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2) == PCIE_SPEED_2_5GT) {
return ret;
} else if (pcie_lbms_seen(dev, lnksta)) {
pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n");
ret = pcie_set_target_speed(dev, PCIE_SPEED_2_5GT, false);
if (ret)
@ -1941,6 +1946,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4388, quirk_no_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4389, quirk_no_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x438a, quirk_no_msi);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x438b, quirk_no_msi);
/* ULi M1575 EHCI (10b9:5239): MSI stalls the async schedule */
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 0x5239, quirk_no_msi);
static void quirk_pcie_mch(struct pci_dev *pdev)
{
@ -2381,6 +2388,24 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2000, quirk_plx_pci9050);
DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2600, quirk_plx_pci9050);
/*
* An idle downstream port of a PLX/Broadcom PEX 8532/8533/8548 switch
* autosuspends into D3hot, and that takes the whole switch down with
* it -- any device behind another port of the same switch then sees a
* fatal, non-recoverable bus fault on its next access. Keep these
* ports out of D3 entirely.
*/
static void quirk_plx_pex_no_d3(struct pci_dev *dev)
{
if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
dev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
pci_info(dev, "avoiding D3 on this downstream port\n");
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PLX, 0x8532, quirk_plx_pex_no_d3);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PLX, 0x8533, quirk_plx_pex_no_d3);
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PLX, 0x8548, quirk_plx_pex_no_d3);
static void quirk_netmos(struct pci_dev *dev)
{
unsigned int num_parallel = (dev->subsystem_device & 0xf0) >> 4;
@ -3822,6 +3847,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TI, 0xb005, quirk_no_bus_reset);
*/
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASMEDIA, 0x1164, quirk_no_bus_reset);
/* Intel DG1 0x4905 may not recover from bus reset */
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4905, quirk_no_bus_reset);
static void quirk_no_pm_reset(struct pci_dev *dev)
{
/*
@ -4104,7 +4132,7 @@ static int nvme_disable_and_flr(struct pci_dev *dev, bool probe)
if (probe)
return 0;
bar = pci_iomap(dev, 0, NVME_REG_CC + sizeof(cfg));
bar = pci_iomap(dev, 0, NVME_REG_CSTS + sizeof(u32));
if (!bar)
return -ENOTTY;
@ -4241,6 +4269,117 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
return 0;
}
#define QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET 0x3008
#define QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V BIT(0)
#define QUALCOMM_WLAN_MHICTRL 0x38
#define QUALCOMM_WLAN_MHICTRL_RESET_MASK 0x2
/*
* Qualcomm WLAN device-specific reset using SoC global reset via BAR0
* registers.
*/
static int reset_qualcomm_wlan(struct pci_dev *pdev, bool probe)
{
void __iomem *bar;
u32 val;
u16 cmd;
int ret;
if (probe)
return 0;
if (pdev->current_state != PCI_D0)
return -EINVAL;
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
bar = pci_iomap(pdev, 0, 0);
if (!bar) {
pci_write_config_word(pdev, PCI_COMMAND, cmd);
return -ENODEV;
}
val = ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
if (PCI_POSSIBLE_ERROR(val)) {
ret = -ENODEV;
goto out_restore;
}
val |= QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V;
iowrite32(val, bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
msleep(10);
val &= ~QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V;
iowrite32(val, bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
msleep(10);
ret = read_poll_timeout(ioread32, val, !PCI_POSSIBLE_ERROR(val),
20 * USEC_PER_MSEC, 5 * USEC_PER_SEC, false,
bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
if (ret) {
pci_err(pdev, "PCIe link failed to recover after reset\n");
goto out_restore;
}
/*
* After SOC_GLOBAL_RESET, MHISTATUS may still have SYSERR bit set
* and thus need to set MHICTRL_RESET to clear SYSERR.
*/
iowrite32(QUALCOMM_WLAN_MHICTRL_RESET_MASK, bar + QUALCOMM_WLAN_MHICTRL);
ioread32(bar + QUALCOMM_WLAN_MHICTRL);
msleep(10);
out_restore:
pci_iounmap(pdev, bar);
pci_write_config_word(pdev, PCI_COMMAND, cmd);
return ret;
}
#define MHI_SOC_RESET_REQ_OFFSET 0xb0
#define MHI_SOC_RESET_REQ BIT(0)
/*
* Qualcomm modem device-specific reset using MHI SoC reset via BAR0
* register.
*/
static int reset_qualcomm_modem(struct pci_dev *pdev, bool probe)
{
void __iomem *bar;
u16 cmd;
if (probe)
return 0;
if (pdev->current_state != PCI_D0)
return -EINVAL;
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
bar = pci_iomap(pdev, 0, 0);
if (!bar) {
pci_write_config_word(pdev, PCI_COMMAND, cmd);
return -ENODEV;
}
iowrite32(MHI_SOC_RESET_REQ, bar + MHI_SOC_RESET_REQ_OFFSET);
ioread32(bar + MHI_SOC_RESET_REQ_OFFSET);
/* Be sure device reset has been executed */
msleep(2000);
pci_iounmap(pdev, bar);
pci_write_config_word(pdev, PCI_COMMAND, cmd);
return 0;
}
static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82599_SFP_VF,
reset_intel_82599_sfp_virtfn },
@ -4256,6 +4395,9 @@ static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
reset_chelsio_generic_dev },
{ PCI_VENDOR_ID_HUAWEI, PCI_DEVICE_ID_HINIC_VF,
reset_hinic_vf_dev },
{ PCI_VENDOR_ID_QCOM, 0x0308, reset_qualcomm_modem }, /* SDX62/SDX65 modems */
{ PCI_VENDOR_ID_QCOM, 0x1103, reset_qualcomm_wlan }, /* WCN6855 WLAN */
{ PCI_VENDOR_ID_QCOM, 0x1107, reset_qualcomm_wlan }, /* WCN7850 WLAN */
{ 0 }
};
@ -5608,6 +5750,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap);
* AMD Neural Processing Unit 0x1502 0x17f0
* Intel 82579LM Gigabit Ethernet Controller 0x1502
* Intel 82579V Gigabit Ethernet Controller 0x1503
* Intel DG1 Discrete Graphics Adapter 0x4905
* Mediatek MT7922 802.11ax PCI Express Wireless Network Adapter
* Mediatek MT7925 802.11be PCI Express Wireless Network Adapter
*/
@ -5623,6 +5766,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1502, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x17f0, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x4905, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x0616, quirk_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x7925, quirk_no_flr);
@ -6286,6 +6430,10 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2303,
pci_fixup_pericom_acs_store_forward);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0x2303,
pci_fixup_pericom_acs_store_forward);
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0xb304,
pci_fixup_pericom_acs_store_forward);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb304,
pci_fixup_pericom_acs_store_forward);
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0xb404,
pci_fixup_pericom_acs_store_forward);
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb404,

View File

@ -119,12 +119,14 @@ static acpi_status tph_invoke_dsm(acpi_handle handle, u32 cpu_uid,
if (!out_obj)
return AE_ERROR;
if (out_obj->type != ACPI_TYPE_BUFFER) {
if (out_obj->type != ACPI_TYPE_BUFFER ||
out_obj->buffer.length < sizeof(st_out->value) ||
!out_obj->buffer.pointer) {
ACPI_FREE(out_obj);
return AE_ERROR;
}
st_out->value = *((u64 *)(out_obj->buffer.pointer));
memcpy(&st_out->value, out_obj->buffer.pointer, sizeof(st_out->value));
ACPI_FREE(out_obj);

View File

@ -163,7 +163,8 @@ enum pci_epf_doorbell_type {
* For MSI-backed doorbells this is the MSI message, while for
* "embedded" doorbells this represents an MMIO write that asserts
* an interrupt on the EP side.
* @virq: IRQ number of this doorbell message
* @virq: IRQ number of this doorbell message. Multiple messages may use the
* same IRQ; consumers must request each distinct IRQ only once.
* @irq_flags: Required flags for request_irq()/request_threaded_irq().
* Callers may OR-in additional flags (e.g. IRQF_ONESHOT).
* @type: Doorbell type.

View File

@ -1916,12 +1916,14 @@ static inline int pci_irqd_intx_xlate(struct irq_domain *d,
#ifdef CONFIG_PCIEPORTBUS
extern bool pcie_ports_disabled;
extern bool pcie_ports_native;
extern bool pcie_ports_dpc_native;
int pcie_set_target_speed(struct pci_dev *port, enum pci_bus_speed speed_req,
bool use_lt);
#else
#define pcie_ports_disabled true
#define pcie_ports_native false
#define pcie_ports_dpc_native false
static inline int pcie_set_target_speed(struct pci_dev *port,
enum pci_bus_speed speed_req,

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved.
* Author: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
*/
#ifndef __QCOM_TC9563_H
#define __QCOM_TC9563_H
#define TC9563_GPIO_DEV_NAME "tc9563-gpio"
#define TC9563_GPIO_IN0_OFFSET 0x801200
#define TC9563_GPIO_EN0_OFFSET 0x801208
#define TC9563_GPIO_OUT0_OFFSET 0x801210
#define TC9563_GPIO_CONFIG TC9563_GPIO_EN0_OFFSET
#define TC9563_RESET_GPIO TC9563_GPIO_OUT0_OFFSET
#endif /* __QCOM_TC9563_H */