summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-30 12:48:42 +0100
committerMark Brown <broonie@kernel.org>2026-09-30 12:48:42 +0100
commit71fac8f5218420bdebb1c82728ed8ad9ae1f711b (patch)
treed85e6c6644264471a2d3fea9de609b859b7390bf
parent3b3fc5d9f44cafc0b9d6536f73691e3abfc9dd59 (diff)
parent81bebadfa0f7f9337a0b4e8e218731457b7eb9af (diff)
downloadlinux-next-71fac8f5218420bdebb1c82728ed8ad9ae1f711b.tar.gz
linux-next-71fac8f5218420bdebb1c82728ed8ad9ae1f711b.zip
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
-rw-r--r--Documentation/ABI/testing/sysfs-bus-pci14
-rw-r--r--Documentation/PCI/controller/index.rst1
-rw-r--r--Documentation/PCI/controller/rcar-pcie-firmware.rst32
-rw-r--r--Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt670
-rw-r--r--Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.yaml532
-rw-r--r--Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml9
-rw-r--r--Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml83
-rw-r--r--Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml33
-rw-r--r--Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml14
-rw-r--r--Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml38
-rw-r--r--Documentation/driver-api/pci/p2pdma.rst6
-rw-r--r--MAINTAINERS1
-rw-r--r--arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts7
-rw-r--r--drivers/gpio/Kconfig11
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-tc9563.c99
-rw-r--r--drivers/net/wireless/ath/ath10k/Kconfig2
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.c11
-rw-r--r--drivers/net/wireless/ath/ath10k/pci.h5
-rw-r--r--drivers/net/wireless/ath/ath11k/Kconfig2
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.c19
-rw-r--r--drivers/net/wireless/ath/ath11k/pci.h3
-rw-r--r--drivers/net/wireless/ath/ath12k/Kconfig2
-rw-r--r--drivers/net/wireless/ath/ath12k/pci.c19
-rw-r--r--drivers/net/wireless/ath/ath12k/pci.h4
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-debugfs.c15
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-plat.c6
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence.c16
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence.h2
-rw-r--r--drivers/pci/controller/dwc/pci-dra7xx.c22
-rw-r--r--drivers/pci/controller/dwc/pci-imx6.c94
-rw-r--r--drivers/pci/controller/dwc/pci-keystone.c32
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-ep.c8
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.h1
-rw-r--r--drivers/pci/controller/dwc/pcie-dw-rockchip.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-histb.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom-ep.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom.c176
-rw-r--r--drivers/pci/controller/dwc/pcie-rcar-gen4.c325
-rw-r--r--drivers/pci/controller/dwc/pcie-spacemit-k1.c3
-rw-r--r--drivers/pci/controller/dwc/pcie-tegra194.c1
-rw-r--r--drivers/pci/controller/pci-tegra.c1
-rw-r--r--drivers/pci/controller/pci-xgene.c11
-rw-r--r--drivers/pci/controller/pcie-aspeed.c9
-rw-r--r--drivers/pci/controller/pcie-mediatek-gen3.c15
-rw-r--r--drivers/pci/controller/pcie-mediatek.c4
-rw-r--r--drivers/pci/controller/pcie-rockchip-host.c1
-rw-r--r--drivers/pci/controller/pcie-rzg3s-host.c76
-rw-r--r--drivers/pci/controller/pcie-xilinx-cpm.c77
-rw-r--r--drivers/pci/controller/plda/pcie-starfive.c1
-rw-r--r--drivers/pci/controller/vmd.c57
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-vntb.c93
-rw-r--r--drivers/pci/endpoint/pci-ep-msi.c40
-rw-r--r--drivers/pci/hotplug/pciehp_core.c2
-rw-r--r--drivers/pci/iov.c25
-rw-r--r--drivers/pci/p2pdma.c39
-rw-r--r--drivers/pci/pci-label.c92
-rw-r--r--drivers/pci/pci.h13
-rw-r--r--drivers/pci/pcie/aspm.c140
-rw-r--r--drivers/pci/probe.c15
-rw-r--r--drivers/pci/pwrctrl/Kconfig2
-rw-r--r--drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c325
-rw-r--r--drivers/pci/quirks.c126
-rw-r--r--drivers/pci/tph.c6
-rw-r--r--include/linux/pci-epf.h3
-rw-r--r--include/linux/pci.h7
-rw-r--r--include/linux/soc/qcom/tc9563.h19
67 files changed, 2246 insertions, 1275 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
index 55ea1db749a1..c4ff6b538233 100644
--- a/Documentation/ABI/testing/sysfs-bus-pci
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -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.
diff --git a/Documentation/PCI/controller/index.rst b/Documentation/PCI/controller/index.rst
index c15e2c9ac855..096d1c3d9807 100644
--- a/Documentation/PCI/controller/index.rst
+++ b/Documentation/PCI/controller/index.rst
@@ -8,4 +8,3 @@ PCI Native Host Bridge and Endpoint Drivers
:maxdepth: 2
pci-controller-drivers
- rcar-pcie-firmware
diff --git a/Documentation/PCI/controller/rcar-pcie-firmware.rst b/Documentation/PCI/controller/rcar-pcie-firmware.rst
deleted file mode 100644
index 67d3bf66e315..000000000000
--- a/Documentation/PCI/controller/rcar-pcie-firmware.rst
+++ /dev/null
@@ -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.
diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
deleted file mode 100644
index d099f3476ccc..000000000000
--- a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
+++ /dev/null
@@ -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";
- };
- };
diff --git a/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.yaml b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.yaml
new file mode 100644
index 000000000000..fcc7875e6853
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.yaml
@@ -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";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml
index b23293314a6d..ce177f0eca75 100644
--- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml
+++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-ep.yaml
@@ -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>;
diff --git a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
index 64f456c6a75a..362c43a1a15d 100644
--- a/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
+++ b/Documentation/devicetree/bindings/pci/rcar-gen4-pci-host.yaml
@@ -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
-
- reg:
- maxItems: 7
-
- reg-names:
- items:
- - const: dbi
- - const: dbi2
- - const: atu
- - const: dma
- - const: app
- - const: phy
- - const: config
+ 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
+
+ - 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>;
diff --git a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
index 09d78bd1a577..7827d81680fe 100644
--- a/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/renesas,r9a08g045-pcie.yaml
@@ -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
@@ -227,6 +234,28 @@ allOf:
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:
+ contains:
enum:
- renesas,r9a09g047-pcie
- renesas,r9a09g057-pcie
@@ -236,6 +265,8 @@ allOf:
minItems: 23
interrupt-names:
minItems: 23
+ clocks:
+ maxItems: 2
clock-names:
items:
- const: aclk
diff --git a/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml b/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
index f084830c6d0c..c4ebb99f50c3 100644
--- a/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
+++ b/Documentation/devicetree/bindings/pci/toshiba,tc9563.yaml
@@ -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>;
};
diff --git a/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml b/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml
index 9823456addea..a28a88dc8e88 100644
--- a/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml
+++ b/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml
@@ -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>;
diff --git a/Documentation/driver-api/pci/p2pdma.rst b/Documentation/driver-api/pci/p2pdma.rst
index d3f406cca694..63cff9e4d2c9 100644
--- a/Documentation/driver-api/pci/p2pdma.rst
+++ b/Documentation/driver-api/pci/p2pdma.rst
@@ -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.
diff --git a/MAINTAINERS b/MAINTAINERS
index c53778bc674b..1287d12ce6a8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21003,7 +21003,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*
diff --git a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts
index 909039816a21..ed9ba2e5550e 100644
--- a/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts
+++ b/arch/arm64/boot/dts/qcom/qcs6490-rb3gen2.dts
@@ -867,7 +867,7 @@
&pcie1_port0 {
reset-gpios = <&tlmm 2 GPIO_ACTIVE_LOW>;
- pcie@0,0 {
+ tc9563: pcie@0,0 {
compatible = "pci1179,0623";
reg = <0x10000 0x0 0x0 0x0 0x0>;
#address-cells = <3>;
@@ -891,6 +891,9 @@
pinctrl-0 = <&tc9563_resx_n>;
pinctrl-names = "default";
+ gpio-controller;
+ #gpio-cells = <2>;
+
pcie1_switch0_dsp1: pcie@1,0 {
reg = <0x20800 0x0 0x0 0x0 0x0>;
#address-cells = <3>;
@@ -899,6 +902,7 @@
device_type = "pci";
ranges;
bus-range = <0x3 0xff>;
+ reset-gpios = <&tc9563 2 GPIO_ACTIVE_LOW>;
};
pcie@2,0 {
@@ -909,6 +913,7 @@
device_type = "pci";
ranges;
bus-range = <0x4 0xff>;
+ reset-gpios = <&tc9563 3 GPIO_ACTIVE_LOW>;
/* Renesas μPD720201 PCIe USB3.0 Host Controller */
usb-controller@0,0 {
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index a48586bb8edb..2179eaffcc48 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1830,6 +1830,17 @@ config GPIO_LTC4283
endmenu
+config GPIO_TC9563
+ tristate "Toshiba TC9563 GPIO support"
+ 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
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index dc9e6d643b5b..792faa2668c9 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -182,6 +182,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
diff --git a/drivers/gpio/gpio-tc9563.c b/drivers/gpio/gpio-tc9563.c
new file mode 100644
index 000000000000..68c20c8cba79
--- /dev/null
+++ b/drivers/gpio/gpio-tc9563.c
@@ -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");
diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig
index efb9f022d8c6..eac3ec5a4e78 100644
--- a/drivers/net/wireless/ath/ath10k/Kconfig
+++ b/drivers/net/wireless/ath/ath10k/Kconfig
@@ -17,7 +17,7 @@ config ATH10K_CE
config ATH10K_PCI
tristate "Atheros ath10k PCI support"
- depends on ATH10K && PCI
+ depends on ATH10K && PCI && PCIEASPM
help
This module adds support for PCIE bus
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 335bc7c488e4..2e27a70065cc 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -1965,9 +1965,7 @@ static int ath10k_pci_hif_start(struct ath10k *ar)
ath10k_pci_irq_enable(ar);
ath10k_pci_rx_post(ar);
- pcie_capability_clear_and_set_word(ar_pci->pdev, PCI_EXP_LNKCTL,
- PCI_EXP_LNKCTL_ASPMC,
- ar_pci->link_ctl & PCI_EXP_LNKCTL_ASPMC);
+ pci_force_enable_link_state(ar_pci->pdev, ar_pci->aspm_states);
return 0;
}
@@ -2822,10 +2820,9 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar,
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot hif power up\n");
- pcie_capability_read_word(ar_pci->pdev, PCI_EXP_LNKCTL,
- &ar_pci->link_ctl);
- pcie_capability_clear_word(ar_pci->pdev, PCI_EXP_LNKCTL,
- PCI_EXP_LNKCTL_ASPMC);
+ ar_pci->aspm_states = pcie_aspm_enabled(ar_pci->pdev);
+
+ pci_disable_link_state(ar_pci->pdev, PCIE_LINK_STATE_ASPM_ALL);
/*
* Bring the target up cleanly.
diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
index d114778edb41..122e365b7cdd 100644
--- a/drivers/net/wireless/ath/ath10k/pci.h
+++ b/drivers/net/wireless/ath/ath10k/pci.h
@@ -128,10 +128,9 @@ struct ath10k_pci {
struct timer_list rx_post_retry;
/* Due to HW quirks it is recommended to disable ASPM during device
- * bootup. To do that the original PCI-E Link Control is stored before
- * device bootup is executed and re-programmed later.
+ * bootup. To do that the ASPM states are saved and re-programmed later.
*/
- u16 link_ctl;
+ u32 aspm_states;
/* Protects ps_awake and ps_wake_refcount */
spinlock_t ps_lock;
diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig
index 122726f84492..c8b418521173 100644
--- a/drivers/net/wireless/ath/ath11k/Kconfig
+++ b/drivers/net/wireless/ath/ath11k/Kconfig
@@ -19,7 +19,7 @@ config ATH11K_AHB
config ATH11K_PCI
tristate "Atheros ath11k PCI support"
- depends on ATH11K && PCI
+ depends on ATH11K && PCI && PCIEASPM
select MHI_BUS
select QRTR
select QRTR_MHI
diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c
index a163168f3617..32dbb844a47b 100644
--- a/drivers/net/wireless/ath/ath11k/pci.c
+++ b/drivers/net/wireless/ath/ath11k/pci.c
@@ -608,19 +608,9 @@ static void ath11k_pci_free_region(struct ath11k_pci *ab_pci)
static void ath11k_pci_aspm_disable(struct ath11k_pci *ab_pci)
{
- struct ath11k_base *ab = ab_pci->ab;
-
- pcie_capability_read_word(ab_pci->pdev, PCI_EXP_LNKCTL,
- &ab_pci->link_ctl);
-
- ath11k_dbg(ab, ATH11K_DBG_PCI, "link_ctl 0x%04x L0s %d L1 %d\n",
- ab_pci->link_ctl,
- u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L0S),
- u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L1));
+ ab_pci->aspm_states = pcie_aspm_enabled(ab_pci->pdev);
- /* disable L0s and L1 */
- pcie_capability_clear_word(ab_pci->pdev, PCI_EXP_LNKCTL,
- PCI_EXP_LNKCTL_ASPMC);
+ pci_disable_link_state(ab_pci->pdev, PCIE_LINK_STATE_ASPM_ALL);
set_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags);
}
@@ -628,10 +618,7 @@ static void ath11k_pci_aspm_disable(struct ath11k_pci *ab_pci)
static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci)
{
if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags))
- pcie_capability_clear_and_set_word(ab_pci->pdev, PCI_EXP_LNKCTL,
- PCI_EXP_LNKCTL_ASPMC,
- ab_pci->link_ctl &
- PCI_EXP_LNKCTL_ASPMC);
+ pci_force_enable_link_state(ab_pci->pdev, ab_pci->aspm_states);
}
#ifdef CONFIG_DEV_COREDUMP
diff --git a/drivers/net/wireless/ath/ath11k/pci.h b/drivers/net/wireless/ath/ath11k/pci.h
index 1e3005a4b64c..474c18741418 100644
--- a/drivers/net/wireless/ath/ath11k/pci.h
+++ b/drivers/net/wireless/ath/ath11k/pci.h
@@ -72,7 +72,8 @@ struct ath11k_pci {
/* enum ath11k_pci_flags */
unsigned long flags;
- u16 link_ctl;
+ /* Cached PCIe ASPM states */
+ u32 aspm_states;
u64 dma_mask;
};
diff --git a/drivers/net/wireless/ath/ath12k/Kconfig b/drivers/net/wireless/ath/ath12k/Kconfig
index 0d5d1c55bfc1..4662d38c88c0 100644
--- a/drivers/net/wireless/ath/ath12k/Kconfig
+++ b/drivers/net/wireless/ath/ath12k/Kconfig
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: BSD-3-Clause-Clear
config ATH12K
tristate "Qualcomm Wi-Fi 7 support (ath12k)"
- depends on MAC80211 && HAS_DMA && PCI
+ depends on MAC80211 && HAS_DMA && PCI && PCIEASPM
select QCOM_QMI_HELPERS
select MHI_BUS
select QRTR
diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c
index 6441927b5382..ae2040004330 100644
--- a/drivers/net/wireless/ath/ath12k/pci.c
+++ b/drivers/net/wireless/ath/ath12k/pci.c
@@ -899,19 +899,9 @@ static void ath12k_pci_free_region(struct ath12k_pci *ab_pci)
static void ath12k_pci_aspm_disable(struct ath12k_pci *ab_pci)
{
- struct ath12k_base *ab = ab_pci->ab;
-
- pcie_capability_read_word(ab_pci->pdev, PCI_EXP_LNKCTL,
- &ab_pci->link_ctl);
-
- ath12k_dbg(ab, ATH12K_DBG_PCI, "pci link_ctl 0x%04x L0s %d L1 %d\n",
- ab_pci->link_ctl,
- u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L0S),
- u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L1));
+ ab_pci->aspm_states = pcie_aspm_enabled(ab_pci->pdev);
- /* disable L0s and L1 */
- pcie_capability_clear_word(ab_pci->pdev, PCI_EXP_LNKCTL,
- PCI_EXP_LNKCTL_ASPMC);
+ pci_disable_link_state(ab_pci->pdev, PCIE_LINK_STATE_ASPM_ALL);
set_bit(ATH12K_PCI_ASPM_RESTORE, &ab_pci->flags);
}
@@ -940,10 +930,7 @@ static void ath12k_pci_aspm_restore(struct ath12k_pci *ab_pci)
{
if (ab_pci->ab->hw_params->supports_aspm &&
test_and_clear_bit(ATH12K_PCI_ASPM_RESTORE, &ab_pci->flags))
- pcie_capability_clear_and_set_word(ab_pci->pdev, PCI_EXP_LNKCTL,
- PCI_EXP_LNKCTL_ASPMC,
- ab_pci->link_ctl &
- PCI_EXP_LNKCTL_ASPMC);
+ pci_force_enable_link_state(ab_pci->pdev, ab_pci->aspm_states);
}
static void ath12k_pci_cancel_workqueue(struct ath12k_base *ab)
diff --git a/drivers/net/wireless/ath/ath12k/pci.h b/drivers/net/wireless/ath/ath12k/pci.h
index 0e0e2020c6ae..409ef063cd69 100644
--- a/drivers/net/wireless/ath/ath12k/pci.h
+++ b/drivers/net/wireless/ath/ath12k/pci.h
@@ -128,7 +128,9 @@ struct ath12k_pci {
/* enum ath12k_pci_flags */
unsigned long flags;
- u16 link_ctl;
+
+ /* Cached PCIe ASPM states */
+ u32 aspm_states;
unsigned long irq_flags;
const struct ath12k_pci_ops *pci_ops;
u32 qmi_instance;
diff --git a/drivers/pci/controller/cadence/pcie-cadence-debugfs.c b/drivers/pci/controller/cadence/pcie-cadence-debugfs.c
index 0a308f95e9f6..7b8354052aea 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-debugfs.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-debugfs.c
@@ -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;
}
diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat.c b/drivers/pci/controller/cadence/pcie-cadence-plat.c
index a1ea24fc3b63..13edc6be21f5 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-plat.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-plat.c
@@ -41,7 +41,6 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
struct pci_host_bridge *bridge;
struct cdns_pcie_ep *ep;
struct cdns_pcie_rc *rc;
- int phy_count;
bool is_rc;
int ret;
@@ -122,11 +121,8 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
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]);
- return 0;
+ return ret;
}
static void cdns_plat_pcie_shutdown(struct platform_device *pdev)
diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c
index a1eada56edba..0ac980249941 100644
--- a/drivers/pci/controller/cadence/pcie-cadence.c
+++ b/drivers/pci/controller/cadence/pcie-cadence.c
@@ -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;
}
diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h
index b4d51adefea5..4a96e1869e53 100644
--- a/drivers/pci/controller/cadence/pcie-cadence.h
+++ b/drivers/pci/controller/cadence/pcie-cadence.h
@@ -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;
diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
index 6ae5b27e27b3..3460a83b4395 100644
--- a/drivers/pci/controller/dwc/pci-dra7xx.c
+++ b/drivers/pci/controller/dwc/pci-dra7xx.c
@@ -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>
@@ -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 @@ err_get_sync:
pm_runtime_disable(dev);
dra7xx_pcie_disable_phy(dra7xx);
-err_link:
- while (--i >= 0)
- device_link_del(link[i]);
-
return ret;
}
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index f7a2eb257c16..28fed010b503 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -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;
}
@@ -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;
}
@@ -1951,6 +2017,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,7 +2045,7 @@ 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;
}
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY))
diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index 602516239a57..b8e303243c03 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -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;
@@ -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 @@ err_get_sync:
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 = {
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index de8ee3db4360..1a3491b5003e 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -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;
}
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 0735ae940924..a53ac27cd244 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -475,6 +475,7 @@ struct dw_pcie_rp {
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);
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index af26a07c0c9e..14976861f8ef 100644
--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -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>
diff --git a/drivers/pci/controller/dwc/pcie-histb.c b/drivers/pci/controller/dwc/pcie-histb.c
index a52071589377..432a54c5bfce 100644
--- a/drivers/pci/controller/dwc/pcie-histb.c
+++ b/drivers/pci/controller/dwc/pcie-histb.c
@@ -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>
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index 56184e6ca6e6..8364696a1b98 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -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>
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index b58a607b713f..fe0cf6b6d966 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -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
+/*
+ * ECAM 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 ECAM_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_ecam_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_ecam_blocker(pcie);
+
return 0;
}
@@ -1104,16 +1145,13 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie)
qcom_pcie_set_slot_cap(pcie->pci);
+ qcom_pcie_init_ecam_blocker(pcie);
+
return 0;
}
static int qcom_pcie_enable_aspm(struct pci_dev *pdev, void *userdata)
{
- /*
- * Downstream devices need to be in D0 state before enabling PCI PM
- * substates.
- */
- pci_set_power_state_locked(pdev, PCI_D0);
pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
return 0;
@@ -1143,50 +1181,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 +1286,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;
}
@@ -1322,6 +1398,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_ecam_blocker(pcie);
+
return 0;
}
@@ -1382,6 +1460,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_ecam_blocker(struct qcom_pcie *pcie)
+{
+ u32 sys_ctrl;
+
+ sys_ctrl = readl(pcie->parf + PARF_SYS_CTRL);
+ sys_ctrl |= ECAM_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 +1865,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,
@@ -1878,6 +1970,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_ecam_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);
@@ -1953,7 +2050,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 +2116,9 @@ skip_perst_parsing:
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 +2304,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 +2445,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 +2507,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)
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index fbe465a29068..21c3dd9b8891 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -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)
@@ -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, 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 + 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, PCIE_PORT_LANE_SKEW);
+ val &= ~PORT_LANE_SKEW_INSERT_MASK;
+ if (dw->num_lanes < 8)
+ val |= BIT(6);
+ dw_pcie_writel_dbi(dw, PCIE_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 @@ err:
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 = {
@@ -487,7 +688,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 +699,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 +758,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 +777,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 +794,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 +828,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 +839,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 +865,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 +890,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,
@@ -850,27 +1063,45 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable
}
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,6 +1119,10 @@ 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);
diff --git a/drivers/pci/controller/dwc/pcie-spacemit-k1.c b/drivers/pci/controller/dwc/pcie-spacemit-k1.c
index 0564c46e2f48..92a71ea90ac3 100644
--- a/drivers/pci/controller/dwc/pcie-spacemit-k1.c
+++ b/drivers/pci/controller/dwc/pcie-spacemit-k1.c
@@ -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>
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 63a0f56cad8e..8ab23d1d7bd6 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -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>
diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c
index 59b56446c507..96bd522fa67b 100644
--- a/drivers/pci/controller/pci-tegra.c
+++ b/drivers/pci/controller/pci-tegra.c
@@ -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>
diff --git a/drivers/pci/controller/pci-xgene.c b/drivers/pci/controller/pci-xgene.c
index 83c9a2930eec..a5b8bbc04a22 100644
--- a/drivers/pci/controller/pci-xgene.c
+++ b/drivers/pci/controller/pci-xgene.c
@@ -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;
diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie-aspeed.c
index 74b03a55643f..ecb7562f84c0 100644
--- a/drivers/pci/controller/pcie-aspeed.c
+++ b/drivers/pci/controller/pcie-aspeed.c
@@ -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);
diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c
index bcbd08c82a5c..e2fb6e372ede 100644
--- a/drivers/pci/controller/pcie-mediatek-gen3.c
+++ b/drivers/pci/controller/pcie-mediatek-gen3.c
@@ -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;
}
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index a60d1ae076f8..3a3e91cd0c21 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
@@ -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;
}
diff --git a/drivers/pci/controller/pcie-rockchip-host.c b/drivers/pci/controller/pcie-rockchip-host.c
index d203c4876d30..be8ff99b243f 100644
--- a/drivers/pci/controller/pcie-rockchip-host.c
+++ b/drivers/pci/controller/pcie-rockchip-host.c
@@ -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"
diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
index a1a78bdb7af1..077cfb0834b3 100644
--- a/drivers/pci/controller/pcie-rzg3s-host.c
+++ b/drivers/pci/controller/pcie-rzg3s-host.c
@@ -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),
@@ -2124,6 +2166,10 @@ static const struct of_device_id rzg3s_pcie_of_match[] = {
.data = &rzg3s_soc_data,
},
{
+ .compatible = "renesas,r9a08g046-pcie",
+ .data = &rzg3l_soc_data,
+ },
+ {
.compatible = "renesas,r9a09g047-pcie",
.data = &rzg3e_soc_data,
},
diff --git a/drivers/pci/controller/pcie-xilinx-cpm.c b/drivers/pci/controller/pcie-xilinx-cpm.c
index d38f27e20761..1fdd7f920590 100644
--- a/drivers/pci/controller/pcie-xilinx-cpm.c
+++ b/drivers/pci/controller/pcie-xilinx-cpm.c
@@ -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;
@@ -470,6 +477,22 @@ static int xilinx_cpm_setup_irq(struct xilinx_cpm_pcie *port)
}
/**
+ * 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
@@ -513,6 +539,42 @@ static void xilinx_cpm_pcie_init_port(struct xilinx_cpm_pcie *port)
}
/**
+ * 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
* @bus_range: Bus resource
@@ -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");
diff --git a/drivers/pci/controller/plda/pcie-starfive.c b/drivers/pci/controller/plda/pcie-starfive.c
index 02d5aa6dc14b..b693eabdf85b 100644
--- a/drivers/pci/controller/plda/pcie-starfive.c
+++ b/drivers/pci/controller/plda/pcie-starfive.c
@@ -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"
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c
index 241023ecf677..d5845ea647f6 100644
--- a/drivers/pci/controller/vmd.c
+++ b/drivers/pci/controller/vmd.c
@@ -92,6 +92,22 @@ enum vmd_features {
* referred to as MEMBAR2 or MSI-X BAR.
*/
VMD_FEAT_USE_BIOS_INFO = (1 << 6),
+
+ /*
+ * Meteor Lake VMD (device ID 0x7d0b) is affected by erratum MTL016:
+ * 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 +130,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 (MTL016); 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 +142,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 +157,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 +323,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 +791,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 +835,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,
@@ -896,11 +944,6 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata)
pci_info(pdev, "VMD: Default LTR value set by driver\n");
out_state_change:
- /*
- * Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per
- * PCIe r6.0, sec 5.5.4.
- */
- pci_set_power_state_locked(pdev, PCI_D0);
pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL);
return 0;
}
@@ -1250,7 +1293,7 @@ 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,},
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B),
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index c3caec927d74..07ba338732d4 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -132,7 +132,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;
@@ -166,10 +166,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,
@@ -195,7 +200,7 @@ static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up)
else
ntb->reg->link_status &= ~LINK_STATUS_UP;
- ntb_link_event(&ntb->ntb);
+ ntb_link_event(ntb->ntb);
return 0;
}
@@ -284,7 +289,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;
}
}
@@ -348,12 +353,18 @@ reset_handler:
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;
@@ -985,7 +996,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 +1342,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 +1436,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;
@@ -1558,7 +1575,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work)
ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no,
PCI_IRQ_MSI, 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);
}
@@ -1676,13 +1693,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;
+
+ ntb = devm_kzalloc(dev, sizeof(*ntb), GFP_KERNEL);
+ if (!ntb)
+ return -ENOMEM;
- ndev->ntb.pdev = pdev;
- ndev->ntb.topo = NTB_TOPO_NONE;
- ndev->ntb.ops = &vntb_epf_ops;
+ 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 +1712,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 +1758,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 ====================*/
@@ -1791,12 +1839,25 @@ err_bar_alloc:
*/
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
diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c
index 9c13f193537d..2fe48326a4c6 100644
--- a/drivers/pci/endpoint/pci-ep-msi.c
+++ b/drivers/pci/endpoint/pci-ep-msi.c
@@ -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) {
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 2cafd3b26f34..5ce32dc7e178 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -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");
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 9d408fb8ac25..9185934a900f 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -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) {
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index 9334eb314663..beb1fe20c9de 100644
--- a/drivers/pci/p2pdma.c
+++ b/drivers/pci/p2pdma.c
@@ -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:
@@ -569,6 +570,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 +717,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 +779,18 @@ check_b_path_acs:
}
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 +876,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;
diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c
index 0c6446519640..08c461f1142d 100644
--- a/drivers/pci/pci-label.c
+++ b/drivers/pci/pci-label.c
@@ -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;
+
+ if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 2) {
+ len = -EIO;
+ goto out;
+ }
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 (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 = {
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index ba3c3fddddc2..a002fe1a3f4c 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -41,6 +41,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.
*/
@@ -711,10 +720,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 */
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 95ac34a34bd5..f8e64971c6c3 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -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);
@@ -1531,6 +1538,17 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool locked
return 0;
}
+/**
+ * pci_disable_link_state_locked - 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.
+ *
+ * Context: Caller holds pci_bus_sem read lock.
+ *
+ * Return: 0 on success, a negative errno otherwise.
+ */
int pci_disable_link_state_locked(struct pci_dev *pdev, int state)
{
lockdep_assert_held_read(&pci_bus_sem);
@@ -1540,13 +1558,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)
{
@@ -1554,7 +1572,8 @@ int pci_disable_link_state(struct pci_dev *pdev, int state)
}
EXPORT_SYMBOL(pci_disable_link_state);
-static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked)
+static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked,
+ bool force)
{
struct pcie_link_state *link = pcie_aspm_get_link(pdev);
@@ -1571,10 +1590,25 @@ static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked)
return -EPERM;
}
+ /*
+ * Ensure the device is in D0 before enabling PCI-PM L1 PM Substates, per
+ * PCIe r6.0, sec 5.5.4.
+ */
+ if (state & PCIE_LINK_STATE_L1_SS_PCIPM) {
+ if (locked)
+ pci_set_power_state_locked(pdev, PCI_D0);
+ else
+ pci_set_power_state(pdev, PCI_D0);
+ }
+
if (!locked)
down_read(&pci_bus_sem);
mutex_lock(&aspm_lock);
link->aspm_default = pci_calc_aspm_enable_mask(state);
+
+ /* Force enable states that were previously disabled */
+ if (force)
+ link->aspm_disable &= ~link->aspm_default;
pcie_config_aspm_link(link, policy_to_aspm_state(link));
link->clkpm_default = (state & PCIE_LINK_STATE_CLKPM) ? 1 : 0;
@@ -1587,47 +1621,79 @@ static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked)
}
/**
- * pci_enable_link_state - Clear and set the default device link state so that
- * the link may be allowed to enter the specified 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. Also note that this does not enable states
- * disabled by pci_disable_link_state(). Return 0 or a negative errno.
- *
- * Note: Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per
- * PCIe r6.0, sec 5.5.4.
- *
+ * pci_enable_link_state - Enable device's link state
* @pdev: PCI device
* @state: Mask of ASPM link states to enable
+ *
+ * Enable device's link state, so the link will enter the specified 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. Also note that this
+ * does not enable states disabled by pci_disable_link_state(). Use
+ * pci_force_enable_link_state() for that.
+ *
+ * Note: The device will be transitioned to D0 state if the PCI-PM L1 Substates
+ * are getting enabled.
+ *
+ * Return: 0 on success, a negative errno otherwise.
*/
int pci_enable_link_state(struct pci_dev *pdev, int state)
{
- return __pci_enable_link_state(pdev, state, false);
+ return __pci_enable_link_state(pdev, state, false, false);
}
EXPORT_SYMBOL(pci_enable_link_state);
/**
- * pci_enable_link_state_locked - Clear and set the default device link state
- * so that the link may be allowed to enter the specified 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. Also note that this does not enable states
- * disabled by pci_disable_link_state(). Return 0 or a negative errno.
- *
- * Note: Ensure devices are in D0 before enabling PCI-PM L1 PM Substates, per
- * PCIe r6.0, sec 5.5.4.
- *
+ * pci_enable_link_state_locked - Enable device's link state
* @pdev: PCI device
* @state: Mask of ASPM link states to enable
*
+ * Enable device's link state, so the link will enter the specified 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. Also note that this
+ * does not enable states disabled by pci_disable_link_state(). Use
+ * pci_force_enable_link_state() for that.
+ *
+ * Note: The device will be transitioned to D0 state if the PCI-PM L1 Substates
+ * are getting enabled.
+ *
* Context: Caller holds pci_bus_sem read lock.
+ *
+ * Return: 0 on success, a negative errno otherwise.
*/
int pci_enable_link_state_locked(struct pci_dev *pdev, int state)
{
lockdep_assert_held_read(&pci_bus_sem);
- return __pci_enable_link_state(pdev, state, true);
+ return __pci_enable_link_state(pdev, state, true, false);
}
EXPORT_SYMBOL(pci_enable_link_state_locked);
+/**
+ * pci_force_enable_link_state - Force enable device link state
+ * @pdev: PCI device
+ * @state: Mask of ASPM link states to enable
+ *
+ * Enable device link state, so the link will enter the specified states.
+ * Unlike pci_enable_link_state(), this also re-enables states previously
+ * disabled by pci_disable_link_state() and overrides the ASPM states disabled
+ * during init (e.g., the pre-1.1 device blacklist). The caller is therefore
+ * responsible for only enabling states the device supports, typically the ones
+ * previously reported by pcie_aspm_enabled().
+ *
+ * Note that if the BIOS didn't grant ASPM control to the OS, this does nothing
+ * because we can't touch the LNKCTL register.
+ *
+ * Note: The device will be transitioned to D0 state if the PCI-PM L1 Substates
+ * are getting enabled.
+ *
+ * Return: 0 on success, a negative errno otherwise.
+ */
+int pci_force_enable_link_state(struct pci_dev *pdev, int state)
+{
+ return __pci_enable_link_state(pdev, state, false, true);
+}
+EXPORT_SYMBOL(pci_force_enable_link_state);
+
void pcie_aspm_remove_cap(struct pci_dev *pdev, u32 lnkcap)
{
if (lnkcap & PCI_EXP_LNKCAP_ASPM_L0S)
@@ -1683,15 +1749,17 @@ module_param_call(policy, pcie_aspm_set_policy, pcie_aspm_get_policy,
NULL, 0644);
/**
- * pcie_aspm_enabled - Check if PCIe ASPM has been enabled for a device.
+ * pcie_aspm_enabled - Get the enabled PCIe ASPM states for a device.
* @pdev: Target device.
*
* Relies on the upstream bridge's link_state being valid. The link_state
* is deallocated only when the last child of the bridge (i.e., @pdev or a
* sibling) is removed, and the caller should be holding a reference to
* @pdev, so this should be safe.
+ *
+ * Return: Enabled PCIe ASPM states. 0 if ASPM is disabled.
*/
-bool pcie_aspm_enabled(struct pci_dev *pdev)
+u32 pcie_aspm_enabled(struct pci_dev *pdev)
{
struct pcie_link_state *link = pcie_aspm_get_link(pdev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 27008e2ea5af..721daf5c5184 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1890,8 +1890,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 +1906,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 +1921,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;
diff --git a/drivers/pci/pwrctrl/Kconfig b/drivers/pci/pwrctrl/Kconfig
index 9eec767cda86..38aab596aa04 100644
--- a/drivers/pci/pwrctrl/Kconfig
+++ b/drivers/pci/pwrctrl/Kconfig
@@ -29,6 +29,8 @@ config PCI_PWRCTRL_TC9563
select PCI_PWRCTRL
default m if ARCH_QCOM
depends on I2C
+ depends on GPIO_TC9563
+ select REGMAP_I2C
help
Say Y here to enable the PCI Power Control driver of TC9563 PCIe
switch.
diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
index fd53a7db9875..59bc0d77d3c4 100644
--- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
+++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
@@ -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;
-
- ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_GPIO_CONFIG,
- TC9563_GPIO_MASK);
- if (ret)
- return ret;
-
- val = deassert ? TC9563_GPIO_DEASSERT_BITS : 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tc9563->cfg); i++) {
+ int err;
+
+ 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;
+
+ err = regmap_assign_bits(tc9563->regmap,
+ TC9563_RESET_GPIO,
+ BIT(i + 1), deassert);
+ if (err)
+ return err;
+ break;
+ default:
+ break;
+ }
+ }
+ }
- return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_RESET_GPIO, val);
+ 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 @@ power_off:
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);
}
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index de9bbccda21f..88624e5ff70d 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -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)
@@ -4230,6 +4235,118 @@ reset_complete:
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 },
@@ -4245,6 +4362,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 }
};
@@ -6263,6 +6383,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,
diff --git a/drivers/pci/tph.c b/drivers/pci/tph.c
index 655ffd60e62f..4d064b86befc 100644
--- a/drivers/pci/tph.c
+++ b/drivers/pci/tph.c
@@ -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);
diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
index 704e1dc8b30a..226f3e59cf60 100644
--- a/include/linux/pci-epf.h
+++ b/include/linux/pci-epf.h
@@ -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.
diff --git a/include/linux/pci.h b/include/linux/pci.h
index d31a8d107b1e..8650c627bebb 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1951,9 +1951,10 @@ int pci_disable_link_state(struct pci_dev *pdev, int state);
int pci_disable_link_state_locked(struct pci_dev *pdev, int state);
int pci_enable_link_state(struct pci_dev *pdev, int state);
int pci_enable_link_state_locked(struct pci_dev *pdev, int state);
+int pci_force_enable_link_state(struct pci_dev *pdev, int state);
void pcie_no_aspm(void);
bool pcie_aspm_support_enabled(void);
-bool pcie_aspm_enabled(struct pci_dev *pdev);
+u32 pcie_aspm_enabled(struct pci_dev *pdev);
#else
static inline int pci_disable_link_state(struct pci_dev *pdev, int state)
{ return 0; }
@@ -1963,9 +1964,11 @@ static inline int pci_enable_link_state(struct pci_dev *pdev, int state)
{ return 0; }
static inline int pci_enable_link_state_locked(struct pci_dev *pdev, int state)
{ return 0; }
+static inline int pci_force_enable_link_state(struct pci_dev *pdev, int state)
+{ return 0; }
static inline void pcie_no_aspm(void) { }
static inline bool pcie_aspm_support_enabled(void) { return false; }
-static inline bool pcie_aspm_enabled(struct pci_dev *pdev) { return false; }
+static inline u32 pcie_aspm_enabled(struct pci_dev *pdev) { return 0; }
#endif
#ifdef CONFIG_HOTPLUG_PCI
diff --git a/include/linux/soc/qcom/tc9563.h b/include/linux/soc/qcom/tc9563.h
new file mode 100644
index 000000000000..086f37a40d80
--- /dev/null
+++ b/include/linux/soc/qcom/tc9563.h
@@ -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 */