summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/PCI/endpoint/pci-endpoint.rst24
-rw-r--r--Documentation/PCI/endpoint/pci-test-howto.rst19
-rw-r--r--Documentation/PCI/endpoint/pci-vntb-howto.rst14
-rw-r--r--Documentation/PCI/msi-howto.rst6
-rw-r--r--Documentation/devicetree/bindings/pci/aspeed,ast2600-pcie.yaml182
-rw-r--r--Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml7
-rw-r--r--Documentation/devicetree/bindings/pci/loongson.yaml2
-rw-r--r--Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml1
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-apq8064.yaml170
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-apq8084.yaml109
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-ipq4019.yaml146
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-ipq5018.yaml189
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-ipq6018.yaml179
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-ipq8074.yaml165
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml183
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-msm8996.yaml156
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-qcs404.yaml131
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml168
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sdm845.yaml190
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sdx55.yaml172
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml1
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml7
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,pcie.yaml782
-rw-r--r--Documentation/devicetree/bindings/pci/qcom,sa8255p-pcie-ep.yaml110
-rw-r--r--Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml6
-rw-r--r--Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml4
-rw-r--r--Documentation/trace/events-pci.rst74
-rw-r--r--Documentation/trace/index.rst1
-rw-r--r--MAINTAINERS9
-rw-r--r--drivers/cpuidle/cpuidle-tegra.c1
-rw-r--r--drivers/misc/pci_endpoint_test.c203
-rw-r--r--drivers/pci/Makefile4
-rw-r--r--drivers/pci/bus.c21
-rw-r--r--drivers/pci/controller/Kconfig19
-rw-r--r--drivers/pci/controller/Makefile1
-rw-r--r--drivers/pci/controller/cadence/pci-j721e.c41
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence-host-common.c12
-rw-r--r--drivers/pci/controller/cadence/pcie-cadence.c4
-rw-r--r--drivers/pci/controller/dwc/Kconfig4
-rw-r--r--drivers/pci/controller/dwc/pci-dra7xx.c1
-rw-r--r--drivers/pci/controller/dwc/pci-imx6.c77
-rw-r--r--drivers/pci/controller/dwc/pci-keystone.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-artpec6.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-debugfs.c52
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-ep.c399
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-host.c229
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-plat.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.c152
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.h45
-rw-r--r--drivers/pci/controller/dwc/pcie-dw-rockchip.c101
-rw-r--r--drivers/pci/controller/dwc/pcie-keembay.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-nxp-s32g.c8
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom-ep.c62
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom.c228
-rw-r--r--drivers/pci/controller/dwc/pcie-rcar-gen4.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-sophgo.c18
-rw-r--r--drivers/pci/controller/dwc/pcie-stm32-ep.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-tegra194.c1
-rw-r--r--drivers/pci/controller/dwc/pcie-uniphier-ep.c2
-rw-r--r--drivers/pci/controller/pci-host-common.c2
-rw-r--r--drivers/pci/controller/pci-tegra.c35
-rw-r--r--drivers/pci/controller/pcie-aspeed.c1111
-rw-r--r--drivers/pci/controller/pcie-mediatek.c4
-rw-r--r--drivers/pci/controller/pcie-rzg3s-host.c37
-rw-r--r--drivers/pci/controller/pcie-xilinx.c9
-rw-r--r--drivers/pci/controller/plda/pcie-starfive.c25
-rw-r--r--drivers/pci/devres.c3
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-mhi.c2
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-ntb.c9
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-test.c271
-rw-r--r--drivers/pci/endpoint/functions/pci-epf-vntb.c9
-rw-r--r--drivers/pci/endpoint/pci-ep-cfs.c23
-rw-r--r--drivers/pci/endpoint/pci-epc-core.c8
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c31
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c3
-rw-r--r--drivers/pci/hotplug/pnv_php.c2
-rw-r--r--drivers/pci/hotplug/shpchp_core.c3
-rw-r--r--drivers/pci/iov.c9
-rw-r--r--drivers/pci/of.c1
-rw-r--r--drivers/pci/p2pdma.c10
-rw-r--r--drivers/pci/pci-acpi.c59
-rw-r--r--drivers/pci/pci-driver.c36
-rw-r--r--drivers/pci/pci-sysfs.c2
-rw-r--r--drivers/pci/pci.c116
-rw-r--r--drivers/pci/pci.h115
-rw-r--r--drivers/pci/pcie/aer.c29
-rw-r--r--drivers/pci/pcie/bwctrl.c7
-rw-r--r--drivers/pci/pcie/portdrv.c55
-rw-r--r--drivers/pci/pcie/ptm.c5
-rw-r--r--drivers/pci/probe.c249
-rw-r--r--drivers/pci/pwrctrl/Kconfig1
-rw-r--r--drivers/pci/pwrctrl/core.c260
-rw-r--r--drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c84
-rw-r--r--drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c226
-rw-r--r--drivers/pci/pwrctrl/slot.c101
-rw-r--r--drivers/pci/quirks.c91
-rw-r--r--drivers/pci/remove.c20
-rw-r--r--drivers/pci/search.c2
-rw-r--r--drivers/pci/setup-bus.c646
-rw-r--r--drivers/pci/setup-cardbus.c306
-rw-r--r--drivers/pci/setup-res.c2
-rw-r--r--drivers/pci/trace.c11
-rw-r--r--drivers/pcmcia/yenta_socket.c2
-rw-r--r--drivers/usb/host/ehci-pci.c1
-rw-r--r--include/linux/ioport.h2
-rw-r--r--include/linux/pci-epc.h9
-rw-r--r--include/linux/pci-epf.h23
-rw-r--r--include/linux/pci-p2pdma.h2
-rw-r--r--include/linux/pci-pwrctrl.h16
-rw-r--r--include/linux/pci.h14
-rw-r--r--include/linux/pci_ids.h2
-rw-r--r--include/trace/events/pci.h129
-rw-r--r--include/uapi/linux/pci.h7
-rw-r--r--include/uapi/linux/pci_regs.h5
-rw-r--r--include/uapi/linux/pcitest.h1
-rw-r--r--kernel/irq/irqdomain.c1
-rw-r--r--kernel/resource.c2
-rw-r--r--tools/testing/selftests/pci_endpoint/pci_endpoint_test.c17
118 files changed, 6649 insertions, 2512 deletions
diff --git a/Documentation/PCI/endpoint/pci-endpoint.rst b/Documentation/PCI/endpoint/pci-endpoint.rst
index 0741c8cbd74e..4697377adeae 100644
--- a/Documentation/PCI/endpoint/pci-endpoint.rst
+++ b/Documentation/PCI/endpoint/pci-endpoint.rst
@@ -95,6 +95,30 @@ by the PCI endpoint function driver.
Register space of the function driver is usually configured
using this API.
+ Some endpoint controllers also support calling pci_epc_set_bar() again
+ for the same BAR (without calling pci_epc_clear_bar()) to update inbound
+ address translations after the host has programmed the BAR base address.
+ Endpoint function drivers can check this capability via the
+ dynamic_inbound_mapping EPC feature bit.
+
+ When pci_epf_bar.num_submap is non-zero, the endpoint function driver is
+ requesting BAR subrange mapping using pci_epf_bar.submap. This requires
+ the EPC to advertise support via the subrange_mapping EPC feature bit.
+
+ When an EPF driver wants to make use of the inbound subrange mapping
+ feature, it requires that the BAR base address has been programmed by
+ the host during enumeration. Thus, it needs to call pci_epc_set_bar()
+ twice for the same BAR (requires dynamic_inbound_mapping): first with
+ num_submap set to zero and configuring the BAR size, then after the PCIe
+ link is up and the host enumerates the endpoint and programs the BAR
+ base address, again with num_submap set to non-zero value.
+
+ Note that when making use of the inbound subrange mapping feature, the
+ EPF driver must not call pci_epc_clear_bar() between the two
+ pci_epc_set_bar() calls, because clearing the BAR can clear/disable the
+ BAR register or BAR decode on the endpoint while the host still expects
+ the assigned BAR address to remain valid.
+
* pci_epc_clear_bar()
The PCI endpoint function driver should use pci_epc_clear_bar() to reset
diff --git a/Documentation/PCI/endpoint/pci-test-howto.rst b/Documentation/PCI/endpoint/pci-test-howto.rst
index dd66858cde46..a822866b1fb0 100644
--- a/Documentation/PCI/endpoint/pci-test-howto.rst
+++ b/Documentation/PCI/endpoint/pci-test-howto.rst
@@ -84,6 +84,25 @@ device, the following commands can be used::
# echo 32 > functions/pci_epf_test/func1/msi_interrupts
# echo 2048 > functions/pci_epf_test/func1/msix_interrupts
+By default, pci-epf-test uses the following BAR sizes::
+
+ # grep . functions/pci_epf_test/func1/pci_epf_test.0/bar?_size
+ functions/pci_epf_test/func1/pci_epf_test.0/bar0_size:131072
+ functions/pci_epf_test/func1/pci_epf_test.0/bar1_size:131072
+ functions/pci_epf_test/func1/pci_epf_test.0/bar2_size:131072
+ functions/pci_epf_test/func1/pci_epf_test.0/bar3_size:131072
+ functions/pci_epf_test/func1/pci_epf_test.0/bar4_size:131072
+ functions/pci_epf_test/func1/pci_epf_test.0/bar5_size:1048576
+
+The user can override a default value using e.g.::
+ # echo 1048576 > functions/pci_epf_test/func1/pci_epf_test.0/bar1_size
+
+Overriding the default BAR sizes can only be done before binding the
+pci-epf-test device to a PCI endpoint controller driver.
+
+Note: Some endpoint controllers might have fixed-size BARs or reserved BARs;
+for such controllers, the corresponding BAR size in configfs will be ignored.
+
Binding pci-epf-test Device to EP Controller
--------------------------------------------
diff --git a/Documentation/PCI/endpoint/pci-vntb-howto.rst b/Documentation/PCI/endpoint/pci-vntb-howto.rst
index 9a7a2f0a6849..3679f5c30254 100644
--- a/Documentation/PCI/endpoint/pci-vntb-howto.rst
+++ b/Documentation/PCI/endpoint/pci-vntb-howto.rst
@@ -52,14 +52,14 @@ pci-epf-vntb device, the following commands can be used::
# cd /sys/kernel/config/pci_ep/
# mkdir functions/pci_epf_vntb/func1
-The "mkdir func1" above creates the pci-epf-ntb function device that will
+The "mkdir func1" above creates the pci-epf-vntb function device that will
be probed by pci_epf_vntb driver.
The PCI endpoint framework populates the directory with the following
configurable fields::
- # ls functions/pci_epf_ntb/func1
- baseclass_code deviceid msi_interrupts pci-epf-ntb.0
+ # ls functions/pci_epf_vntb/func1
+ baseclass_code deviceid msi_interrupts pci-epf-vntb.0
progif_code secondary subsys_id vendorid
cache_line_size interrupt_pin msix_interrupts primary
revid subclass_code subsys_vendor_id
@@ -111,13 +111,13 @@ A sample configuration for virtual NTB driver for virtual PCI bus::
# echo 0x080A > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vntb_pid
# echo 0x10 > functions/pci_epf_vntb/func1/pci_epf_vntb.0/vbus_number
-Binding pci-epf-ntb Device to EP Controller
+Binding pci-epf-vntb Device to EP Controller
--------------------------------------------
NTB function device should be attached to PCI endpoint controllers
connected to the host.
- # ln -s controllers/5f010000.pcie_ep functions/pci-epf-ntb/func1/primary
+ # ln -s controllers/5f010000.pcie_ep functions/pci_epf_vntb/func1/primary
Once the above step is completed, the PCI endpoint controllers are ready to
establish a link with the host.
@@ -139,7 +139,7 @@ lspci Output at Host side
-------------------------
Note that the devices listed here correspond to the values populated in
-"Creating pci-epf-ntb Device" section above::
+"Creating pci-epf-vntb Device" section above::
# lspci
00:00.0 PCI bridge: Freescale Semiconductor Inc Device 0000 (rev 01)
@@ -152,7 +152,7 @@ lspci Output at EP Side / Virtual PCI bus
-----------------------------------------
Note that the devices listed here correspond to the values populated in
-"Creating pci-epf-ntb Device" section above::
+"Creating pci-epf-vntb Device" section above::
# lspci
10:00.0 Unassigned class [ffff]: Dawicontrol Computersysteme GmbH Device 1234 (rev ff)
diff --git a/Documentation/PCI/msi-howto.rst b/Documentation/PCI/msi-howto.rst
index 0692c9aec66f..667ebe2156b4 100644
--- a/Documentation/PCI/msi-howto.rst
+++ b/Documentation/PCI/msi-howto.rst
@@ -98,7 +98,7 @@ function::
which allocates up to max_vecs interrupt vectors for a PCI device. It
returns the number of vectors allocated or a negative error. If the device
-has a requirements for a minimum number of vectors the driver can pass a
+has a requirement for a minimum number of vectors the driver can pass a
min_vecs argument set to this limit, and the PCI core will return -ENOSPC
if it can't meet the minimum number of vectors.
@@ -127,7 +127,7 @@ not be able to allocate as many vectors for MSI as it could for MSI-X. On
some platforms, MSI interrupts must all be targeted at the same set of CPUs
whereas MSI-X interrupts can all be targeted at different CPUs.
-If a device supports neither MSI-X or MSI it will fall back to a single
+If a device supports neither MSI-X nor MSI it will fall back to a single
legacy IRQ vector.
The typical usage of MSI or MSI-X interrupts is to allocate as many vectors
@@ -203,7 +203,7 @@ How to tell whether MSI/MSI-X is enabled on a device
----------------------------------------------------
Using 'lspci -v' (as root) may show some devices with "MSI", "Message
-Signalled Interrupts" or "MSI-X" capabilities. Each of these capabilities
+Signaled Interrupts" or "MSI-X" capabilities. Each of these capabilities
has an 'Enable' flag which is followed with either "+" (enabled)
or "-" (disabled).
diff --git a/Documentation/devicetree/bindings/pci/aspeed,ast2600-pcie.yaml b/Documentation/devicetree/bindings/pci/aspeed,ast2600-pcie.yaml
new file mode 100644
index 000000000000..d9478249418a
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/aspeed,ast2600-pcie.yaml
@@ -0,0 +1,182 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/aspeed,ast2600-pcie.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASPEED PCIe Root Complex Controller
+
+maintainers:
+ - Jacky Chou <jacky_chou@aspeedtech.com>
+
+description:
+ The ASPEED PCIe Root Complex controller provides PCI Express Root Complex
+ functionality for ASPEED SoCs, such as the AST2600 and AST2700.
+ This controller enables connectivity to PCIe endpoint devices, supporting
+ memory and I/O windows, MSI and INTx interrupts, and integration with
+ the SoC's clock, reset, and pinctrl subsystems. On AST2600, the PCIe Root
+ Port device number is always 8.
+
+properties:
+ compatible:
+ enum:
+ - aspeed,ast2600-pcie
+ - aspeed,ast2700-pcie
+
+ reg:
+ maxItems: 1
+
+ ranges:
+ minItems: 2
+ maxItems: 2
+
+ interrupts:
+ maxItems: 1
+ description: INTx and MSI interrupt
+
+ resets:
+ items:
+ - description: PCIe controller reset
+
+ reset-names:
+ items:
+ - const: h2x
+
+ aspeed,ahbc:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the ASPEED AHB Controller (AHBC) syscon node.
+ This reference is used by the PCIe controller to access
+ system-level configuration registers related to the AHB bus.
+ To enable AHB access for the PCIe controller.
+
+ aspeed,pciecfg:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Phandle to the ASPEED PCIe configuration syscon node.
+ This reference allows the PCIe controller to access
+ SoC-specific PCIe configuration registers. There are the others
+ functions such PCIe RC and PCIe EP will use this common register
+ to configure the SoC interfaces.
+
+ interrupt-controller: true
+
+patternProperties:
+ "^pcie@[0-9a-f]+,0$":
+ type: object
+ $ref: /schemas/pci/pci-pci-bridge.yaml#
+
+ properties:
+ reg:
+ maxItems: 1
+
+ resets:
+ items:
+ - description: PERST# signal
+
+ reset-names:
+ items:
+ - const: perst
+
+ clocks:
+ maxItems: 1
+
+ phys:
+ maxItems: 1
+
+ required:
+ - resets
+ - reset-names
+ - clocks
+ - phys
+ - ranges
+
+ unevaluatedProperties: false
+
+allOf:
+ - $ref: /schemas/pci/pci-host-bridge.yaml#
+ - $ref: /schemas/interrupt-controller/msi-controller.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: aspeed,ast2600-pcie
+ then:
+ required:
+ - aspeed,ahbc
+ else:
+ properties:
+ aspeed,ahbc: false
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: aspeed,ast2700-pcie
+ then:
+ required:
+ - aspeed,pciecfg
+ else:
+ properties:
+ aspeed,pciecfg: false
+
+required:
+ - reg
+ - interrupts
+ - bus-range
+ - ranges
+ - resets
+ - reset-names
+ - msi-controller
+ - interrupt-controller
+ - interrupt-map-mask
+ - interrupt-map
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/ast2600-clock.h>
+
+ pcie0: pcie@1e770000 {
+ compatible = "aspeed,ast2600-pcie";
+ device_type = "pci";
+ reg = <0x1e770000 0x100>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ interrupts = <GIC_SPI 168 IRQ_TYPE_LEVEL_HIGH>;
+ bus-range = <0x00 0xff>;
+
+ ranges = <0x01000000 0x0 0x00018000 0x00018000 0x0 0x00008000
+ 0x02000000 0x0 0x60000000 0x60000000 0x0 0x20000000>;
+
+ resets = <&syscon ASPEED_RESET_H2X>;
+ reset-names = "h2x";
+
+ #interrupt-cells = <1>;
+ msi-controller;
+
+ aspeed,ahbc = <&ahbc>;
+
+ interrupt-controller;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &pcie0 0>,
+ <0 0 0 2 &pcie0 1>,
+ <0 0 0 3 &pcie0 2>,
+ <0 0 0 4 &pcie0 3>;
+
+ pcie@8,0 {
+ compatible = "pciclass,0604";
+ reg = <0x00004000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ resets = <&syscon ASPEED_RESET_PCIE_RC_O>;
+ reset-names = "perst";
+ clocks = <&syscon ASPEED_CLK_GATE_BCLK>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_pcierc1_default>;
+ phys = <&pcie_phy1>;
+ ranges;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
index ca5f2970f217..12a01f7a5744 100644
--- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
@@ -44,7 +44,7 @@ properties:
clock-names:
minItems: 3
- maxItems: 5
+ maxItems: 6
interrupts:
minItems: 1
@@ -212,14 +212,17 @@ allOf:
then:
properties:
clocks:
- maxItems: 5
+ minItems: 5
+ maxItems: 6
clock-names:
+ minItems: 5
items:
- const: pcie
- const: pcie_bus
- const: pcie_phy
- const: pcie_aux
- const: ref
+ - const: extref # Optional
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/pci/loongson.yaml b/Documentation/devicetree/bindings/pci/loongson.yaml
index e5bba63aa947..26e77218b901 100644
--- a/Documentation/devicetree/bindings/pci/loongson.yaml
+++ b/Documentation/devicetree/bindings/pci/loongson.yaml
@@ -32,6 +32,8 @@ properties:
minItems: 1
maxItems: 3
+ msi-parent: true
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
index 0278845701ce..4db700fc36ba 100644
--- a/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
+++ b/Documentation/devicetree/bindings/pci/mediatek-pcie-gen3.yaml
@@ -48,6 +48,7 @@ properties:
oneOf:
- items:
- enum:
+ - mediatek,mt7981-pcie
- mediatek,mt7986-pcie
- mediatek,mt8188-pcie
- mediatek,mt8195-pcie
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-apq8064.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-apq8064.yaml
new file mode 100644
index 000000000000..eb5b81d1defc
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-apq8064.yaml
@@ -0,0 +1,170 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-apq8064.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm APQ8064/IPQ8064 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - qcom,pcie-apq8064
+ - qcom,pcie-ipq8064
+ - qcom,pcie-ipq8064-v2
+
+ reg:
+ maxItems: 4
+
+ reg-names:
+ items:
+ - const: dbi
+ - const: elbi
+ - const: parf
+ - const: config
+
+ clocks:
+ minItems: 3
+ maxItems: 5
+
+ clock-names:
+ minItems: 3
+ items:
+ - const: core # Clocks the pcie hw block
+ - const: iface # Configuration AHB clock
+ - const: phy
+ - const: aux
+ - const: ref
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ items:
+ - const: msi
+
+ resets:
+ minItems: 5
+ maxItems: 6
+
+ reset-names:
+ minItems: 5
+ items:
+ - const: axi
+ - const: ahb
+ - const: por
+ - const: pci
+ - const: phy
+ - const: ext
+
+ vdda-supply:
+ description: A phandle to the core analog power supply
+
+ vdda_phy-supply:
+ description: A phandle to the core analog power supply for PHY
+
+ vdda_refclk-supply:
+ description: A phandle to the core analog power supply for IC which generates reference clock
+
+required:
+ - resets
+ - reset-names
+ - vdda-supply
+ - vdda_phy-supply
+ - vdda_refclk-supply
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,pcie-apq8064
+ then:
+ properties:
+ clocks:
+ maxItems: 3
+ clock-names:
+ maxItems: 3
+ resets:
+ maxItems: 5
+ reset-names:
+ maxItems: 5
+ else:
+ properties:
+ clocks:
+ minItems: 5
+ clock-names:
+ minItems: 5
+ resets:
+ minItems: 6
+ reset-names:
+ minItems: 6
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-msm8960.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/qcom,gcc-msm8960.h>
+
+ pcie@1b500000 {
+ compatible = "qcom,pcie-apq8064";
+ reg = <0x1b500000 0x1000>,
+ <0x1b502000 0x80>,
+ <0x1b600000 0x100>,
+ <0x0ff00000 0x100000>;
+ reg-names = "dbi", "elbi", "parf", "config";
+ ranges = <0x81000000 0x0 0x00000000 0x0fe00000 0x0 0x00100000>, /* I/O */
+ <0x82000000 0x0 0x08000000 0x08000000 0x0 0x07e00000>; /* mem */
+
+ device_type = "pci";
+ linux,pci-domain = <0>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ clocks = <&gcc PCIE_A_CLK>,
+ <&gcc PCIE_H_CLK>,
+ <&gcc PCIE_PHY_REF_CLK>;
+ clock-names = "core", "iface", "phy";
+
+ interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ resets = <&gcc PCIE_ACLK_RESET>,
+ <&gcc PCIE_HCLK_RESET>,
+ <&gcc PCIE_POR_RESET>,
+ <&gcc PCIE_PCI_RESET>,
+ <&gcc PCIE_PHY_RESET>;
+ reset-names = "axi", "ahb", "por", "pci", "phy";
+
+ perst-gpios = <&tlmm_pinmux 27 GPIO_ACTIVE_LOW>;
+ vdda-supply = <&pm8921_s3>;
+ vdda_phy-supply = <&pm8921_lvs6>;
+ vdda_refclk-supply = <&v3p3_fixed>;
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-apq8084.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-apq8084.yaml
new file mode 100644
index 000000000000..a6403a3de076
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-apq8084.yaml
@@ -0,0 +1,109 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-apq8084.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm APQ8084 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - qcom,pcie-apq8084
+
+ reg:
+ minItems: 4
+ maxItems: 5
+
+ reg-names:
+ minItems: 4
+ items:
+ - const: parf
+ - const: dbi
+ - const: elbi
+ - const: config
+ - const: mhi
+
+ clocks:
+ maxItems: 4
+
+ clock-names:
+ items:
+ - const: iface # Configuration AHB clock
+ - const: master_bus # Master AXI clock
+ - const: slave_bus # Slave AXI clock
+ - const: aux
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ items:
+ - const: msi
+
+ resets:
+ maxItems: 1
+
+ reset-names:
+ items:
+ - const: core
+
+ vdda-supply:
+ description: A phandle to the core analog power supply
+
+required:
+ - power-domains
+ - resets
+ - reset-names
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/gpio/gpio.h>
+ pcie@fc520000 {
+ compatible = "qcom,pcie-apq8084";
+ reg = <0xfc520000 0x2000>,
+ <0xff000000 0x1000>,
+ <0xff001000 0x1000>,
+ <0xff002000 0x2000>;
+ reg-names = "parf", "dbi", "elbi", "config";
+ device_type = "pci";
+ linux,pci-domain = <0>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges = <0x81000000 0 0 0xff200000 0 0x00100000>,
+ <0x82000000 0 0x00300000 0xff300000 0 0x00d00000>;
+ interrupts = <GIC_SPI 243 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc 324>,
+ <&gcc 325>,
+ <&gcc 327>,
+ <&gcc 323>;
+ clock-names = "iface", "master_bus", "slave_bus", "aux";
+ resets = <&gcc 81>;
+ reset-names = "core";
+ power-domains = <&gcc 1>;
+ vdda-supply = <&pma8084_l3>;
+ phys = <&pciephy0>;
+ phy-names = "pciephy";
+ perst-gpios = <&tlmm 70 GPIO_ACTIVE_LOW>;
+ pinctrl-0 = <&pcie0_pins_default>;
+ pinctrl-names = "default";
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq4019.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq4019.yaml
new file mode 100644
index 000000000000..fd6ecd1c43a1
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq4019.yaml
@@ -0,0 +1,146 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-ipq4019.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ4019 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - qcom,pcie-ipq4019
+
+ reg:
+ maxItems: 4
+
+ reg-names:
+ items:
+ - const: dbi
+ - const: elbi
+ - const: parf
+ - const: config
+
+ clocks:
+ maxItems: 3
+
+ clock-names:
+ items:
+ - const: aux
+ - const: master_bus # Master AXI clock
+ - const: slave_bus # Slave AXI clock
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ items:
+ - const: msi
+
+ resets:
+ maxItems: 12
+
+ reset-names:
+ items:
+ - const: axi_m # AXI master reset
+ - const: axi_s # AXI slave reset
+ - const: pipe
+ - const: axi_m_vmid
+ - const: axi_s_xpu
+ - const: parf
+ - const: phy
+ - const: axi_m_sticky # AXI master sticky reset
+ - const: pipe_sticky
+ - const: pwr
+ - const: ahb
+ - const: phy_ahb
+
+required:
+ - resets
+ - reset-names
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-ipq4019.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ pcie@40000000 {
+ compatible = "qcom,pcie-ipq4019";
+ reg = <0x40000000 0xf1d>,
+ <0x40000f20 0xa8>,
+ <0x80000 0x2000>,
+ <0x40100000 0x1000>;
+ reg-names = "dbi", "elbi", "parf", "config";
+ ranges = <0x81000000 0x0 0x00000000 0x40200000 0x0 0x00100000>,
+ <0x82000000 0x0 0x40300000 0x40300000 0x0 0x00d00000>;
+
+ device_type = "pci";
+ linux,pci-domain = <0>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ clocks = <&gcc GCC_PCIE_AHB_CLK>,
+ <&gcc GCC_PCIE_AXI_M_CLK>,
+ <&gcc GCC_PCIE_AXI_S_CLK>;
+ clock-names = "aux",
+ "master_bus",
+ "slave_bus";
+
+ interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ resets = <&gcc PCIE_AXI_M_ARES>,
+ <&gcc PCIE_AXI_S_ARES>,
+ <&gcc PCIE_PIPE_ARES>,
+ <&gcc PCIE_AXI_M_VMIDMT_ARES>,
+ <&gcc PCIE_AXI_S_XPU_ARES>,
+ <&gcc PCIE_PARF_XPU_ARES>,
+ <&gcc PCIE_PHY_ARES>,
+ <&gcc PCIE_AXI_M_STICKY_ARES>,
+ <&gcc PCIE_PIPE_STICKY_ARES>,
+ <&gcc PCIE_PWR_ARES>,
+ <&gcc PCIE_AHB_ARES>,
+ <&gcc PCIE_PHY_AHB_ARES>;
+ reset-names = "axi_m",
+ "axi_s",
+ "pipe",
+ "axi_m_vmid",
+ "axi_s_xpu",
+ "parf",
+ "phy",
+ "axi_m_sticky",
+ "pipe_sticky",
+ "pwr",
+ "ahb",
+ "phy_ahb";
+
+ perst-gpios = <&tlmm 38 GPIO_ACTIVE_LOW>;
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq5018.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq5018.yaml
new file mode 100644
index 000000000000..20c2c946f474
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq5018.yaml
@@ -0,0 +1,189 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-ipq5018.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ5018 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - qcom,pcie-ipq5018
+
+ reg:
+ minItems: 5
+ maxItems: 6
+
+ reg-names:
+ minItems: 5
+ items:
+ - const: dbi
+ - const: elbi
+ - const: atu
+ - const: parf
+ - const: config
+ - const: mhi
+
+ clocks:
+ maxItems: 6
+
+ clock-names:
+ items:
+ - const: iface # PCIe to SysNOC BIU clock
+ - const: axi_m # AXI Master clock
+ - const: axi_s # AXI Slave clock
+ - const: ahb
+ - const: aux
+ - const: axi_bridge
+
+ interrupts:
+ maxItems: 9
+
+ interrupt-names:
+ items:
+ - const: msi0
+ - const: msi1
+ - const: msi2
+ - const: msi3
+ - const: msi4
+ - const: msi5
+ - const: msi6
+ - const: msi7
+ - const: global
+
+ resets:
+ maxItems: 8
+
+ reset-names:
+ items:
+ - const: pipe
+ - const: sleep
+ - const: sticky # Core sticky reset
+ - const: axi_m # AXI master reset
+ - const: axi_s # AXI slave reset
+ - const: ahb
+ - const: axi_m_sticky # AXI master sticky reset
+ - const: axi_s_sticky # AXI slave sticky reset
+
+required:
+ - resets
+ - reset-names
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-ipq5018.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/qcom,gcc-ipq5018.h>
+
+ pcie@a0000000 {
+ compatible = "qcom,pcie-ipq5018";
+ reg = <0xa0000000 0xf1d>,
+ <0xa0000f20 0xa8>,
+ <0xa0001000 0x1000>,
+ <0x00080000 0x3000>,
+ <0xa0100000 0x1000>,
+ <0x00083000 0x1000>;
+ reg-names = "dbi",
+ "elbi",
+ "atu",
+ "parf",
+ "config",
+ "mhi";
+ ranges = <0x01000000 0 0x00000000 0xa0200000 0 0x00100000>,
+ <0x02000000 0 0xa0300000 0xa0300000 0 0x10000000>;
+
+ device_type = "pci";
+ linux,pci-domain = <0>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <2>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ /* The controller supports Gen3, but the connected PHY is Gen2-capable */
+ max-link-speed = <2>;
+
+ clocks = <&gcc GCC_SYS_NOC_PCIE0_AXI_CLK>,
+ <&gcc GCC_PCIE0_AXI_M_CLK>,
+ <&gcc GCC_PCIE0_AXI_S_CLK>,
+ <&gcc GCC_PCIE0_AHB_CLK>,
+ <&gcc GCC_PCIE0_AUX_CLK>,
+ <&gcc GCC_PCIE0_AXI_S_BRIDGE_CLK>;
+ clock-names = "iface",
+ "axi_m",
+ "axi_s",
+ "ahb",
+ "aux",
+ "axi_bridge";
+
+ msi-map = <0x0 &v2m0 0x0 0xff8>;
+
+ interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi0",
+ "msi1",
+ "msi2",
+ "msi3",
+ "msi4",
+ "msi5",
+ "msi6",
+ "msi7",
+ "global";
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc 0 GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &intc 0 GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &intc 0 GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &intc 0 GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
+
+ phys = <&pcie0_phy>;
+ phy-names = "pciephy";
+
+ resets = <&gcc GCC_PCIE0_PIPE_ARES>,
+ <&gcc GCC_PCIE0_SLEEP_ARES>,
+ <&gcc GCC_PCIE0_CORE_STICKY_ARES>,
+ <&gcc GCC_PCIE0_AXI_MASTER_ARES>,
+ <&gcc GCC_PCIE0_AXI_SLAVE_ARES>,
+ <&gcc GCC_PCIE0_AHB_ARES>,
+ <&gcc GCC_PCIE0_AXI_MASTER_STICKY_ARES>,
+ <&gcc GCC_PCIE0_AXI_SLAVE_STICKY_ARES>;
+ reset-names = "pipe",
+ "sleep",
+ "sticky",
+ "axi_m",
+ "axi_s",
+ "ahb",
+ "axi_m_sticky",
+ "axi_s_sticky";
+
+ perst-gpios = <&tlmm 15 GPIO_ACTIVE_LOW>;
+ wake-gpios = <&tlmm 16 GPIO_ACTIVE_LOW>;
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq6018.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq6018.yaml
new file mode 100644
index 000000000000..6843570eb051
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq6018.yaml
@@ -0,0 +1,179 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-ipq6018.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ6018 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - qcom,pcie-ipq6018
+ - qcom,pcie-ipq8074-gen3
+
+ reg:
+ minItems: 5
+ maxItems: 6
+
+ reg-names:
+ minItems: 5
+ items:
+ - const: dbi
+ - const: elbi
+ - const: atu
+ - const: parf
+ - const: config
+ - const: mhi
+
+ clocks:
+ maxItems: 5
+
+ clock-names:
+ items:
+ - const: iface # PCIe to SysNOC BIU clock
+ - const: axi_m # AXI Master clock
+ - const: axi_s # AXI Slave clock
+ - const: axi_bridge
+ - const: rchng
+
+ interrupts:
+ maxItems: 9
+
+ interrupt-names:
+ items:
+ - const: msi0
+ - const: msi1
+ - const: msi2
+ - const: msi3
+ - const: msi4
+ - const: msi5
+ - const: msi6
+ - const: msi7
+ - const: global
+
+ resets:
+ maxItems: 8
+
+ reset-names:
+ items:
+ - const: pipe
+ - const: sleep
+ - const: sticky # Core sticky reset
+ - const: axi_m # AXI master reset
+ - const: axi_s # AXI slave reset
+ - const: ahb
+ - const: axi_m_sticky # AXI master sticky reset
+ - const: axi_s_sticky # AXI slave sticky reset
+
+required:
+ - resets
+ - reset-names
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-ipq6018.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/qcom,gcc-ipq6018.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@20000000 {
+ compatible = "qcom,pcie-ipq6018";
+ reg = <0x0 0x20000000 0x0 0xf1d>,
+ <0x0 0x20000f20 0x0 0xa8>,
+ <0x0 0x20001000 0x0 0x1000>,
+ <0x0 0x80000 0x0 0x4000>,
+ <0x0 0x20100000 0x0 0x1000>;
+ reg-names = "dbi", "elbi", "atu", "parf", "config";
+ ranges = <0x81000000 0x0 0x00000000 0x0 0x20200000 0x0 0x10000>,
+ <0x82000000 0x0 0x20220000 0x0 0x20220000 0x0 0xfde0000>;
+
+ device_type = "pci";
+ linux,pci-domain = <0>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ max-link-speed = <3>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ clocks = <&gcc GCC_SYS_NOC_PCIE0_AXI_CLK>,
+ <&gcc GCC_PCIE0_AXI_M_CLK>,
+ <&gcc GCC_PCIE0_AXI_S_CLK>,
+ <&gcc GCC_PCIE0_AXI_S_BRIDGE_CLK>,
+ <&gcc PCIE0_RCHNG_CLK>;
+ clock-names = "iface",
+ "axi_m",
+ "axi_s",
+ "axi_bridge",
+ "rchng";
+
+ interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi0",
+ "msi1",
+ "msi2",
+ "msi3",
+ "msi4",
+ "msi5",
+ "msi6",
+ "msi7",
+ "global";
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc 0 0 GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc 0 0 GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc 0 0 GIC_SPI 79 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc 0 0 GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ phys = <&pcie_phy>;
+ phy-names = "pciephy";
+
+ resets = <&gcc GCC_PCIE0_PIPE_ARES>,
+ <&gcc GCC_PCIE0_SLEEP_ARES>,
+ <&gcc GCC_PCIE0_CORE_STICKY_ARES>,
+ <&gcc GCC_PCIE0_AXI_MASTER_ARES>,
+ <&gcc GCC_PCIE0_AXI_SLAVE_ARES>,
+ <&gcc GCC_PCIE0_AHB_ARES>,
+ <&gcc GCC_PCIE0_AXI_MASTER_STICKY_ARES>,
+ <&gcc GCC_PCIE0_AXI_SLAVE_STICKY_ARES>;
+ reset-names = "pipe",
+ "sleep",
+ "sticky",
+ "axi_m",
+ "axi_s",
+ "ahb",
+ "axi_m_sticky",
+ "axi_s_sticky";
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq8074.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq8074.yaml
new file mode 100644
index 000000000000..da975f943a7b
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq8074.yaml
@@ -0,0 +1,165 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-ipq8074.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ8074 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - qcom,pcie-ipq8074
+
+ reg:
+ maxItems: 4
+
+ reg-names:
+ items:
+ - const: dbi
+ - const: elbi
+ - const: parf
+ - const: config
+
+ clocks:
+ maxItems: 5
+
+ clock-names:
+ items:
+ - const: iface # PCIe to SysNOC BIU clock
+ - const: axi_m # AXI Master clock
+ - const: axi_s # AXI Slave clock
+ - const: ahb
+ - const: aux
+
+ interrupts:
+ maxItems: 9
+
+ interrupt-names:
+ items:
+ - const: msi0
+ - const: msi1
+ - const: msi2
+ - const: msi3
+ - const: msi4
+ - const: msi5
+ - const: msi6
+ - const: msi7
+ - const: global
+
+ resets:
+ maxItems: 7
+
+ reset-names:
+ items:
+ - const: pipe
+ - const: sleep
+ - const: sticky # Core sticky reset
+ - const: axi_m # AXI master reset
+ - const: axi_s # AXI slave reset
+ - const: ahb
+ - const: axi_m_sticky # AXI master sticky reset
+
+required:
+ - resets
+ - reset-names
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-ipq8074.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ pcie@10000000 {
+ compatible = "qcom,pcie-ipq8074";
+ reg = <0x10000000 0xf1d>,
+ <0x10000f20 0xa8>,
+ <0x00088000 0x2000>,
+ <0x10100000 0x1000>;
+ reg-names = "dbi", "elbi", "parf", "config";
+ ranges = <0x81000000 0x0 0x00000000 0x10200000 0x0 0x10000>, /* I/O */
+ <0x82000000 0x0 0x10220000 0x10220000 0x0 0xfde0000>; /* MEM */
+
+ device_type = "pci";
+ linux,pci-domain = <1>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ max-link-speed = <2>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ clocks = <&gcc GCC_SYS_NOC_PCIE1_AXI_CLK>,
+ <&gcc GCC_PCIE1_AXI_M_CLK>,
+ <&gcc GCC_PCIE1_AXI_S_CLK>,
+ <&gcc GCC_PCIE1_AHB_CLK>,
+ <&gcc GCC_PCIE1_AUX_CLK>;
+ clock-names = "iface",
+ "axi_m",
+ "axi_s",
+ "ahb",
+ "aux";
+
+ interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi0",
+ "msi1",
+ "msi2",
+ "msi3",
+ "msi4",
+ "msi5",
+ "msi6",
+ "msi7",
+ "global";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc 0 GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc 0 GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc 0 GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc 0 GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ phys = <&pcie_qmp1>;
+ phy-names = "pciephy";
+
+ resets = <&gcc GCC_PCIE1_PIPE_ARES>,
+ <&gcc GCC_PCIE1_SLEEP_ARES>,
+ <&gcc GCC_PCIE1_CORE_STICKY_ARES>,
+ <&gcc GCC_PCIE1_AXI_MASTER_ARES>,
+ <&gcc GCC_PCIE1_AXI_SLAVE_ARES>,
+ <&gcc GCC_PCIE1_AHB_ARES>,
+ <&gcc GCC_PCIE1_AXI_MASTER_STICKY_ARES>;
+ reset-names = "pipe",
+ "sleep",
+ "sticky",
+ "axi_m",
+ "axi_s",
+ "ahb",
+ "axi_m_sticky";
+
+ perst-gpios = <&tlmm 58 GPIO_ACTIVE_LOW>;
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml
new file mode 100644
index 000000000000..4be342cc04e1
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-ipq9574.yaml
@@ -0,0 +1,183 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-ipq9574.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ9574 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - qcom,pcie-ipq9574
+ - items:
+ - enum:
+ - qcom,pcie-ipq5332
+ - qcom,pcie-ipq5424
+ - const: qcom,pcie-ipq9574
+
+ reg:
+ maxItems: 6
+
+ reg-names:
+ items:
+ - const: dbi
+ - const: elbi
+ - const: atu
+ - const: parf
+ - const: config
+ - const: mhi
+
+ clocks:
+ maxItems: 6
+
+ clock-names:
+ items:
+ - const: axi_m # AXI Master clock
+ - const: axi_s # AXI Slave clock
+ - const: axi_bridge
+ - const: rchng
+ - const: ahb
+ - const: aux
+
+ interrupts:
+ minItems: 8
+ maxItems: 9
+
+ interrupt-names:
+ minItems: 8
+ items:
+ - const: msi0
+ - const: msi1
+ - const: msi2
+ - const: msi3
+ - const: msi4
+ - const: msi5
+ - const: msi6
+ - const: msi7
+ - const: global
+
+ resets:
+ maxItems: 8
+
+ reset-names:
+ items:
+ - const: pipe
+ - const: sticky # Core sticky reset
+ - const: axi_s_sticky # AXI Slave Sticky reset
+ - const: axi_s # AXI slave reset
+ - const: axi_m_sticky # AXI Master Sticky reset
+ - const: axi_m # AXI master reset
+ - const: aux
+ - const: ahb
+
+required:
+ - resets
+ - reset-names
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,ipq9574-gcc.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interconnect/qcom,ipq9574.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/qcom,ipq9574-gcc.h>
+
+ pcie@10000000 {
+ compatible = "qcom,pcie-ipq9574";
+ reg = <0x10000000 0xf1d>,
+ <0x10000f20 0xa8>,
+ <0x10001000 0x1000>,
+ <0x000f8000 0x4000>,
+ <0x10100000 0x1000>,
+ <0x000fe000 0x1000>;
+ reg-names = "dbi",
+ "elbi",
+ "atu",
+ "parf",
+ "config",
+ "mhi";
+ ranges = <0x01000000 0x0 0x00000000 0x10200000 0x0 0x100000>,
+ <0x02000000 0x0 0x10300000 0x10300000 0x0 0x7d00000>;
+
+ device_type = "pci";
+ linux,pci-domain = <1>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ clocks = <&gcc GCC_PCIE1_AXI_M_CLK>,
+ <&gcc GCC_PCIE1_AXI_S_CLK>,
+ <&gcc GCC_PCIE1_AXI_S_BRIDGE_CLK>,
+ <&gcc GCC_PCIE1_RCHNG_CLK>,
+ <&gcc GCC_PCIE1_AHB_CLK>,
+ <&gcc GCC_PCIE1_AUX_CLK>;
+ clock-names = "axi_m",
+ "axi_s",
+ "axi_bridge",
+ "rchng",
+ "ahb",
+ "aux";
+
+ interconnects = <&gcc MASTER_ANOC_PCIE1 &gcc SLAVE_ANOC_PCIE1>,
+ <&gcc MASTER_SNOC_PCIE1 &gcc SLAVE_SNOC_PCIE1>;
+ interconnect-names = "pcie-mem", "cpu-pcie";
+
+ interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi0",
+ "msi1",
+ "msi2",
+ "msi3",
+ "msi4",
+ "msi5",
+ "msi6",
+ "msi7";
+
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc 0 GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 2 &intc 0 GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 3 &intc 0 GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>,
+ <0 0 0 4 &intc 0 GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
+
+ resets = <&gcc GCC_PCIE1_PIPE_ARES>,
+ <&gcc GCC_PCIE1_CORE_STICKY_ARES>,
+ <&gcc GCC_PCIE1_AXI_S_STICKY_ARES>,
+ <&gcc GCC_PCIE1_AXI_S_ARES>,
+ <&gcc GCC_PCIE1_AXI_M_STICKY_ARES>,
+ <&gcc GCC_PCIE1_AXI_M_ARES>,
+ <&gcc GCC_PCIE1_AUX_ARES>,
+ <&gcc GCC_PCIE1_AHB_ARES>;
+ reset-names = "pipe",
+ "sticky",
+ "axi_s_sticky",
+ "axi_s",
+ "axi_m_sticky",
+ "axi_m",
+ "aux",
+ "ahb";
+
+ phys = <&pcie1_phy>;
+ phy-names = "pciephy";
+
+ perst-gpios = <&tlmm 26 GPIO_ACTIVE_LOW>;
+ wake-gpios = <&tlmm 27 GPIO_ACTIVE_LOW>;
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-msm8996.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-msm8996.yaml
new file mode 100644
index 000000000000..f2081ae1593f
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-msm8996.yaml
@@ -0,0 +1,156 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-msm8996.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm MSM8996 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - qcom,pcie-msm8996
+ - items:
+ - const: qcom,pcie-msm8998
+ - const: qcom,pcie-msm8996
+
+ reg:
+ minItems: 4
+ maxItems: 5
+
+ reg-names:
+ minItems: 4
+ items:
+ - const: parf
+ - const: dbi
+ - const: elbi
+ - const: config
+ - const: mhi
+
+ clocks:
+ maxItems: 5
+
+ clock-names:
+ items:
+ - const: pipe # Pipe Clock driving internal logic
+ - const: aux
+ - const: cfg
+ - const: bus_master # Master AXI clock
+ - const: bus_slave # Slave AXI clock
+
+ interrupts:
+ minItems: 8
+ maxItems: 9
+
+ interrupt-names:
+ minItems: 8
+ items:
+ - const: msi0
+ - const: msi1
+ - const: msi2
+ - const: msi3
+ - const: msi4
+ - const: msi5
+ - const: msi6
+ - const: msi7
+ - const: global
+
+ vdda-supply:
+ description: A phandle to the core analog power supply
+
+ vddpe-3v3-supply:
+ description: A phandle to the PCIe endpoint power supply
+
+required:
+ - power-domains
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-msm8996.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ pcie@600000 {
+ compatible = "qcom,pcie-msm8996";
+ reg = <0x00600000 0x2000>,
+ <0x0c000000 0xf1d>,
+ <0x0c000f20 0xa8>,
+ <0x0c100000 0x100000>;
+ reg-names = "parf", "dbi", "elbi", "config";
+ ranges = <0x01000000 0x0 0x00000000 0x0c200000 0x0 0x100000>,
+ <0x02000000 0x0 0x0c300000 0x0c300000 0x0 0xd00000>;
+
+ device_type = "pci";
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ linux,pci-domain = <0>;
+
+ clocks = <&gcc GCC_PCIE_0_PIPE_CLK>,
+ <&gcc GCC_PCIE_0_AUX_CLK>,
+ <&gcc GCC_PCIE_0_CFG_AHB_CLK>,
+ <&gcc GCC_PCIE_0_MSTR_AXI_CLK>,
+ <&gcc GCC_PCIE_0_SLV_AXI_CLK>;
+ clock-names = "pipe",
+ "aux",
+ "cfg",
+ "bus_master",
+ "bus_slave";
+
+ interrupts = <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 411 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 412 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi0",
+ "msi1",
+ "msi2",
+ "msi3",
+ "msi4",
+ "msi5",
+ "msi6",
+ "msi7";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc GIC_SPI 244 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ pinctrl-names = "default", "sleep";
+ pinctrl-0 = <&pcie0_state_on>;
+ pinctrl-1 = <&pcie0_state_off>;
+
+ phys = <&pciephy_0>;
+ phy-names = "pciephy";
+
+ power-domains = <&gcc PCIE0_GDSC>;
+
+ perst-gpios = <&tlmm 35 GPIO_ACTIVE_LOW>;
+ vddpe-3v3-supply = <&wlan_en>;
+ vdda-supply = <&vreg_l28a_0p925>;
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-qcs404.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-qcs404.yaml
new file mode 100644
index 000000000000..99b3ed43b87c
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-qcs404.yaml
@@ -0,0 +1,131 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-qcs404.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm QCS404 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - qcom,pcie-qcs404
+
+ reg:
+ maxItems: 4
+
+ reg-names:
+ items:
+ - const: dbi
+ - const: elbi
+ - const: parf
+ - const: config
+
+ clocks:
+ maxItems: 4
+
+ clock-names:
+ items:
+ - const: iface # AHB clock
+ - const: aux
+ - const: master_bus # AXI Master clock
+ - const: slave_bus # AXI Slave clock
+
+ interrupts:
+ maxItems: 1
+
+ interrupt-names:
+ items:
+ - const: msi
+
+ resets:
+ maxItems: 6
+
+ reset-names:
+ items:
+ - const: axi_m # AXI Master reset
+ - const: axi_s # AXI Slave reset
+ - const: axi_m_sticky # AXI Master Sticky reset
+ - const: pipe_sticky
+ - const: pwr
+ - const: ahb
+
+required:
+ - resets
+ - reset-names
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-qcs404.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ pcie@10000000 {
+ compatible = "qcom,pcie-qcs404";
+ reg = <0x10000000 0xf1d>,
+ <0x10000f20 0xa8>,
+ <0x07780000 0x2000>,
+ <0x10001000 0x2000>;
+ reg-names = "dbi", "elbi", "parf", "config";
+ ranges = <0x81000000 0x0 0x00000000 0x10003000 0x0 0x00010000>, /* I/O */
+ <0x82000000 0x0 0x10013000 0x10013000 0x0 0x007ed000>; /* memory */
+
+ device_type = "pci";
+ linux,pci-domain = <0>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ clocks = <&gcc GCC_PCIE_0_CFG_AHB_CLK>,
+ <&gcc GCC_PCIE_0_AUX_CLK>,
+ <&gcc GCC_PCIE_0_MSTR_AXI_CLK>,
+ <&gcc GCC_PCIE_0_SLV_AXI_CLK>;
+ clock-names = "iface", "aux", "master_bus", "slave_bus";
+
+ interrupts = <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc GIC_SPI 224 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc GIC_SPI 267 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ phys = <&pcie_phy>;
+ phy-names = "pciephy";
+
+ perst-gpios = <&tlmm 43 GPIO_ACTIVE_LOW>;
+
+ resets = <&gcc GCC_PCIE_0_AXI_MASTER_ARES>,
+ <&gcc GCC_PCIE_0_AXI_SLAVE_ARES>,
+ <&gcc GCC_PCIE_0_AXI_MASTER_STICKY_ARES>,
+ <&gcc GCC_PCIE_0_CORE_STICKY_ARES>,
+ <&gcc GCC_PCIE_0_BCR>,
+ <&gcc GCC_PCIE_0_AHB_ARES>;
+ reset-names = "axi_m",
+ "axi_s",
+ "axi_m_sticky",
+ "pipe_sticky",
+ "pwr",
+ "ahb";
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml
deleted file mode 100644
index 6a7c410c9fc3..000000000000
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sc8180x.yaml
+++ /dev/null
@@ -1,168 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/pci/qcom,pcie-sc8180x.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Qualcomm SC8180x PCI Express Root Complex
-
-maintainers:
- - Bjorn Andersson <andersson@kernel.org>
- - Manivannan Sadhasivam <mani@kernel.org>
-
-description:
- Qualcomm SC8180x SoC PCIe root complex controller is based on the Synopsys
- DesignWare PCIe IP.
-
-properties:
- compatible:
- const: qcom,pcie-sc8180x
-
- reg:
- minItems: 5
- maxItems: 6
-
- reg-names:
- minItems: 5
- items:
- - const: parf # Qualcomm specific registers
- - const: dbi # DesignWare PCIe registers
- - const: elbi # External local bus interface registers
- - const: atu # ATU address space
- - const: config # PCIe configuration space
- - const: mhi # MHI registers
-
- clocks:
- minItems: 6
- maxItems: 6
-
- clock-names:
- items:
- - const: pipe # PIPE clock
- - const: aux # Auxiliary clock
- - const: cfg # Configuration clock
- - const: bus_master # Master AXI clock
- - const: bus_slave # Slave AXI clock
- - const: slave_q2a # Slave Q2A clock
-
- interrupts:
- minItems: 8
- maxItems: 9
-
- interrupt-names:
- minItems: 8
- items:
- - const: msi0
- - const: msi1
- - const: msi2
- - const: msi3
- - const: msi4
- - const: msi5
- - const: msi6
- - const: msi7
- - const: global
-
- resets:
- maxItems: 1
-
- reset-names:
- items:
- - const: pci
-
-allOf:
- - $ref: qcom,pcie-common.yaml#
-
-unevaluatedProperties: false
-
-examples:
- - |
- #include <dt-bindings/clock/qcom,gcc-sc8180x.h>
- #include <dt-bindings/interconnect/qcom,sc8180x.h>
- #include <dt-bindings/interrupt-controller/arm-gic.h>
-
- soc {
- #address-cells = <2>;
- #size-cells = <2>;
-
- pcie@1c00000 {
- compatible = "qcom,pcie-sc8180x";
- reg = <0 0x01c00000 0 0x3000>,
- <0 0x60000000 0 0xf1d>,
- <0 0x60000f20 0 0xa8>,
- <0 0x60001000 0 0x1000>,
- <0 0x60100000 0 0x100000>;
- reg-names = "parf",
- "dbi",
- "elbi",
- "atu",
- "config";
- ranges = <0x01000000 0x0 0x60200000 0x0 0x60200000 0x0 0x100000>,
- <0x02000000 0x0 0x60300000 0x0 0x60300000 0x0 0x3d00000>;
-
- bus-range = <0x00 0xff>;
- device_type = "pci";
- linux,pci-domain = <0>;
- num-lanes = <2>;
-
- #address-cells = <3>;
- #size-cells = <2>;
-
- assigned-clocks = <&gcc GCC_PCIE_0_AUX_CLK>;
- assigned-clock-rates = <19200000>;
-
- clocks = <&gcc GCC_PCIE_0_PIPE_CLK>,
- <&gcc GCC_PCIE_0_AUX_CLK>,
- <&gcc GCC_PCIE_0_CFG_AHB_CLK>,
- <&gcc GCC_PCIE_0_MSTR_AXI_CLK>,
- <&gcc GCC_PCIE_0_SLV_AXI_CLK>,
- <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>;
- clock-names = "pipe",
- "aux",
- "cfg",
- "bus_master",
- "bus_slave",
- "slave_q2a";
-
- dma-coherent;
-
- interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "msi0",
- "msi1",
- "msi2",
- "msi3",
- "msi4",
- "msi5",
- "msi6",
- "msi7",
- "global";
- #interrupt-cells = <1>;
- interrupt-map-mask = <0 0 0 0x7>;
- interrupt-map = <0 0 0 1 &intc 0 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
- <0 0 0 2 &intc 0 150 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
- <0 0 0 3 &intc 0 151 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
- <0 0 0 4 &intc 0 152 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
-
- interconnects = <&aggre2_noc MASTER_PCIE 0 &mc_virt SLAVE_EBI_CH0 0>,
- <&gem_noc MASTER_AMPSS_M0 0 &config_noc SLAVE_PCIE_0 0>;
- interconnect-names = "pcie-mem", "cpu-pcie";
-
- iommu-map = <0x0 &apps_smmu 0x1d80 0x1>,
- <0x100 &apps_smmu 0x1d81 0x1>;
-
- phys = <&pcie0_phy>;
- phy-names = "pciephy";
-
- power-domains = <&gcc PCIE_0_GDSC>;
-
- resets = <&gcc GCC_PCIE_0_BCR>;
- reset-names = "pci";
- };
- };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sdm845.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sdm845.yaml
new file mode 100644
index 000000000000..1ec9e4f3ff57
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sdm845.yaml
@@ -0,0 +1,190 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-sdm845.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm SDM845 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - qcom,pcie-sdm845
+
+ reg:
+ minItems: 4
+ maxItems: 5
+
+ reg-names:
+ minItems: 4
+ items:
+ - const: parf
+ - const: dbi
+ - const: elbi
+ - const: config
+ - const: mhi
+
+ clocks:
+ minItems: 7
+ maxItems: 8
+
+ clock-names:
+ minItems: 7
+ items:
+ - const: pipe
+ - const: aux
+ - const: cfg
+ - const: bus_master # Master AXI clock
+ - const: bus_slave # Slave AXI clock
+ - const: slave_q2a
+ - enum: [ ref, tbu ]
+ - const: tbu
+
+ interrupts:
+ minItems: 8
+ maxItems: 9
+
+ interrupt-names:
+ minItems: 8
+ items:
+ - const: msi0
+ - const: msi1
+ - const: msi2
+ - const: msi3
+ - const: msi4
+ - const: msi5
+ - const: msi6
+ - const: msi7
+ - const: global
+
+ resets:
+ maxItems: 1
+
+ reset-names:
+ items:
+ - const: pci
+
+required:
+ - power-domains
+ - resets
+ - reset-names
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-sdm845.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ pcie@1c00000 {
+ compatible = "qcom,pcie-sdm845";
+ reg = <0x0 0x01c00000 0x0 0x2000>,
+ <0x0 0x60000000 0x0 0xf1d>,
+ <0x0 0x60000f20 0x0 0xa8>,
+ <0x0 0x60100000 0x0 0x100000>,
+ <0x0 0x01c07000 0x0 0x1000>;
+ reg-names = "parf", "dbi", "elbi", "config", "mhi";
+ ranges = <0x01000000 0x0 0x00000000 0x0 0x60200000 0x0 0x100000>,
+ <0x02000000 0x0 0x60300000 0x0 0x60300000 0x0 0xd00000>;
+
+ device_type = "pci";
+ linux,pci-domain = <0>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ clocks = <&gcc GCC_PCIE_0_PIPE_CLK>,
+ <&gcc GCC_PCIE_0_AUX_CLK>,
+ <&gcc GCC_PCIE_0_CFG_AHB_CLK>,
+ <&gcc GCC_PCIE_0_MSTR_AXI_CLK>,
+ <&gcc GCC_PCIE_0_SLV_AXI_CLK>,
+ <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>,
+ <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>;
+ clock-names = "pipe",
+ "aux",
+ "cfg",
+ "bus_master",
+ "bus_slave",
+ "slave_q2a",
+ "tbu";
+
+ interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi0",
+ "msi1",
+ "msi2",
+ "msi3",
+ "msi4",
+ "msi5",
+ "msi6",
+ "msi7",
+ "global";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc 0 0 GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc 0 0 GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc 0 0 GIC_SPI 151 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc 0 0 GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ iommu-map = <0x0 &apps_smmu 0x1c10 0x1>,
+ <0x100 &apps_smmu 0x1c11 0x1>,
+ <0x200 &apps_smmu 0x1c12 0x1>,
+ <0x300 &apps_smmu 0x1c13 0x1>,
+ <0x400 &apps_smmu 0x1c14 0x1>,
+ <0x500 &apps_smmu 0x1c15 0x1>,
+ <0x600 &apps_smmu 0x1c16 0x1>,
+ <0x700 &apps_smmu 0x1c17 0x1>,
+ <0x800 &apps_smmu 0x1c18 0x1>,
+ <0x900 &apps_smmu 0x1c19 0x1>,
+ <0xa00 &apps_smmu 0x1c1a 0x1>,
+ <0xb00 &apps_smmu 0x1c1b 0x1>,
+ <0xc00 &apps_smmu 0x1c1c 0x1>,
+ <0xd00 &apps_smmu 0x1c1d 0x1>,
+ <0xe00 &apps_smmu 0x1c1e 0x1>,
+ <0xf00 &apps_smmu 0x1c1f 0x1>;
+
+ power-domains = <&gcc PCIE_0_GDSC>;
+
+ phys = <&pcie0_phy>;
+ phy-names = "pciephy";
+
+ resets = <&gcc GCC_PCIE_0_BCR>;
+ reset-names = "pci";
+
+ perst-gpios = <&tlmm 35 GPIO_ACTIVE_LOW>;
+ wake-gpios = <&tlmm 134 GPIO_ACTIVE_HIGH>;
+
+ vddpe-3v3-supply = <&pcie0_3p3v_dual>;
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sdx55.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sdx55.yaml
new file mode 100644
index 000000000000..7f6fd81e7ed0
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sdx55.yaml
@@ -0,0 +1,172 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,pcie-sdx55.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm SDX55 PCI Express Root Complex
+
+maintainers:
+ - Bjorn Andersson <andersson@kernel.org>
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ enum:
+ - qcom,pcie-sdx55
+
+ reg:
+ minItems: 5
+ maxItems: 6
+
+ reg-names:
+ minItems: 5
+ items:
+ - const: parf
+ - const: dbi
+ - const: elbi
+ - const: atu
+ - const: config
+ - const: mhi
+
+ clocks:
+ maxItems: 7
+
+ clock-names:
+ items:
+ - const: pipe
+ - const: aux
+ - const: cfg
+ - const: bus_master # Master AXI clock
+ - const: bus_slave # Slave AXI clock
+ - const: slave_q2a
+ - const: sleep
+
+ interrupts:
+ maxItems: 8
+
+ interrupt-names:
+ items:
+ - const: msi
+ - const: msi2
+ - const: msi3
+ - const: msi4
+ - const: msi5
+ - const: msi6
+ - const: msi7
+ - const: msi8
+
+ resets:
+ maxItems: 1
+
+ reset-names:
+ items:
+ - const: pci
+
+required:
+ - power-domains
+ - resets
+ - reset-names
+
+allOf:
+ - $ref: qcom,pcie-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-sdx55.h>
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ pcie@1c00000 {
+ compatible = "qcom,pcie-sdx55";
+ reg = <0x01c00000 0x3000>,
+ <0x40000000 0xf1d>,
+ <0x40000f20 0xc8>,
+ <0x40001000 0x1000>,
+ <0x40100000 0x100000>;
+ reg-names = "parf",
+ "dbi",
+ "elbi",
+ "atu",
+ "config";
+ ranges = <0x01000000 0x0 0x00000000 0x40200000 0x0 0x100000>,
+ <0x02000000 0x0 0x40300000 0x40300000 0x0 0x3fd00000>;
+
+ device_type = "pci";
+ linux,pci-domain = <0>;
+ bus-range = <0x00 0xff>;
+ num-lanes = <1>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+
+ interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi",
+ "msi2",
+ "msi3",
+ "msi4",
+ "msi5",
+ "msi6",
+ "msi7",
+ "msi8";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0x7>;
+ interrupt-map = <0 0 0 1 &intc GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>, /* int_a */
+ <0 0 0 2 &intc GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
+ <0 0 0 3 &intc GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
+ <0 0 0 4 &intc GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
+
+ clocks = <&gcc GCC_PCIE_PIPE_CLK>,
+ <&gcc GCC_PCIE_AUX_CLK>,
+ <&gcc GCC_PCIE_CFG_AHB_CLK>,
+ <&gcc GCC_PCIE_MSTR_AXI_CLK>,
+ <&gcc GCC_PCIE_SLV_AXI_CLK>,
+ <&gcc GCC_PCIE_SLV_Q2A_AXI_CLK>,
+ <&gcc GCC_PCIE_SLEEP_CLK>;
+ clock-names = "pipe",
+ "aux",
+ "cfg",
+ "bus_master",
+ "bus_slave",
+ "slave_q2a",
+ "sleep";
+
+ assigned-clocks = <&gcc GCC_PCIE_AUX_CLK>;
+ assigned-clock-rates = <19200000>;
+
+ iommu-map = <0x0 &apps_smmu 0x0200 0x1>,
+ <0x100 &apps_smmu 0x0201 0x1>,
+ <0x200 &apps_smmu 0x0202 0x1>,
+ <0x300 &apps_smmu 0x0203 0x1>,
+ <0x400 &apps_smmu 0x0204 0x1>;
+
+ power-domains = <&gcc PCIE_GDSC>;
+
+ phys = <&pcie_phy>;
+ phy-names = "pciephy";
+
+ resets = <&gcc GCC_PCIE_BCR>;
+ reset-names = "pci";
+
+ perst-gpios = <&tlmm 57 GPIO_ACTIVE_LOW>;
+ wake-gpios = <&tlmm 53 GPIO_ACTIVE_HIGH>;
+
+ pcie@0 {
+ device_type = "pci";
+ reg = <0x0 0x0 0x0 0x0 0x0>;
+ bus-range = <0x01 0xff>;
+
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml
index 6a5421e4f19d..ea29d0900a25 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-sm8150.yaml
@@ -17,6 +17,7 @@ description:
properties:
compatible:
oneOf:
+ - const: qcom,pcie-sc8180x
- const: qcom,pcie-sm8150
- items:
- enum:
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml
index 62c674ca0cf7..3d3b9f309a73 100644
--- a/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml
+++ b/Documentation/devicetree/bindings/pci/qcom,pcie-x1e80100.yaml
@@ -16,7 +16,12 @@ description:
properties:
compatible:
- const: qcom,pcie-x1e80100
+ oneOf:
+ - const: qcom,pcie-x1e80100
+ - items:
+ - enum:
+ - qcom,glymur-pcie
+ - const: qcom,pcie-x1e80100
reg:
minItems: 6
diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml b/Documentation/devicetree/bindings/pci/qcom,pcie.yaml
deleted file mode 100644
index c61930441be0..000000000000
--- a/Documentation/devicetree/bindings/pci/qcom,pcie.yaml
+++ /dev/null
@@ -1,782 +0,0 @@
-# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-%YAML 1.2
----
-$id: http://devicetree.org/schemas/pci/qcom,pcie.yaml#
-$schema: http://devicetree.org/meta-schemas/core.yaml#
-
-title: Qualcomm PCI express root complex
-
-maintainers:
- - Bjorn Andersson <bjorn.andersson@linaro.org>
- - Manivannan Sadhasivam <mani@kernel.org>
-
-description: |
- Qualcomm PCIe root complex controller is based on the Synopsys DesignWare
- PCIe IP.
-
-properties:
- compatible:
- oneOf:
- - enum:
- - qcom,pcie-apq8064
- - qcom,pcie-apq8084
- - qcom,pcie-ipq4019
- - qcom,pcie-ipq5018
- - qcom,pcie-ipq6018
- - qcom,pcie-ipq8064
- - qcom,pcie-ipq8064-v2
- - qcom,pcie-ipq8074
- - qcom,pcie-ipq8074-gen3
- - qcom,pcie-ipq9574
- - qcom,pcie-msm8996
- - qcom,pcie-qcs404
- - qcom,pcie-sdm845
- - qcom,pcie-sdx55
- - items:
- - enum:
- - qcom,pcie-ipq5332
- - qcom,pcie-ipq5424
- - const: qcom,pcie-ipq9574
- - items:
- - const: qcom,pcie-msm8998
- - const: qcom,pcie-msm8996
-
- reg:
- minItems: 4
- maxItems: 6
-
- reg-names:
- minItems: 4
- maxItems: 6
-
- interrupts:
- minItems: 1
- maxItems: 9
-
- interrupt-names:
- minItems: 1
- maxItems: 9
-
- iommu-map:
- minItems: 1
- maxItems: 16
-
- # Common definitions for clocks, clock-names and reset.
- # Platform constraints are described later.
- clocks:
- minItems: 3
- maxItems: 13
-
- clock-names:
- minItems: 3
- maxItems: 13
-
- dma-coherent: true
-
- interconnects:
- maxItems: 2
-
- interconnect-names:
- items:
- - const: pcie-mem
- - const: cpu-pcie
-
- resets:
- minItems: 1
- maxItems: 12
-
- reset-names:
- minItems: 1
- maxItems: 12
-
- vdda-supply:
- description: A phandle to the core analog power supply
-
- vdda_phy-supply:
- description: A phandle to the core analog power supply for PHY
-
- vdda_refclk-supply:
- description: A phandle to the core analog power supply for IC which generates reference clock
-
- vddpe-3v3-supply:
- description: A phandle to the PCIe endpoint power supply
-
- phys:
- maxItems: 1
-
- phy-names:
- items:
- - const: pciephy
-
- power-domains:
- maxItems: 1
-
- perst-gpios:
- description: GPIO controlled connection to PERST# signal
- maxItems: 1
-
- required-opps:
- maxItems: 1
-
- wake-gpios:
- description: GPIO controlled connection to WAKE# signal
- maxItems: 1
-
-required:
- - compatible
- - reg
- - reg-names
- - interrupt-map-mask
- - interrupt-map
- - clocks
- - clock-names
-
-anyOf:
- - required:
- - interrupts
- - interrupt-names
- - "#interrupt-cells"
- - required:
- - msi-map
-
-allOf:
- - $ref: /schemas/pci/pci-host-bridge.yaml#
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-apq8064
- - qcom,pcie-ipq4019
- - qcom,pcie-ipq8064
- - qcom,pcie-ipq8064v2
- - qcom,pcie-ipq8074
- - qcom,pcie-qcs404
- then:
- properties:
- reg:
- minItems: 4
- maxItems: 4
- reg-names:
- items:
- - const: dbi # DesignWare PCIe registers
- - const: elbi # External local bus interface registers
- - const: parf # Qualcomm specific registers
- - const: config # PCIe configuration space
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-ipq5018
- - qcom,pcie-ipq6018
- - qcom,pcie-ipq8074-gen3
- - qcom,pcie-ipq9574
- then:
- properties:
- reg:
- minItems: 5
- maxItems: 6
- reg-names:
- minItems: 5
- items:
- - const: dbi # DesignWare PCIe registers
- - const: elbi # External local bus interface registers
- - const: atu # ATU address space
- - const: parf # Qualcomm specific registers
- - const: config # PCIe configuration space
- - const: mhi # MHI registers
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-apq8084
- - qcom,pcie-msm8996
- - qcom,pcie-sdm845
- then:
- properties:
- reg:
- minItems: 4
- maxItems: 5
- reg-names:
- minItems: 4
- items:
- - const: parf # Qualcomm specific registers
- - const: dbi # DesignWare PCIe registers
- - const: elbi # External local bus interface registers
- - const: config # PCIe configuration space
- - const: mhi # MHI registers
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-sdx55
- then:
- properties:
- reg:
- minItems: 5
- maxItems: 6
- reg-names:
- minItems: 5
- items:
- - const: parf # Qualcomm specific registers
- - const: dbi # DesignWare PCIe registers
- - const: elbi # External local bus interface registers
- - const: atu # ATU address space
- - const: config # PCIe configuration space
- - const: mhi # MHI registers
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-apq8064
- - qcom,pcie-ipq8064
- - qcom,pcie-ipq8064v2
- then:
- properties:
- clocks:
- minItems: 3
- maxItems: 5
- clock-names:
- minItems: 3
- items:
- - const: core # Clocks the pcie hw block
- - const: iface # Configuration AHB clock
- - const: phy # Clocks the pcie PHY block
- - const: aux # Clocks the pcie AUX block, not on apq8064
- - const: ref # Clocks the pcie ref block, not on apq8064
- resets:
- minItems: 5
- maxItems: 6
- reset-names:
- minItems: 5
- items:
- - const: axi # AXI reset
- - const: ahb # AHB reset
- - const: por # POR reset
- - const: pci # PCI reset
- - const: phy # PHY reset
- - const: ext # EXT reset, not on apq8064
- required:
- - vdda-supply
- - vdda_phy-supply
- - vdda_refclk-supply
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-apq8084
- then:
- properties:
- clocks:
- minItems: 4
- maxItems: 4
- clock-names:
- items:
- - const: iface # Configuration AHB clock
- - const: master_bus # Master AXI clock
- - const: slave_bus # Slave AXI clock
- - const: aux # Auxiliary (AUX) clock
- resets:
- maxItems: 1
- reset-names:
- items:
- - const: core # Core reset
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-ipq4019
- then:
- properties:
- clocks:
- minItems: 3
- maxItems: 3
- clock-names:
- items:
- - const: aux # Auxiliary (AUX) clock
- - const: master_bus # Master AXI clock
- - const: slave_bus # Slave AXI clock
- resets:
- minItems: 12
- maxItems: 12
- reset-names:
- items:
- - const: axi_m # AXI master reset
- - const: axi_s # AXI slave reset
- - const: pipe # PIPE reset
- - const: axi_m_vmid # VMID reset
- - const: axi_s_xpu # XPU reset
- - const: parf # PARF reset
- - const: phy # PHY reset
- - const: axi_m_sticky # AXI sticky reset
- - const: pipe_sticky # PIPE sticky reset
- - const: pwr # PWR reset
- - const: ahb # AHB reset
- - const: phy_ahb # PHY AHB reset
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-ipq5018
- then:
- properties:
- clocks:
- minItems: 6
- maxItems: 6
- clock-names:
- items:
- - const: iface # PCIe to SysNOC BIU clock
- - const: axi_m # AXI Master clock
- - const: axi_s # AXI Slave clock
- - const: ahb # AHB clock
- - const: aux # Auxiliary clock
- - const: axi_bridge # AXI bridge clock
- resets:
- minItems: 8
- maxItems: 8
- reset-names:
- items:
- - const: pipe # PIPE reset
- - const: sleep # Sleep reset
- - const: sticky # Core sticky reset
- - const: axi_m # AXI master reset
- - const: axi_s # AXI slave reset
- - const: ahb # AHB reset
- - const: axi_m_sticky # AXI master sticky reset
- - const: axi_s_sticky # AXI slave sticky reset
- interrupts:
- minItems: 9
- maxItems: 9
- interrupt-names:
- items:
- - const: msi0
- - const: msi1
- - const: msi2
- - const: msi3
- - const: msi4
- - const: msi5
- - const: msi6
- - const: msi7
- - const: global
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-msm8996
- then:
- properties:
- clocks:
- minItems: 5
- maxItems: 5
- clock-names:
- items:
- - const: pipe # Pipe Clock driving internal logic
- - const: aux # Auxiliary (AUX) clock
- - const: cfg # Configuration clock
- - const: bus_master # Master AXI clock
- - const: bus_slave # Slave AXI clock
- resets: false
- reset-names: false
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-ipq8074
- then:
- properties:
- clocks:
- minItems: 5
- maxItems: 5
- clock-names:
- items:
- - const: iface # PCIe to SysNOC BIU clock
- - const: axi_m # AXI Master clock
- - const: axi_s # AXI Slave clock
- - const: ahb # AHB clock
- - const: aux # Auxiliary clock
- resets:
- minItems: 7
- maxItems: 7
- reset-names:
- items:
- - const: pipe # PIPE reset
- - const: sleep # Sleep reset
- - const: sticky # Core Sticky reset
- - const: axi_m # AXI Master reset
- - const: axi_s # AXI Slave reset
- - const: ahb # AHB Reset
- - const: axi_m_sticky # AXI Master Sticky reset
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-ipq6018
- - qcom,pcie-ipq8074-gen3
- then:
- properties:
- clocks:
- minItems: 5
- maxItems: 5
- clock-names:
- items:
- - const: iface # PCIe to SysNOC BIU clock
- - const: axi_m # AXI Master clock
- - const: axi_s # AXI Slave clock
- - const: axi_bridge # AXI bridge clock
- - const: rchng
- resets:
- minItems: 8
- maxItems: 8
- reset-names:
- items:
- - const: pipe # PIPE reset
- - const: sleep # Sleep reset
- - const: sticky # Core Sticky reset
- - const: axi_m # AXI Master reset
- - const: axi_s # AXI Slave reset
- - const: ahb # AHB Reset
- - const: axi_m_sticky # AXI Master Sticky reset
- - const: axi_s_sticky # AXI Slave Sticky reset
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-ipq9574
- then:
- properties:
- clocks:
- minItems: 6
- maxItems: 6
- clock-names:
- items:
- - const: axi_m # AXI Master clock
- - const: axi_s # AXI Slave clock
- - const: axi_bridge
- - const: rchng
- - const: ahb
- - const: aux
-
- resets:
- minItems: 8
- maxItems: 8
- reset-names:
- items:
- - const: pipe # PIPE reset
- - const: sticky # Core Sticky reset
- - const: axi_s_sticky # AXI Slave Sticky reset
- - const: axi_s # AXI Slave reset
- - const: axi_m_sticky # AXI Master Sticky reset
- - const: axi_m # AXI Master reset
- - const: aux # AUX Reset
- - const: ahb # AHB Reset
-
- interrupts:
- minItems: 8
- interrupt-names:
- minItems: 8
- items:
- - const: msi0
- - const: msi1
- - const: msi2
- - const: msi3
- - const: msi4
- - const: msi5
- - const: msi6
- - const: msi7
- - const: global
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-qcs404
- then:
- properties:
- clocks:
- minItems: 4
- maxItems: 4
- clock-names:
- items:
- - const: iface # AHB clock
- - const: aux # Auxiliary clock
- - const: master_bus # AXI Master clock
- - const: slave_bus # AXI Slave clock
- resets:
- minItems: 6
- maxItems: 6
- reset-names:
- items:
- - const: axi_m # AXI Master reset
- - const: axi_s # AXI Slave reset
- - const: axi_m_sticky # AXI Master Sticky reset
- - const: pipe_sticky # PIPE sticky reset
- - const: pwr # PWR reset
- - const: ahb # AHB reset
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-sdm845
- then:
- oneOf:
- # Unfortunately the "optional" ref clock is used in the middle of the list
- - properties:
- clocks:
- minItems: 8
- maxItems: 8
- clock-names:
- items:
- - const: pipe # PIPE clock
- - const: aux # Auxiliary clock
- - const: cfg # Configuration clock
- - const: bus_master # Master AXI clock
- - const: bus_slave # Slave AXI clock
- - const: slave_q2a # Slave Q2A clock
- - const: ref # REFERENCE clock
- - const: tbu # PCIe TBU clock
- - properties:
- clocks:
- minItems: 7
- maxItems: 7
- clock-names:
- items:
- - const: pipe # PIPE clock
- - const: aux # Auxiliary clock
- - const: cfg # Configuration clock
- - const: bus_master # Master AXI clock
- - const: bus_slave # Slave AXI clock
- - const: slave_q2a # Slave Q2A clock
- - const: tbu # PCIe TBU clock
- properties:
- resets:
- maxItems: 1
- reset-names:
- items:
- - const: pci # PCIe core reset
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-sdx55
- then:
- properties:
- clocks:
- minItems: 7
- maxItems: 7
- clock-names:
- items:
- - const: pipe # PIPE clock
- - const: aux # Auxiliary clock
- - const: cfg # Configuration clock
- - const: bus_master # Master AXI clock
- - const: bus_slave # Slave AXI clock
- - const: slave_q2a # Slave Q2A clock
- - const: sleep # PCIe Sleep clock
- resets:
- maxItems: 1
- reset-names:
- items:
- - const: pci # PCIe core reset
-
- - if:
- not:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-apq8064
- - qcom,pcie-ipq4019
- - qcom,pcie-ipq5018
- - qcom,pcie-ipq8064
- - qcom,pcie-ipq8064v2
- - qcom,pcie-ipq8074
- - qcom,pcie-ipq8074-gen3
- - qcom,pcie-ipq9574
- - qcom,pcie-qcs404
- then:
- required:
- - power-domains
-
- - if:
- not:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-msm8996
- then:
- required:
- - resets
- - reset-names
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-ipq6018
- - qcom,pcie-ipq8074
- - qcom,pcie-ipq8074-gen3
- - qcom,pcie-msm8996
- - qcom,pcie-msm8998
- - qcom,pcie-sdm845
- then:
- oneOf:
- - properties:
- interrupts:
- maxItems: 1
- interrupt-names:
- items:
- - const: msi
- - properties:
- interrupts:
- minItems: 8
- maxItems: 9
- interrupt-names:
- minItems: 8
- items:
- - const: msi0
- - const: msi1
- - const: msi2
- - const: msi3
- - const: msi4
- - const: msi5
- - const: msi6
- - const: msi7
- - const: global
-
- - if:
- properties:
- compatible:
- contains:
- enum:
- - qcom,pcie-apq8064
- - qcom,pcie-apq8084
- - qcom,pcie-ipq4019
- - qcom,pcie-ipq8064
- - qcom,pcie-ipq8064-v2
- - qcom,pcie-qcs404
- then:
- properties:
- interrupts:
- maxItems: 1
- interrupt-names:
- items:
- - const: msi
-
-unevaluatedProperties: false
-
-examples:
- - |
- #include <dt-bindings/interrupt-controller/arm-gic.h>
- pcie@1b500000 {
- compatible = "qcom,pcie-ipq8064";
- reg = <0x1b500000 0x1000>,
- <0x1b502000 0x80>,
- <0x1b600000 0x100>,
- <0x0ff00000 0x100000>;
- reg-names = "dbi", "elbi", "parf", "config";
- device_type = "pci";
- linux,pci-domain = <0>;
- bus-range = <0x00 0xff>;
- num-lanes = <1>;
- #address-cells = <3>;
- #size-cells = <2>;
- ranges = <0x81000000 0 0 0x0fe00000 0 0x00100000>,
- <0x82000000 0 0 0x08000000 0 0x07e00000>;
- interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "msi";
- #interrupt-cells = <1>;
- interrupt-map-mask = <0 0 0 0x7>;
- interrupt-map = <0 0 0 1 &intc 0 36 IRQ_TYPE_LEVEL_HIGH>,
- <0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>,
- <0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>,
- <0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&gcc 41>,
- <&gcc 43>,
- <&gcc 44>,
- <&gcc 42>,
- <&gcc 248>;
- clock-names = "core", "iface", "phy", "aux", "ref";
- resets = <&gcc 27>,
- <&gcc 26>,
- <&gcc 25>,
- <&gcc 24>,
- <&gcc 23>,
- <&gcc 22>;
- reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
- pinctrl-0 = <&pcie_pins_default>;
- pinctrl-names = "default";
- vdda-supply = <&pm8921_s3>;
- vdda_phy-supply = <&pm8921_lvs6>;
- vdda_refclk-supply = <&ext_3p3v>;
- };
- - |
- #include <dt-bindings/interrupt-controller/arm-gic.h>
- #include <dt-bindings/gpio/gpio.h>
- pcie@fc520000 {
- compatible = "qcom,pcie-apq8084";
- reg = <0xfc520000 0x2000>,
- <0xff000000 0x1000>,
- <0xff001000 0x1000>,
- <0xff002000 0x2000>;
- reg-names = "parf", "dbi", "elbi", "config";
- device_type = "pci";
- linux,pci-domain = <0>;
- bus-range = <0x00 0xff>;
- num-lanes = <1>;
- #address-cells = <3>;
- #size-cells = <2>;
- ranges = <0x81000000 0 0 0xff200000 0 0x00100000>,
- <0x82000000 0 0x00300000 0xff300000 0 0x00d00000>;
- interrupts = <GIC_SPI 243 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "msi";
- #interrupt-cells = <1>;
- interrupt-map-mask = <0 0 0 0x7>;
- interrupt-map = <0 0 0 1 &intc 0 244 IRQ_TYPE_LEVEL_HIGH>,
- <0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>,
- <0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>,
- <0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&gcc 324>,
- <&gcc 325>,
- <&gcc 327>,
- <&gcc 323>;
- clock-names = "iface", "master_bus", "slave_bus", "aux";
- resets = <&gcc 81>;
- reset-names = "core";
- power-domains = <&gcc 1>;
- vdda-supply = <&pma8084_l3>;
- phys = <&pciephy0>;
- phy-names = "pciephy";
- perst-gpios = <&tlmm 70 GPIO_ACTIVE_LOW>;
- pinctrl-0 = <&pcie0_pins_default>;
- pinctrl-names = "default";
- };
-...
diff --git a/Documentation/devicetree/bindings/pci/qcom,sa8255p-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/qcom,sa8255p-pcie-ep.yaml
new file mode 100644
index 000000000000..e338797d5dc2
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/qcom,sa8255p-pcie-ep.yaml
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pci/qcom,sa8255p-pcie-ep.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm firmware managed PCIe Endpoint Controller
+
+description:
+ Qualcomm SA8255p SoC PCIe endpoint controller is based on the Synopsys
+ DesignWare PCIe IP which is managed by firmware.
+
+maintainers:
+ - Manivannan Sadhasivam <mani@kernel.org>
+
+properties:
+ compatible:
+ const: qcom,sa8255p-pcie-ep
+
+ reg:
+ items:
+ - description: Qualcomm-specific PARF configuration registers
+ - description: DesignWare PCIe registers
+ - description: External local bus interface registers
+ - description: Address Translation Unit (ATU) registers
+ - description: Memory region used to map remote RC address space
+ - description: BAR memory region
+ - description: DMA register space
+
+ reg-names:
+ items:
+ - const: parf
+ - const: dbi
+ - const: elbi
+ - const: atu
+ - const: addr_space
+ - const: mmio
+ - const: dma
+
+ interrupts:
+ items:
+ - description: PCIe Global interrupt
+ - description: PCIe Doorbell interrupt
+ - description: DMA interrupt
+
+ interrupt-names:
+ items:
+ - const: global
+ - const: doorbell
+ - const: dma
+
+ iommus:
+ maxItems: 1
+
+ reset-gpios:
+ description: GPIO used as PERST# input signal
+ maxItems: 1
+
+ wake-gpios:
+ description: GPIO used as WAKE# output signal
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+ dma-coherent: true
+
+ num-lanes:
+ default: 2
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - interrupts
+ - interrupt-names
+ - reset-gpios
+ - power-domains
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ pcie1_ep: pcie-ep@1c10000 {
+ compatible = "qcom,sa8255p-pcie-ep";
+ reg = <0x0 0x01c10000 0x0 0x3000>,
+ <0x0 0x60000000 0x0 0xf20>,
+ <0x0 0x60000f20 0x0 0xa8>,
+ <0x0 0x60001000 0x0 0x4000>,
+ <0x0 0x60200000 0x0 0x100000>,
+ <0x0 0x01c13000 0x0 0x1000>,
+ <0x0 0x60005000 0x0 0x2000>;
+ reg-names = "parf", "dbi", "elbi", "atu", "addr_space", "mmio", "dma";
+ interrupts = <GIC_SPI 518 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 474 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "global", "doorbell", "dma";
+ reset-gpios = <&tlmm 4 GPIO_ACTIVE_LOW>;
+ wake-gpios = <&tlmm 5 GPIO_ACTIVE_LOW>;
+ dma-coherent;
+ iommus = <&pcie_smmu 0x80 0x7f>;
+ power-domains = <&scmi6_pd 1>;
+ num-lanes = <4>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml
index 6339a76499b2..2c4dc04f9984 100644
--- a/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml
+++ b/Documentation/devicetree/bindings/pci/snps,dw-pcie-common.yaml
@@ -106,6 +106,12 @@ properties:
be connected to a single source of the periodic signal).
const: ref
- description:
+ Some dwc wrappers (like i.MX95 PCIes) have two reference clock
+ inputs, one from an internal PLL, the other from an off-chip crystal
+ oscillator. If present, 'extref' refers to a reference clock from
+ an external oscillator.
+ const: extref
+ - description:
Clock for the PHY registers interface. Originally this is
a PHY-viewport-based interface, but some platform may have
specifically designed one.
diff --git a/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml
index c07b0ed51613..8a2f1eef51bd 100644
--- a/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml
+++ b/Documentation/devicetree/bindings/pci/socionext,uniphier-pcie.yaml
@@ -51,7 +51,7 @@ properties:
phy-names:
const: pcie-phy
- interrupt-controller:
+ legacy-interrupt-controller:
type: object
additionalProperties: false
@@ -111,7 +111,7 @@ examples:
<0 0 0 3 &pcie_intc 2>,
<0 0 0 4 &pcie_intc 3>;
- pcie_intc: interrupt-controller {
+ pcie_intc: legacy-interrupt-controller {
#address-cells = <0>;
interrupt-controller;
#interrupt-cells = <1>;
diff --git a/Documentation/trace/events-pci.rst b/Documentation/trace/events-pci.rst
new file mode 100644
index 000000000000..03ff4ad30ddf
--- /dev/null
+++ b/Documentation/trace/events-pci.rst
@@ -0,0 +1,74 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================
+Subsystem Trace Points: PCI
+===========================
+
+Overview
+========
+The PCI tracing system provides tracepoints to monitor critical hardware events
+that can impact system performance and reliability. These events normally show
+up here:
+
+ /sys/kernel/tracing/events/pci
+
+Cf. include/trace/events/pci.h for the events definitions.
+
+Available Tracepoints
+=====================
+
+pci_hp_event
+------------
+
+Monitors PCI hotplug events including card insertion/removal and link
+state changes.
+::
+
+ pci_hp_event "%s slot:%s, event:%s\n"
+
+**Event Types**:
+
+* ``LINK_UP`` - PCIe link established
+* ``LINK_DOWN`` - PCIe link lost
+* ``CARD_PRESENT`` - Card detected in slot
+* ``CARD_NOT_PRESENT`` - Card removed from slot
+
+**Example Usage**::
+
+ # Enable the tracepoint
+ echo 1 > /sys/kernel/debug/tracing/events/pci/pci_hp_event/enable
+
+ # Monitor events (the following output is generated when a device is hotplugged)
+ cat /sys/kernel/debug/tracing/trace_pipe
+ irq/51-pciehp-88 [001] ..... 1311.177459: pci_hp_event: 0000:00:02.0 slot:10, event:CARD_PRESENT
+
+ irq/51-pciehp-88 [001] ..... 1311.177566: pci_hp_event: 0000:00:02.0 slot:10, event:LINK_UP
+
+pcie_link_event
+---------------
+
+Monitors PCIe link speed changes and provides detailed link status information.
+::
+
+ pcie_link_event "%s type:%d, reason:%d, cur_bus_speed:%d, max_bus_speed:%d, width:%u, flit_mode:%u, status:%s\n"
+
+**Parameters**:
+
+* ``type`` - PCIe device type (4=Root Port, etc.)
+* ``reason`` - Reason for link change:
+
+ - ``0`` - Link retrain
+ - ``1`` - Bus enumeration
+ - ``2`` - Bandwidth notification enable
+ - ``3`` - Bandwidth notification IRQ
+ - ``4`` - Hotplug event
+
+
+**Example Usage**::
+
+ # Enable the tracepoint
+ echo 1 > /sys/kernel/debug/tracing/events/pci/pcie_link_event/enable
+
+ # Monitor events (the following output is generated when a device is hotplugged)
+ cat /sys/kernel/debug/tracing/trace_pipe
+ irq/51-pciehp-88 [001] ..... 381.545386: pcie_link_event: 0000:00:02.0 type:4, reason:4, cur_bus_speed:20, max_bus_speed:23, width:1, flit_mode:0, status:DLLLA
diff --git a/Documentation/trace/index.rst b/Documentation/trace/index.rst
index cfd8128ac56d..338bc4d7cfab 100644
--- a/Documentation/trace/index.rst
+++ b/Documentation/trace/index.rst
@@ -54,6 +54,7 @@ applications.
events-power
events-nmi
events-msr
+ events-pci
boottime-trace
histogram
histogram-design
diff --git a/MAINTAINERS b/MAINTAINERS
index 2a13e8e6dbcd..d4b1d09d12ef 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3922,6 +3922,14 @@ S: Maintained
F: Documentation/devicetree/bindings/media/aspeed,video-engine.yaml
F: drivers/media/platform/aspeed/
+ASPEED PCIE CONTROLLER DRIVER
+M: Jacky Chou <jacky_chou@aspeedtech.com>
+L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers)
+L: linux-pci@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/pci/aspeed,ast2600-pcie.yaml
+F: drivers/pci/controller/pcie-aspeed.c
+
ASUS EC HARDWARE MONITOR DRIVER
M: Eugene Shalygin <eugene.shalygin@gmail.com>
L: linux-hwmon@vger.kernel.org
@@ -20503,6 +20511,7 @@ L: linux-pci@vger.kernel.org
L: linux-arm-msm@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/pci/qcom,pcie-ep.yaml
+F: Documentation/devicetree/bindings/pci/qcom,sa8255p-pcie-ep.yaml
F: drivers/pci/controller/dwc/pcie-qcom-common.c
F: drivers/pci/controller/dwc/pcie-qcom-ep.c
diff --git a/drivers/cpuidle/cpuidle-tegra.c b/drivers/cpuidle/cpuidle-tegra.c
index b203a93deac5..aca907a62bb5 100644
--- a/drivers/cpuidle/cpuidle-tegra.c
+++ b/drivers/cpuidle/cpuidle-tegra.c
@@ -336,6 +336,7 @@ void tegra_cpuidle_pcie_irqs_in_use(void)
pr_info("disabling CC6 state, since PCIe IRQs are in use\n");
tegra_cpuidle_disable_state(TEGRA_CC6);
}
+EXPORT_SYMBOL_GPL(tegra_cpuidle_pcie_irqs_in_use);
static void tegra_cpuidle_setup_tegra114_c7_state(void)
{
diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 1c0fd185114f..74ab5b5b9011 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -39,6 +39,8 @@
#define COMMAND_COPY BIT(5)
#define COMMAND_ENABLE_DOORBELL BIT(6)
#define COMMAND_DISABLE_DOORBELL BIT(7)
+#define COMMAND_BAR_SUBRANGE_SETUP BIT(8)
+#define COMMAND_BAR_SUBRANGE_CLEAR BIT(9)
#define PCI_ENDPOINT_TEST_STATUS 0x8
#define STATUS_READ_SUCCESS BIT(0)
@@ -55,6 +57,10 @@
#define STATUS_DOORBELL_ENABLE_FAIL BIT(11)
#define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12)
#define STATUS_DOORBELL_DISABLE_FAIL BIT(13)
+#define STATUS_BAR_SUBRANGE_SETUP_SUCCESS BIT(14)
+#define STATUS_BAR_SUBRANGE_SETUP_FAIL BIT(15)
+#define STATUS_BAR_SUBRANGE_CLEAR_SUCCESS BIT(16)
+#define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17)
#define PCI_ENDPOINT_TEST_LOWER_SRC_ADDR 0x0c
#define PCI_ENDPOINT_TEST_UPPER_SRC_ADDR 0x10
@@ -77,6 +83,7 @@
#define CAP_MSI BIT(1)
#define CAP_MSIX BIT(2)
#define CAP_INTX BIT(3)
+#define CAP_SUBRANGE_MAPPING BIT(4)
#define PCI_ENDPOINT_TEST_DB_BAR 0x34
#define PCI_ENDPOINT_TEST_DB_OFFSET 0x38
@@ -100,6 +107,8 @@
#define PCI_DEVICE_ID_ROCKCHIP_RK3588 0x3588
+#define PCI_ENDPOINT_TEST_BAR_SUBRANGE_NSUB 2
+
static DEFINE_IDA(pci_endpoint_test_ida);
#define to_endpoint_test(priv) container_of((priv), struct pci_endpoint_test, \
@@ -414,6 +423,193 @@ static int pci_endpoint_test_bars(struct pci_endpoint_test *test)
return 0;
}
+static u8 pci_endpoint_test_subrange_sig_byte(enum pci_barno barno,
+ unsigned int subno)
+{
+ return 0x50 + (barno * 8) + subno;
+}
+
+static u8 pci_endpoint_test_subrange_test_byte(enum pci_barno barno,
+ unsigned int subno)
+{
+ return 0xa0 + (barno * 8) + subno;
+}
+
+static int pci_endpoint_test_bar_subrange_cmd(struct pci_endpoint_test *test,
+ enum pci_barno barno, u32 command,
+ u32 ok_bit, u32 fail_bit)
+{
+ struct pci_dev *pdev = test->pdev;
+ struct device *dev = &pdev->dev;
+ int irq_type = test->irq_type;
+ u32 status;
+
+ if (irq_type < PCITEST_IRQ_TYPE_INTX ||
+ irq_type > PCITEST_IRQ_TYPE_MSIX) {
+ dev_err(dev, "Invalid IRQ type\n");
+ return -EINVAL;
+ }
+
+ reinit_completion(&test->irq_raised);
+
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_STATUS, 0);
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, irq_type);
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_NUMBER, 1);
+ /* Reuse SIZE as a command parameter: bar number. */
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_SIZE, barno);
+ pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, command);
+
+ if (!wait_for_completion_timeout(&test->irq_raised,
+ msecs_to_jiffies(1000)))
+ return -ETIMEDOUT;
+
+ status = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS);
+ if (status & fail_bit)
+ return -EIO;
+
+ if (!(status & ok_bit))
+ return -EIO;
+
+ return 0;
+}
+
+static int pci_endpoint_test_bar_subrange_setup(struct pci_endpoint_test *test,
+ enum pci_barno barno)
+{
+ return pci_endpoint_test_bar_subrange_cmd(test, barno,
+ COMMAND_BAR_SUBRANGE_SETUP,
+ STATUS_BAR_SUBRANGE_SETUP_SUCCESS,
+ STATUS_BAR_SUBRANGE_SETUP_FAIL);
+}
+
+static int pci_endpoint_test_bar_subrange_clear(struct pci_endpoint_test *test,
+ enum pci_barno barno)
+{
+ return pci_endpoint_test_bar_subrange_cmd(test, barno,
+ COMMAND_BAR_SUBRANGE_CLEAR,
+ STATUS_BAR_SUBRANGE_CLEAR_SUCCESS,
+ STATUS_BAR_SUBRANGE_CLEAR_FAIL);
+}
+
+static int pci_endpoint_test_bar_subrange(struct pci_endpoint_test *test,
+ enum pci_barno barno)
+{
+ u32 nsub = PCI_ENDPOINT_TEST_BAR_SUBRANGE_NSUB;
+ struct device *dev = &test->pdev->dev;
+ size_t sub_size, buf_size;
+ resource_size_t bar_size;
+ void __iomem *bar_addr;
+ void *read_buf = NULL;
+ int ret, clear_ret;
+ size_t off, chunk;
+ u32 i, exp, val;
+ u8 pattern;
+
+ if (!(test->ep_caps & CAP_SUBRANGE_MAPPING))
+ return -EOPNOTSUPP;
+
+ /*
+ * The test register BAR is not safe to reprogram and write/read
+ * over its full size. BAR_TEST already special-cases it to a tiny
+ * range. For subrange mapping tests, let's simply skip it.
+ */
+ if (barno == test->test_reg_bar)
+ return -EBUSY;
+
+ bar_size = pci_resource_len(test->pdev, barno);
+ if (!bar_size)
+ return -ENODATA;
+
+ bar_addr = test->bar[barno];
+ if (!bar_addr)
+ return -ENOMEM;
+
+ ret = pci_endpoint_test_bar_subrange_setup(test, barno);
+ if (ret)
+ return ret;
+
+ if (bar_size % nsub || bar_size / nsub > SIZE_MAX) {
+ ret = -EINVAL;
+ goto out_clear;
+ }
+
+ sub_size = bar_size / nsub;
+ if (sub_size < sizeof(u32)) {
+ ret = -ENOSPC;
+ goto out_clear;
+ }
+
+ /* Limit the temporary buffer size */
+ buf_size = min_t(size_t, sub_size, SZ_1M);
+
+ read_buf = kmalloc(buf_size, GFP_KERNEL);
+ if (!read_buf) {
+ ret = -ENOMEM;
+ goto out_clear;
+ }
+
+ /*
+ * Step 1: verify EP-provided signature per subrange. This detects
+ * whether the EP actually applied the submap order.
+ */
+ for (i = 0; i < nsub; i++) {
+ exp = (u32)pci_endpoint_test_subrange_sig_byte(barno, i) *
+ 0x01010101U;
+ val = ioread32(bar_addr + (i * sub_size));
+ if (val != exp) {
+ dev_err(dev,
+ "BAR%d subrange%u signature mismatch @%#zx: exp %#08x got %#08x\n",
+ barno, i, (size_t)i * sub_size, exp, val);
+ ret = -EIO;
+ goto out_clear;
+ }
+ val = ioread32(bar_addr + (i * sub_size) + sub_size - sizeof(u32));
+ if (val != exp) {
+ dev_err(dev,
+ "BAR%d subrange%u signature mismatch @%#zx: exp %#08x got %#08x\n",
+ barno, i,
+ ((size_t)i * sub_size) + sub_size - sizeof(u32),
+ exp, val);
+ ret = -EIO;
+ goto out_clear;
+ }
+ }
+
+ /* Step 2: write unique pattern per subrange (write all first). */
+ for (i = 0; i < nsub; i++) {
+ pattern = pci_endpoint_test_subrange_test_byte(barno, i);
+ memset_io(bar_addr + (i * sub_size), pattern, sub_size);
+ }
+
+ /* Step 3: read back and verify (read all after all writes). */
+ for (i = 0; i < nsub; i++) {
+ pattern = pci_endpoint_test_subrange_test_byte(barno, i);
+ for (off = 0; off < sub_size; off += chunk) {
+ void *bad;
+
+ chunk = min_t(size_t, buf_size, sub_size - off);
+ memcpy_fromio(read_buf, bar_addr + (i * sub_size) + off,
+ chunk);
+ bad = memchr_inv(read_buf, pattern, chunk);
+ if (bad) {
+ size_t bad_off = (u8 *)bad - (u8 *)read_buf;
+
+ dev_err(dev,
+ "BAR%d subrange%u data mismatch @%#zx (pattern %#02x)\n",
+ barno, i, (size_t)i * sub_size + off + bad_off,
+ pattern);
+ ret = -EIO;
+ goto out_clear;
+ }
+ }
+ }
+
+out_clear:
+ kfree(read_buf);
+ clear_ret = pci_endpoint_test_bar_subrange_clear(test, barno);
+ return ret ?: clear_ret;
+}
+
static int pci_endpoint_test_intx_irq(struct pci_endpoint_test *test)
{
u32 val;
@@ -936,12 +1132,17 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd,
switch (cmd) {
case PCITEST_BAR:
+ case PCITEST_BAR_SUBRANGE:
bar = arg;
if (bar <= NO_BAR || bar > BAR_5)
goto ret;
if (is_am654_pci_dev(pdev) && bar == BAR_0)
goto ret;
- ret = pci_endpoint_test_bar(test, bar);
+
+ if (cmd == PCITEST_BAR)
+ ret = pci_endpoint_test_bar(test, bar);
+ else
+ ret = pci_endpoint_test_bar_subrange(test, bar);
break;
case PCITEST_BARS:
ret = pci_endpoint_test_bars(test);
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index e10cfe5a280b..41ebc3b9a518 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_PCI_TSM) += tsm.o
obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
obj-$(CONFIG_PCI_NPEM) += npem.o
obj-$(CONFIG_PCIE_TPH) += tph.o
+obj-$(CONFIG_CARDBUS) += setup-cardbus.o
# Endpoint library must be initialized before its users
obj-$(CONFIG_PCI_ENDPOINT) += endpoint/
@@ -47,3 +48,6 @@ obj-y += controller/
obj-y += switch/
subdir-ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
+
+CFLAGS_trace.o := -I$(src)
+obj-$(CONFIG_TRACING) += trace.o
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 4383a36fd6ca..51af9e6c541c 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -15,6 +15,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
@@ -344,7 +345,6 @@ void __weak pcibios_bus_add_device(struct pci_dev *pdev) { }
void pci_bus_add_device(struct pci_dev *dev)
{
struct device_node *dn = dev->dev.of_node;
- struct platform_device *pdev;
/*
* Can not put in pci_device_add yet because resources
@@ -362,22 +362,11 @@ void pci_bus_add_device(struct pci_dev *dev)
pci_save_state(dev);
/*
- * If the PCI device is associated with a pwrctrl device with a
- * power supply, create a device link between the PCI device and
- * pwrctrl device. This ensures that pwrctrl drivers are probed
- * before PCI client drivers.
+ * Enable runtime PM, which potentially allows the device to
+ * suspend immediately, only after the PCI state has been
+ * configured completely.
*/
- pdev = of_find_device_by_node(dn);
- if (pdev) {
- if (of_pci_supply_present(dn)) {
- if (!device_link_add(&dev->dev, &pdev->dev,
- DL_FLAG_AUTOREMOVE_CONSUMER)) {
- pci_err(dev, "failed to add device link to power control device %s\n",
- pdev->name);
- }
- }
- put_device(&pdev->dev);
- }
+ pm_runtime_enable(&dev->dev);
if (!dn || of_device_is_available(dn))
pci_dev_allow_binding(dev);
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index c254d2b8bf17..5aaed8ac6e44 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -58,6 +58,22 @@ config PCI_VERSATILE
bool "ARM Versatile PB PCI controller"
depends on ARCH_VERSATILE || COMPILE_TEST
+config PCIE_ASPEED
+ bool "ASPEED PCIe controller"
+ depends on ARCH_ASPEED || COMPILE_TEST
+ depends on OF
+ depends on PCI_MSI
+ select IRQ_MSI_LIB
+ help
+ Enable this option to support the PCIe controller found on ASPEED
+ SoCs.
+
+ This driver provides initialization and management for PCIe
+ Root Complex functionality, including INTx and MSI support.
+
+ Select Y if your platform uses an ASPEED SoC and requires PCIe
+ connectivity.
+
config PCIE_BRCMSTB
tristate "Broadcom Brcmstb PCIe controller"
depends on ARCH_BRCMSTB || ARCH_BCM2835 || ARCH_BCMBCA || \
@@ -232,7 +248,7 @@ config PCI_HYPERV_INTERFACE
driver.
config PCI_TEGRA
- bool "NVIDIA Tegra PCIe controller"
+ tristate "NVIDIA Tegra PCIe controller"
depends on ARCH_TEGRA || COMPILE_TEST
depends on PCI_MSI
select IRQ_MSI_LIB
@@ -243,6 +259,7 @@ config PCI_TEGRA
config PCIE_RCAR_HOST
bool "Renesas R-Car PCIe controller (host mode)"
depends on ARCH_RENESAS || COMPILE_TEST
+ depends on OF
depends on PCI_MSI
select IRQ_MSI_LIB
help
diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
index 229929a945c2..ac8db283f0fe 100644
--- a/drivers/pci/controller/Makefile
+++ b/drivers/pci/controller/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o
obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o
obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o
obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o
+obj-$(CONFIG_PCIE_ASPEED) += pcie-aspeed.o
# pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
obj-y += dwc/
diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c
index ecd1b0312400..6f2501479c70 100644
--- a/drivers/pci/controller/cadence/pci-j721e.c
+++ b/drivers/pci/controller/cadence/pci-j721e.c
@@ -620,9 +620,11 @@ static int j721e_pcie_probe(struct platform_device *pdev)
gpiod_set_value_cansleep(pcie->reset_gpio, 1);
}
- ret = cdns_pcie_host_setup(rc);
- if (ret < 0)
- goto err_pcie_setup;
+ if (IS_ENABLED(CONFIG_PCI_J721E_HOST)) {
+ ret = cdns_pcie_host_setup(rc);
+ if (ret < 0)
+ goto err_pcie_setup;
+ }
break;
case PCI_MODE_EP:
@@ -632,9 +634,11 @@ static int j721e_pcie_probe(struct platform_device *pdev)
goto err_get_sync;
}
- ret = cdns_pcie_ep_setup(ep);
- if (ret < 0)
- goto err_pcie_setup;
+ if (IS_ENABLED(CONFIG_PCI_J721E_EP)) {
+ ret = cdns_pcie_ep_setup(ep);
+ if (ret < 0)
+ goto err_pcie_setup;
+ }
break;
}
@@ -659,10 +663,11 @@ static void j721e_pcie_remove(struct platform_device *pdev)
struct cdns_pcie_ep *ep;
struct cdns_pcie_rc *rc;
- if (pcie->mode == PCI_MODE_RC) {
+ if (IS_ENABLED(CONFIG_PCI_J721E_HOST) &&
+ pcie->mode == PCI_MODE_RC) {
rc = container_of(cdns_pcie, struct cdns_pcie_rc, pcie);
cdns_pcie_host_disable(rc);
- } else {
+ } else if (IS_ENABLED(CONFIG_PCI_J721E_EP)) {
ep = container_of(cdns_pcie, struct cdns_pcie_ep, pcie);
cdns_pcie_ep_disable(ep);
}
@@ -728,10 +733,12 @@ static int j721e_pcie_resume_noirq(struct device *dev)
gpiod_set_value_cansleep(pcie->reset_gpio, 1);
}
- ret = cdns_pcie_host_link_setup(rc);
- if (ret < 0) {
- clk_disable_unprepare(pcie->refclk);
- return ret;
+ if (IS_ENABLED(CONFIG_PCI_J721E_HOST)) {
+ ret = cdns_pcie_host_link_setup(rc);
+ if (ret < 0) {
+ clk_disable_unprepare(pcie->refclk);
+ return ret;
+ }
}
/*
@@ -741,10 +748,12 @@ static int j721e_pcie_resume_noirq(struct device *dev)
for (enum cdns_pcie_rp_bar bar = RP_BAR0; bar <= RP_NO_BAR; bar++)
rc->avail_ib_bar[bar] = true;
- ret = cdns_pcie_host_init(rc);
- if (ret) {
- clk_disable_unprepare(pcie->refclk);
- return ret;
+ if (IS_ENABLED(CONFIG_PCI_J721E_HOST)) {
+ ret = cdns_pcie_host_init(rc);
+ if (ret) {
+ clk_disable_unprepare(pcie->refclk);
+ return ret;
+ }
}
}
diff --git a/drivers/pci/controller/cadence/pcie-cadence-host-common.c b/drivers/pci/controller/cadence/pcie-cadence-host-common.c
index 15415d7f35ee..2b0211870f02 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-host-common.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-host-common.c
@@ -173,11 +173,21 @@ int cdns_pcie_host_dma_ranges_cmp(void *priv, const struct list_head *a,
const struct list_head *b)
{
struct resource_entry *entry1, *entry2;
+ u64 size1, size2;
entry1 = container_of(a, struct resource_entry, node);
entry2 = container_of(b, struct resource_entry, node);
- return resource_size(entry2->res) - resource_size(entry1->res);
+ size1 = resource_size(entry1->res);
+ size2 = resource_size(entry2->res);
+
+ if (size1 > size2)
+ return -1;
+
+ if (size1 < size2)
+ return 1;
+
+ return 0;
}
EXPORT_SYMBOL_GPL(cdns_pcie_host_dma_ranges_cmp);
diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c
index e6f1a4ac0fb7..a1eada56edba 100644
--- a/drivers/pci/controller/cadence/pcie-cadence.c
+++ b/drivers/pci/controller/cadence/pcie-cadence.c
@@ -13,13 +13,13 @@
u8 cdns_pcie_find_capability(struct cdns_pcie *pcie, u8 cap)
{
return PCI_FIND_NEXT_CAP(cdns_pcie_read_cfg, PCI_CAPABILITY_LIST,
- cap, pcie);
+ cap, NULL, pcie);
}
EXPORT_SYMBOL_GPL(cdns_pcie_find_capability);
u16 cdns_pcie_find_ext_capability(struct cdns_pcie *pcie, u8 cap)
{
- return PCI_FIND_NEXT_EXT_CAP(cdns_pcie_read_cfg, 0, cap, pcie);
+ return PCI_FIND_NEXT_EXT_CAP(cdns_pcie_read_cfg, 0, cap, NULL, pcie);
}
EXPORT_SYMBOL_GPL(cdns_pcie_find_ext_capability);
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index 519b59422b47..d0aa031397fa 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -228,7 +228,7 @@ config PCIE_TEGRA194
config PCIE_TEGRA194_HOST
tristate "NVIDIA Tegra194 (and later) PCIe controller (host mode)"
- depends on ARCH_TEGRA_194_SOC || COMPILE_TEST
+ depends on (ARCH_TEGRA && ARM64) || COMPILE_TEST
depends on PCI_MSI
select PCIE_DW_HOST
select PHY_TEGRA194_P2U
@@ -243,7 +243,7 @@ config PCIE_TEGRA194_HOST
config PCIE_TEGRA194_EP
tristate "NVIDIA Tegra194 (and later) PCIe controller (endpoint mode)"
- depends on ARCH_TEGRA_194_SOC || COMPILE_TEST
+ depends on (ARCH_TEGRA && ARM64) || COMPILE_TEST
depends on PCI_ENDPOINT
select PCIE_DW_EP
select PHY_TEGRA194_P2U
diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c
index 01cfd9aeb0b8..d5d26229063f 100644
--- a/drivers/pci/controller/dwc/pci-dra7xx.c
+++ b/drivers/pci/controller/dwc/pci-dra7xx.c
@@ -424,6 +424,7 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features dra7xx_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
};
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index 4668fc9648bf..a5b8d0b71677 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -52,6 +52,8 @@
#define IMX95_PCIE_REF_CLKEN BIT(23)
#define IMX95_PCIE_PHY_CR_PARA_SEL BIT(9)
#define IMX95_PCIE_SS_RW_REG_1 0xf4
+#define IMX95_PCIE_CLKREQ_OVERRIDE_EN BIT(8)
+#define IMX95_PCIE_CLKREQ_OVERRIDE_VAL BIT(9)
#define IMX95_PCIE_SYS_AUX_PWR_DET BIT(31)
#define IMX95_PE0_GEN_CTRL_1 0x1050
@@ -114,6 +116,7 @@ enum imx_pcie_variants {
#define IMX_PCIE_FLAG_BROKEN_SUSPEND BIT(9)
#define IMX_PCIE_FLAG_HAS_LUT BIT(10)
#define IMX_PCIE_FLAG_8GT_ECN_ERR051586 BIT(11)
+#define IMX_PCIE_FLAG_SKIP_L23_READY BIT(12)
#define imx_check_flag(pci, val) (pci->drvdata->flags & val)
@@ -136,6 +139,7 @@ struct imx_pcie_drvdata {
int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
int (*core_reset)(struct imx_pcie *pcie, bool assert);
int (*wait_pll_lock)(struct imx_pcie *pcie);
+ void (*clr_clkreq_override)(struct imx_pcie *pcie);
const struct dw_pcie_host_ops *ops;
};
@@ -149,6 +153,8 @@ struct imx_pcie {
struct gpio_desc *reset_gpiod;
struct clk_bulk_data *clks;
int num_clks;
+ bool supports_clkreq;
+ bool enable_ext_refclk;
struct regmap *iomuxc_gpr;
u16 msi_ctrl;
u32 controller_id;
@@ -241,6 +247,8 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie)
static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
{
+ bool ext = imx_pcie->enable_ext_refclk;
+
/*
* ERR051624: The Controller Without Vaux Cannot Exit L23 Ready
* Through Beacon or PERST# De-assertion
@@ -259,13 +267,12 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
IMX95_PCIE_PHY_CR_PARA_SEL,
IMX95_PCIE_PHY_CR_PARA_SEL);
- regmap_update_bits(imx_pcie->iomuxc_gpr,
- IMX95_PCIE_PHY_GEN_CTRL,
- IMX95_PCIE_REF_USE_PAD, 0);
- regmap_update_bits(imx_pcie->iomuxc_gpr,
- IMX95_PCIE_SS_RW_REG_0,
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL,
+ ext ? IMX95_PCIE_REF_USE_PAD : 0,
+ IMX95_PCIE_REF_USE_PAD);
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
IMX95_PCIE_REF_CLKEN,
- IMX95_PCIE_REF_CLKEN);
+ ext ? 0 : IMX95_PCIE_REF_CLKEN);
return 0;
}
@@ -685,7 +692,7 @@ static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
return 0;
}
-static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
+static void imx8mm_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable)
{
int offset = imx_pcie_grp_offset(imx_pcie);
@@ -695,6 +702,11 @@ static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
regmap_update_bits(imx_pcie->iomuxc_gpr, offset,
IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN,
enable ? IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN : 0);
+}
+
+static int imx8mm_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
+{
+ imx8mm_pcie_clkreq_override(imx_pcie, enable);
return 0;
}
@@ -706,6 +718,32 @@ static int imx7d_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
return 0;
}
+static void imx95_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable)
+{
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1,
+ IMX95_PCIE_CLKREQ_OVERRIDE_EN,
+ enable ? IMX95_PCIE_CLKREQ_OVERRIDE_EN : 0);
+ regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_1,
+ IMX95_PCIE_CLKREQ_OVERRIDE_VAL,
+ enable ? IMX95_PCIE_CLKREQ_OVERRIDE_VAL : 0);
+}
+
+static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
+{
+ imx95_pcie_clkreq_override(imx_pcie, enable);
+ return 0;
+}
+
+static void imx8mm_pcie_clr_clkreq_override(struct imx_pcie *imx_pcie)
+{
+ imx8mm_pcie_clkreq_override(imx_pcie, false);
+}
+
+static void imx95_pcie_clr_clkreq_override(struct imx_pcie *imx_pcie)
+{
+ imx95_pcie_clkreq_override(imx_pcie, false);
+}
+
static int imx_pcie_clk_enable(struct imx_pcie *imx_pcie)
{
struct dw_pcie *pci = imx_pcie->pci;
@@ -1322,6 +1360,12 @@ static void imx_pcie_host_post_init(struct dw_pcie_rp *pp)
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
dw_pcie_dbi_ro_wr_dis(pci);
}
+
+ /* Clear CLKREQ# override if supports_clkreq is true and link is up */
+ if (dw_pcie_link_up(pci) && imx_pcie->supports_clkreq) {
+ if (imx_pcie->drvdata->clr_clkreq_override)
+ imx_pcie->drvdata->clr_clkreq_override(imx_pcie);
+ }
}
/*
@@ -1387,6 +1431,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features imx8m_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_RESERVED, },
.bar[BAR_3] = { .type = BAR_RESERVED, },
@@ -1396,6 +1441,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = {
};
static const struct pci_epc_features imx8q_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_RESERVED, },
.bar[BAR_3] = { .type = BAR_RESERVED, },
@@ -1416,6 +1462,7 @@ static const struct pci_epc_features imx8q_pcie_epc_features = {
* BAR5 | Enable | 32-bit | 64 KB | Programmable Size
*/
static const struct pci_epc_features imx95_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_FIXED, .fixed_size = SZ_64K, },
.align = SZ_4K,
@@ -1602,7 +1649,7 @@ static int imx_pcie_probe(struct platform_device *pdev)
struct imx_pcie *imx_pcie;
struct device_node *np;
struct device_node *node = dev->of_node;
- int ret, domain;
+ int i, ret, domain;
u16 val;
imx_pcie = devm_kzalloc(dev, sizeof(*imx_pcie), GFP_KERNEL);
@@ -1653,6 +1700,9 @@ static int imx_pcie_probe(struct platform_device *pdev)
if (imx_pcie->num_clks < 0)
return dev_err_probe(dev, imx_pcie->num_clks,
"failed to get clocks\n");
+ for (i = 0; i < imx_pcie->num_clks; i++)
+ if (strncmp(imx_pcie->clks[i].id, "extref", 6) == 0)
+ imx_pcie->enable_ext_refclk = true;
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_PHYDRV)) {
imx_pcie->phy = devm_phy_get(dev, "pcie-phy");
@@ -1740,6 +1790,7 @@ static int imx_pcie_probe(struct platform_device *pdev)
/* Limit link speed */
pci->max_link_speed = 1;
of_property_read_u32(node, "fsl,max-link-speed", &pci->max_link_speed);
+ imx_pcie->supports_clkreq = of_property_read_bool(node, "supports-clkreq");
ret = devm_regulator_get_enable_optional(&pdev->dev, "vpcie3v3aux");
if (ret < 0 && ret != -ENODEV)
@@ -1777,6 +1828,8 @@ static int imx_pcie_probe(struct platform_device *pdev)
*/
imx_pcie_add_lut_by_rid(imx_pcie, 0);
} else {
+ if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY))
+ pci->pp.skip_l23_ready = true;
pci->pp.use_atu_msg = true;
ret = dw_pcie_host_init(&pci->pp);
if (ret < 0)
@@ -1838,6 +1891,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.variant = IMX6QP,
.flags = IMX_PCIE_FLAG_IMX_PHY |
IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND |
+ IMX_PCIE_FLAG_SKIP_L23_READY |
IMX_PCIE_FLAG_SUPPORTS_SUSPEND,
.dbi_length = 0x200,
.gpr = "fsl,imx6q-iomuxc-gpr",
@@ -1854,6 +1908,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.variant = IMX7D,
.flags = IMX_PCIE_FLAG_SUPPORTS_SUSPEND |
IMX_PCIE_FLAG_HAS_APP_RESET |
+ IMX_PCIE_FLAG_SKIP_L23_READY |
IMX_PCIE_FLAG_HAS_PHY_RESET,
.gpr = "fsl,imx7d-iomuxc-gpr",
.mode_off[0] = IOMUXC_GPR12,
@@ -1873,6 +1928,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_mask[1] = IMX8MQ_GPR12_PCIE2_CTRL_DEVICE_TYPE,
.init_phy = imx8mq_pcie_init_phy,
.enable_ref_clk = imx8mm_pcie_enable_ref_clk,
+ .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override,
},
[IMX8MM] = {
.variant = IMX8MM,
@@ -1883,6 +1939,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.enable_ref_clk = imx8mm_pcie_enable_ref_clk,
+ .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override,
},
[IMX8MP] = {
.variant = IMX8MP,
@@ -1893,6 +1950,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.enable_ref_clk = imx8mm_pcie_enable_ref_clk,
+ .clr_clkreq_override = imx8mm_pcie_clr_clkreq_override,
},
[IMX8Q] = {
.variant = IMX8Q,
@@ -1913,6 +1971,8 @@ static const struct imx_pcie_drvdata drvdata[] = {
.core_reset = imx95_pcie_core_reset,
.init_phy = imx95_pcie_init_phy,
.wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
+ .enable_ref_clk = imx95_pcie_enable_ref_clk,
+ .clr_clkreq_override = imx95_pcie_clr_clkreq_override,
},
[IMX8MQ_EP] = {
.variant = IMX8MQ_EP,
@@ -1969,6 +2029,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.core_reset = imx95_pcie_core_reset,
.wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
.epc_features = &imx95_pcie_epc_features,
+ .enable_ref_clk = imx95_pcie_enable_ref_clk,
.mode = DW_PCIE_EP_TYPE,
},
};
diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c
index f86d9111f863..20fa4dadb82a 100644
--- a/drivers/pci/controller/dwc/pci-keystone.c
+++ b/drivers/pci/controller/dwc/pci-keystone.c
@@ -930,6 +930,7 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features ks_pcie_am654_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.msix_capable = true,
.bar[BAR_0] = { .type = BAR_RESERVED, },
diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c
index f4a136ee2daf..e994b75986c3 100644
--- a/drivers/pci/controller/dwc/pcie-artpec6.c
+++ b/drivers/pci/controller/dwc/pcie-artpec6.c
@@ -370,6 +370,7 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features artpec6_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
};
diff --git a/drivers/pci/controller/dwc/pcie-designware-debugfs.c b/drivers/pci/controller/dwc/pcie-designware-debugfs.c
index 0fbf86c0b97e..0d1340c9b364 100644
--- a/drivers/pci/controller/dwc/pcie-designware-debugfs.c
+++ b/drivers/pci/controller/dwc/pcie-designware-debugfs.c
@@ -443,63 +443,13 @@ static ssize_t counter_value_read(struct file *file, char __user *buf,
return simple_read_from_buffer(buf, count, ppos, debugfs_buf, pos);
}
-static const char *ltssm_status_string(enum dw_pcie_ltssm ltssm)
-{
- const char *str;
-
- switch (ltssm) {
-#define DW_PCIE_LTSSM_NAME(n) case n: str = #n; break
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_QUIET);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_ACT);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_ACTIVE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_COMPLIANCE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_CONFIG);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_PRE_DETECT_QUIET);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_WAIT);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_START);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_ACEPT);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_WAI);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_ACEPT);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_COMPLETE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_IDLE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_LOCK);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_SPEED);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_RCVRCFG);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_IDLE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0S);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L123_SEND_EIDLE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_IDLE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_IDLE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_WAKE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_ENTRY);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_IDLE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ENTRY);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ACTIVE);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT_TIMEOUT);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET_ENTRY);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ0);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ1);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ2);
- DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ3);
- default:
- str = "DW_PCIE_LTSSM_UNKNOWN";
- break;
- }
-
- return str + strlen("DW_PCIE_LTSSM_");
-}
-
static int ltssm_status_show(struct seq_file *s, void *v)
{
struct dw_pcie *pci = s->private;
enum dw_pcie_ltssm val;
val = dw_pcie_get_ltssm(pci);
- seq_printf(s, "%s (0x%02x)\n", ltssm_status_string(val), val);
+ seq_printf(s, "%s (0x%02x)\n", dw_pcie_ltssm_status_string(val), val);
return 0;
}
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 19571ac2b961..7e7844ff0f7e 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -72,47 +72,15 @@ EXPORT_SYMBOL_GPL(dw_pcie_ep_reset_bar);
static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap)
{
return PCI_FIND_NEXT_CAP(dw_pcie_ep_read_cfg, PCI_CAPABILITY_LIST,
- cap, ep, func_no);
+ cap, NULL, ep, func_no);
}
-/**
- * dw_pcie_ep_hide_ext_capability - Hide a capability from the linked list
- * @pci: DWC PCI device
- * @prev_cap: Capability preceding the capability that should be hidden
- * @cap: Capability that should be hidden
- *
- * Return: 0 if success, errno otherwise.
- */
-int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, u8 prev_cap, u8 cap)
+static u16 dw_pcie_ep_find_ext_capability(struct dw_pcie_ep *ep,
+ u8 func_no, u8 cap)
{
- u16 prev_cap_offset, cap_offset;
- u32 prev_cap_header, cap_header;
-
- prev_cap_offset = dw_pcie_find_ext_capability(pci, prev_cap);
- if (!prev_cap_offset)
- return -EINVAL;
-
- prev_cap_header = dw_pcie_readl_dbi(pci, prev_cap_offset);
- cap_offset = PCI_EXT_CAP_NEXT(prev_cap_header);
- cap_header = dw_pcie_readl_dbi(pci, cap_offset);
-
- /* cap must immediately follow prev_cap. */
- if (PCI_EXT_CAP_ID(cap_header) != cap)
- return -EINVAL;
-
- /* Clear next ptr. */
- prev_cap_header &= ~GENMASK(31, 20);
-
- /* Set next ptr to next ptr of cap. */
- prev_cap_header |= cap_header & GENMASK(31, 20);
-
- dw_pcie_dbi_ro_wr_en(pci);
- dw_pcie_writel_dbi(pci, prev_cap_offset, prev_cap_header);
- dw_pcie_dbi_ro_wr_dis(pci);
-
- return 0;
+ return PCI_FIND_NEXT_EXT_CAP(dw_pcie_ep_read_cfg, 0,
+ cap, NULL, ep, func_no);
}
-EXPORT_SYMBOL_GPL(dw_pcie_ep_hide_ext_capability);
static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epf_header *hdr)
@@ -139,18 +107,23 @@ static int dw_pcie_ep_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
return 0;
}
-static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
- dma_addr_t parent_bus_addr, enum pci_barno bar,
- size_t size)
+/* BAR Match Mode inbound iATU mapping */
+static int dw_pcie_ep_ib_atu_bar(struct dw_pcie_ep *ep, u8 func_no, int type,
+ dma_addr_t parent_bus_addr, enum pci_barno bar,
+ size_t size)
{
int ret;
u32 free_win;
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
- if (!ep->bar_to_atu[bar])
+ if (!ep_func)
+ return -EINVAL;
+
+ if (!ep_func->bar_to_atu[bar])
free_win = find_first_zero_bit(ep->ib_window_map, pci->num_ib_windows);
else
- free_win = ep->bar_to_atu[bar] - 1;
+ free_win = ep_func->bar_to_atu[bar] - 1;
if (free_win >= pci->num_ib_windows) {
dev_err(pci->dev, "No free inbound window\n");
@@ -168,12 +141,190 @@ static int dw_pcie_ep_inbound_atu(struct dw_pcie_ep *ep, u8 func_no, int type,
* Always increment free_win before assignment, since value 0 is used to identify
* unallocated mapping.
*/
- ep->bar_to_atu[bar] = free_win + 1;
+ ep_func->bar_to_atu[bar] = free_win + 1;
set_bit(free_win, ep->ib_window_map);
return 0;
}
+static void dw_pcie_ep_clear_ib_maps(struct dw_pcie_ep *ep, u8 func_no, enum pci_barno bar)
+{
+ struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct device *dev = pci->dev;
+ unsigned int i, num;
+ u32 atu_index;
+ u32 *indexes;
+
+ if (!ep_func)
+ return;
+
+ /* Tear down the BAR Match Mode mapping, if any. */
+ if (ep_func->bar_to_atu[bar]) {
+ atu_index = ep_func->bar_to_atu[bar] - 1;
+ dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index);
+ clear_bit(atu_index, ep->ib_window_map);
+ ep_func->bar_to_atu[bar] = 0;
+ }
+
+ /* Tear down all Address Match Mode mappings, if any. */
+ indexes = ep_func->ib_atu_indexes[bar];
+ num = ep_func->num_ib_atu_indexes[bar];
+ ep_func->ib_atu_indexes[bar] = NULL;
+ ep_func->num_ib_atu_indexes[bar] = 0;
+ if (!indexes)
+ return;
+ for (i = 0; i < num; i++) {
+ dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, indexes[i]);
+ clear_bit(indexes[i], ep->ib_window_map);
+ }
+ devm_kfree(dev, indexes);
+}
+
+static u64 dw_pcie_ep_read_bar_assigned(struct dw_pcie_ep *ep, u8 func_no,
+ enum pci_barno bar, int flags)
+{
+ u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar);
+ u32 lo, hi;
+ u64 addr;
+
+ lo = dw_pcie_ep_readl_dbi(ep, func_no, reg);
+
+ if (flags & PCI_BASE_ADDRESS_SPACE)
+ return lo & PCI_BASE_ADDRESS_IO_MASK;
+
+ addr = lo & PCI_BASE_ADDRESS_MEM_MASK;
+ if (!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64))
+ return addr;
+
+ hi = dw_pcie_ep_readl_dbi(ep, func_no, reg + 4);
+ return addr | ((u64)hi << 32);
+}
+
+static int dw_pcie_ep_validate_submap(struct dw_pcie_ep *ep,
+ const struct pci_epf_bar_submap *submap,
+ unsigned int num_submap, size_t bar_size)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ u32 align = pci->region_align;
+ size_t off = 0;
+ unsigned int i;
+ size_t size;
+
+ if (!align || !IS_ALIGNED(bar_size, align))
+ return -EINVAL;
+
+ /*
+ * The submap array order defines the BAR layout (submap[0] starts
+ * at offset 0 and each entry immediately follows the previous
+ * one). Here, validate that it forms a strict, gapless
+ * decomposition of the BAR:
+ * - each entry has a non-zero size
+ * - sizes, implicit offsets and phys_addr are aligned to
+ * pci->region_align
+ * - each entry lies within the BAR range
+ * - the entries exactly cover the whole BAR
+ *
+ * Note: dw_pcie_prog_inbound_atu() also checks alignment for the
+ * PCI address and the target phys_addr, but validating up-front
+ * avoids partially programming iATU windows in vain.
+ */
+ for (i = 0; i < num_submap; i++) {
+ size = submap[i].size;
+
+ if (!size)
+ return -EINVAL;
+
+ if (!IS_ALIGNED(size, align) || !IS_ALIGNED(off, align))
+ return -EINVAL;
+
+ if (!IS_ALIGNED(submap[i].phys_addr, align))
+ return -EINVAL;
+
+ if (off > bar_size || size > bar_size - off)
+ return -EINVAL;
+
+ off += size;
+ }
+ if (off != bar_size)
+ return -EINVAL;
+
+ return 0;
+}
+
+/* Address Match Mode inbound iATU mapping */
+static int dw_pcie_ep_ib_atu_addr(struct dw_pcie_ep *ep, u8 func_no, int type,
+ const struct pci_epf_bar *epf_bar)
+{
+ struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
+ const struct pci_epf_bar_submap *submap = epf_bar->submap;
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ enum pci_barno bar = epf_bar->barno;
+ struct device *dev = pci->dev;
+ u64 pci_addr, parent_bus_addr;
+ u64 size, base, off = 0;
+ int free_win, ret;
+ unsigned int i;
+ u32 *indexes;
+
+ if (!ep_func || !epf_bar->num_submap || !submap || !epf_bar->size)
+ return -EINVAL;
+
+ ret = dw_pcie_ep_validate_submap(ep, submap, epf_bar->num_submap,
+ epf_bar->size);
+ if (ret)
+ return ret;
+
+ base = dw_pcie_ep_read_bar_assigned(ep, func_no, bar, epf_bar->flags);
+ if (!base) {
+ dev_err(dev,
+ "BAR%u not assigned, cannot set up sub-range mappings\n",
+ bar);
+ return -EINVAL;
+ }
+
+ indexes = devm_kcalloc(dev, epf_bar->num_submap, sizeof(*indexes),
+ GFP_KERNEL);
+ if (!indexes)
+ return -ENOMEM;
+
+ ep_func->ib_atu_indexes[bar] = indexes;
+ ep_func->num_ib_atu_indexes[bar] = 0;
+
+ for (i = 0; i < epf_bar->num_submap; i++) {
+ size = submap[i].size;
+ parent_bus_addr = submap[i].phys_addr;
+
+ if (off > (~0ULL) - base) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ pci_addr = base + off;
+ off += size;
+
+ free_win = find_first_zero_bit(ep->ib_window_map,
+ pci->num_ib_windows);
+ if (free_win >= pci->num_ib_windows) {
+ ret = -ENOSPC;
+ goto err;
+ }
+
+ ret = dw_pcie_prog_inbound_atu(pci, free_win, type,
+ parent_bus_addr, pci_addr, size);
+ if (ret)
+ goto err;
+
+ set_bit(free_win, ep->ib_window_map);
+ indexes[i] = free_win;
+ ep_func->num_ib_atu_indexes[bar] = i + 1;
+ }
+ return 0;
+err:
+ dw_pcie_ep_clear_ib_maps(ep, func_no, bar);
+ return ret;
+}
+
static int dw_pcie_ep_outbound_atu(struct dw_pcie_ep *ep,
struct dw_pcie_ob_atu_cfg *atu)
{
@@ -204,35 +355,34 @@ static void dw_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
enum pci_barno bar = epf_bar->barno;
- u32 atu_index = ep->bar_to_atu[bar] - 1;
+ struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
- if (!ep->bar_to_atu[bar])
+ if (!ep_func || !ep_func->epf_bar[bar])
return;
__dw_pcie_ep_reset_bar(pci, func_no, bar, epf_bar->flags);
- dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index);
- clear_bit(atu_index, ep->ib_window_map);
- ep->epf_bar[bar] = NULL;
- ep->bar_to_atu[bar] = 0;
+ dw_pcie_ep_clear_ib_maps(ep, func_no, bar);
+
+ ep_func->epf_bar[bar] = NULL;
}
-static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie *pci,
+static unsigned int dw_pcie_ep_get_rebar_offset(struct dw_pcie_ep *ep, u8 func_no,
enum pci_barno bar)
{
u32 reg, bar_index;
unsigned int offset, nbars;
int i;
- offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
+ offset = dw_pcie_ep_find_ext_capability(ep, func_no, PCI_EXT_CAP_ID_REBAR);
if (!offset)
return offset;
- reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
+ reg = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL);
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, reg);
for (i = 0; i < nbars; i++, offset += PCI_REBAR_CTRL) {
- reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
+ reg = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL);
bar_index = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, reg);
if (bar_index == bar)
return offset;
@@ -253,7 +403,7 @@ static int dw_pcie_ep_set_bar_resizable(struct dw_pcie_ep *ep, u8 func_no,
u32 rebar_cap, rebar_ctrl;
int ret;
- rebar_offset = dw_pcie_ep_get_rebar_offset(pci, bar);
+ rebar_offset = dw_pcie_ep_get_rebar_offset(ep, func_no, bar);
if (!rebar_offset)
return -EINVAL;
@@ -283,16 +433,16 @@ static int dw_pcie_ep_set_bar_resizable(struct dw_pcie_ep *ep, u8 func_no,
* 1 MB to 128 TB. Bits 31:16 in PCI_REBAR_CTRL define "supported sizes"
* bits for sizes 256 TB to 8 EB. Disallow sizes 256 TB to 8 EB.
*/
- rebar_ctrl = dw_pcie_readl_dbi(pci, rebar_offset + PCI_REBAR_CTRL);
+ rebar_ctrl = dw_pcie_ep_readl_dbi(ep, func_no, rebar_offset + PCI_REBAR_CTRL);
rebar_ctrl &= ~GENMASK(31, 16);
- dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CTRL, rebar_ctrl);
+ dw_pcie_ep_writel_dbi(ep, func_no, rebar_offset + PCI_REBAR_CTRL, rebar_ctrl);
/*
* The "selected size" (bits 13:8) in PCI_REBAR_CTRL are automatically
* updated when writing PCI_REBAR_CAP, see "Figure 3-26 Resizable BAR
* Example for 32-bit Memory BAR0" in DWC EP databook 5.96a.
*/
- dw_pcie_writel_dbi(pci, rebar_offset + PCI_REBAR_CAP, rebar_cap);
+ dw_pcie_ep_writel_dbi(ep, func_no, rebar_offset + PCI_REBAR_CAP, rebar_cap);
dw_pcie_dbi_ro_wr_dis(pci);
@@ -341,12 +491,16 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
enum pci_barno bar = epf_bar->barno;
size_t size = epf_bar->size;
enum pci_epc_bar_type bar_type;
int flags = epf_bar->flags;
int ret, type;
+ if (!ep_func)
+ return -EINVAL;
+
/*
* DWC does not allow BAR pairs to overlap, e.g. you cannot combine BARs
* 1 and 2 to form a 64-bit BAR.
@@ -360,21 +514,38 @@ static int dw_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
* calling clear_bar() would clear the BAR's PCI address assigned by the
* host).
*/
- if (ep->epf_bar[bar]) {
+ if (ep_func->epf_bar[bar]) {
/*
* We can only dynamically change a BAR if the new BAR size and
* BAR flags do not differ from the existing configuration.
*/
- if (ep->epf_bar[bar]->barno != bar ||
- ep->epf_bar[bar]->size != size ||
- ep->epf_bar[bar]->flags != flags)
+ if (ep_func->epf_bar[bar]->barno != bar ||
+ ep_func->epf_bar[bar]->size != size ||
+ ep_func->epf_bar[bar]->flags != flags)
return -EINVAL;
/*
+ * When dynamically changing a BAR, tear down any existing
+ * mappings before re-programming.
+ */
+ if (ep_func->epf_bar[bar]->num_submap || epf_bar->num_submap)
+ dw_pcie_ep_clear_ib_maps(ep, func_no, bar);
+
+ /*
* When dynamically changing a BAR, skip writing the BAR reg, as
* that would clear the BAR's PCI address assigned by the host.
*/
goto config_atu;
+ } else {
+ /*
+ * Subrange mapping is an update-only operation. The BAR
+ * must have been configured once without submaps so that
+ * subsequent set_bar() calls can update inbound mappings
+ * without touching the BAR register (and clobbering the
+ * host-assigned address).
+ */
+ if (epf_bar->num_submap)
+ return -EINVAL;
}
bar_type = dw_pcie_ep_get_bar_type(ep, bar);
@@ -408,12 +579,16 @@ config_atu:
else
type = PCIE_ATU_TYPE_IO;
- ret = dw_pcie_ep_inbound_atu(ep, func_no, type, epf_bar->phys_addr, bar,
- size);
+ if (epf_bar->num_submap)
+ ret = dw_pcie_ep_ib_atu_addr(ep, func_no, type, epf_bar);
+ else
+ ret = dw_pcie_ep_ib_atu_bar(ep, func_no, type,
+ epf_bar->phys_addr, bar, size);
+
if (ret)
return ret;
- ep->epf_bar[bar] = epf_bar;
+ ep_func->epf_bar[bar] = epf_bar;
return 0;
}
@@ -601,6 +776,16 @@ static void dw_pcie_ep_stop(struct pci_epc *epc)
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ /*
+ * Tear down the dedicated outbound window used for MSI
+ * generation. This avoids leaking an iATU window across
+ * endpoint stop/start cycles.
+ */
+ if (ep->msi_iatu_mapped) {
+ dw_pcie_ep_unmap_addr(epc, 0, 0, ep->msi_mem_phys);
+ ep->msi_iatu_mapped = false;
+ }
+
dw_pcie_stop_link(pci);
}
@@ -702,14 +887,37 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
msg_addr = ((u64)msg_addr_upper) << 32 | msg_addr_lower;
msg_addr = dw_pcie_ep_align_addr(epc, msg_addr, &map_size, &offset);
- ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr,
- map_size);
- if (ret)
- return ret;
- writel(msg_data | (interrupt_num - 1), ep->msi_mem + offset);
+ /*
+ * Program the outbound iATU once and keep it enabled.
+ *
+ * The spec warns that updating iATU registers while there are
+ * operations in flight on the AXI bridge interface is not
+ * supported, so we avoid reprogramming the region on every MSI,
+ * specifically unmapping immediately after writel().
+ */
+ if (!ep->msi_iatu_mapped) {
+ ret = dw_pcie_ep_map_addr(epc, func_no, 0,
+ ep->msi_mem_phys, msg_addr,
+ map_size);
+ if (ret)
+ return ret;
+
+ ep->msi_iatu_mapped = true;
+ ep->msi_msg_addr = msg_addr;
+ ep->msi_map_size = map_size;
+ } else if (WARN_ON_ONCE(ep->msi_msg_addr != msg_addr ||
+ ep->msi_map_size != map_size)) {
+ /*
+ * The host changed the MSI target address or the required
+ * mapping size changed. Reprogramming the iATU at runtime is
+ * unsafe on this controller, so bail out instead of trying to
+ * update the existing region.
+ */
+ return -EINVAL;
+ }
- dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys);
+ writel(msg_data | (interrupt_num - 1), ep->msi_mem + offset);
return 0;
}
@@ -775,7 +983,7 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
bir = FIELD_GET(PCI_MSIX_TABLE_BIR, tbl_offset);
tbl_offset &= PCI_MSIX_TABLE_OFFSET;
- msix_tbl = ep->epf_bar[bir]->addr + tbl_offset;
+ msix_tbl = ep_func->epf_bar[bir]->addr + tbl_offset;
msg_addr = msix_tbl[(interrupt_num - 1)].msg_addr;
msg_data = msix_tbl[(interrupt_num - 1)].msg_data;
vec_ctrl = msix_tbl[(interrupt_num - 1)].vector_ctrl;
@@ -836,20 +1044,20 @@ void dw_pcie_ep_deinit(struct dw_pcie_ep *ep)
}
EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit);
-static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)
+static void dw_pcie_ep_init_rebar_registers(struct dw_pcie_ep *ep, u8 func_no)
{
- struct dw_pcie_ep *ep = &pci->ep;
- unsigned int offset;
- unsigned int nbars;
+ struct dw_pcie_ep_func *ep_func = dw_pcie_ep_get_func_from_ep(ep, func_no);
+ unsigned int offset, nbars;
enum pci_barno bar;
u32 reg, i, val;
- offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_REBAR);
+ if (!ep_func)
+ return;
- dw_pcie_dbi_ro_wr_en(pci);
+ offset = dw_pcie_ep_find_ext_capability(ep, func_no, PCI_EXT_CAP_ID_REBAR);
if (offset) {
- reg = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
+ reg = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL);
nbars = FIELD_GET(PCI_REBAR_CTRL_NBAR_MASK, reg);
/*
@@ -870,16 +1078,28 @@ static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)
* the controller when RESBAR_CAP_REG is written, which
* is why RESBAR_CAP_REG is written here.
*/
- val = dw_pcie_readl_dbi(pci, offset + PCI_REBAR_CTRL);
+ val = dw_pcie_ep_readl_dbi(ep, func_no, offset + PCI_REBAR_CTRL);
bar = FIELD_GET(PCI_REBAR_CTRL_BAR_IDX, val);
- if (ep->epf_bar[bar])
- pci_epc_bar_size_to_rebar_cap(ep->epf_bar[bar]->size, &val);
+ if (ep_func->epf_bar[bar])
+ pci_epc_bar_size_to_rebar_cap(ep_func->epf_bar[bar]->size, &val);
else
val = BIT(4);
- dw_pcie_writel_dbi(pci, offset + PCI_REBAR_CAP, val);
+ dw_pcie_ep_writel_dbi(ep, func_no, offset + PCI_REBAR_CAP, val);
}
}
+}
+
+static void dw_pcie_ep_init_non_sticky_registers(struct dw_pcie *pci)
+{
+ struct dw_pcie_ep *ep = &pci->ep;
+ u8 funcs = ep->epc->max_functions;
+ u8 func_no;
+
+ dw_pcie_dbi_ro_wr_en(pci);
+
+ for (func_no = 0; func_no < funcs; func_no++)
+ dw_pcie_ep_init_rebar_registers(ep, func_no);
dw_pcie_setup(pci);
dw_pcie_dbi_ro_wr_dis(pci);
@@ -967,6 +1187,18 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)
if (ep->ops->init)
ep->ops->init(ep);
+ /*
+ * PCIe r6.0, section 7.9.15 states that for endpoints that support
+ * PTM, this capability structure is required in exactly one
+ * function, which controls the PTM behavior of all PTM capable
+ * functions. This indicates the PTM capability structure
+ * represents controller-level registers rather than per-function
+ * registers.
+ *
+ * Therefore, PTM capability registers are configured using the
+ * standard DBI accessors, instead of func_no indexed per-function
+ * accessors.
+ */
ptm_cap_base = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_PTM);
/*
@@ -1087,6 +1319,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
struct device *dev = pci->dev;
INIT_LIST_HEAD(&ep->func_list);
+ ep->msi_iatu_mapped = false;
+ ep->msi_msg_addr = 0;
+ ep->msi_map_size = 0;
epc = devm_pci_epc_create(dev, &epc_ops);
if (IS_ERR(epc)) {
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index f116591975ff..6ae6189e9b8a 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -244,7 +244,7 @@ void dw_pcie_msi_init(struct dw_pcie_rp *pp)
u64 msi_target = (u64)pp->msi_data;
u32 ctrl, num_ctrls;
- if (!pci_msi_enabled() || !pp->has_msi_ctrl)
+ if (!pci_msi_enabled() || !pp->use_imsi_rx)
return;
num_ctrls = pp->num_vectors / MAX_MSI_IRQS_PER_CTRL;
@@ -356,10 +356,20 @@ int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)
* order not to miss MSI TLPs from those devices the MSI target
* address has to be within the lowest 4GB.
*
- * Note until there is a better alternative found the reservation is
- * done by allocating from the artificially limited DMA-coherent
- * memory.
+ * Per DWC databook r6.21a, section 3.10.2.3, the incoming MWr TLP
+ * targeting the MSI_CTRL_ADDR is terminated by the iMSI-RX and never
+ * appears on the AXI bus. So MSI_CTRL_ADDR address doesn't need to be
+ * mapped and can be any memory that doesn't get allocated for the BAR
+ * memory. Since most of the platforms provide 32-bit address for
+ * 'config' region, try cfg0_base as the first option for the MSI target
+ * address if it's a 32-bit address. Otherwise, try 32-bit and 64-bit
+ * coherent memory allocation one by one.
*/
+ if (!(pp->cfg0_base & GENMASK_ULL(63, 32))) {
+ pp->msi_data = pp->cfg0_base;
+ return 0;
+ }
+
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
if (!ret)
msi_vaddr = dmam_alloc_coherent(dev, sizeof(u64), &pp->msi_data,
@@ -582,15 +592,15 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
}
if (pci_msi_enabled()) {
- pp->has_msi_ctrl = !(pp->ops->msi_init ||
+ pp->use_imsi_rx = !(pp->ops->msi_init ||
of_property_present(np, "msi-parent") ||
of_property_present(np, "msi-map"));
/*
- * For the has_msi_ctrl case the default assignment is handled
+ * For the use_imsi_rx case the default assignment is handled
* in the dw_pcie_msi_host_init().
*/
- if (!pp->has_msi_ctrl && !pp->num_vectors) {
+ if (!pp->use_imsi_rx && !pp->num_vectors) {
pp->num_vectors = MSI_DEF_NUM_VECTORS;
} else if (pp->num_vectors > MAX_MSI_IRQS) {
dev_err(dev, "Invalid number of vectors\n");
@@ -602,7 +612,7 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
ret = pp->ops->msi_init(pp);
if (ret < 0)
goto err_deinit_host;
- } else if (pp->has_msi_ctrl) {
+ } else if (pp->use_imsi_rx) {
ret = dw_pcie_msi_host_init(pp);
if (ret < 0)
goto err_deinit_host;
@@ -620,14 +630,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
goto err_free_msi;
- if (pp->ecam_enabled) {
- ret = dw_pcie_config_ecam_iatu(pp);
- if (ret) {
- dev_err(dev, "Failed to configure iATU in ECAM mode\n");
- goto err_free_msi;
- }
- }
-
/*
* Allocate the resource for MSG TLP before programming the iATU
* outbound window in dw_pcie_setup_rc(). Since the allocation depends
@@ -655,13 +657,12 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
}
/*
- * Note: Skip the link up delay only when a Link Up IRQ is present.
- * If there is no Link Up IRQ, we should not bypass the delay
- * because that would require users to manually rescan for devices.
+ * Only fail on timeout error. Other errors indicate the device may
+ * become available later, so continue without failing.
*/
- if (!pp->use_linkup_irq)
- /* Ignore errors, the link may come up later */
- dw_pcie_wait_for_link(pci);
+ ret = dw_pcie_wait_for_link(pci);
+ if (ret == -ETIMEDOUT)
+ goto err_stop_link;
ret = pci_host_probe(bridge);
if (ret)
@@ -681,7 +682,7 @@ err_remove_edma:
dw_pcie_edma_remove(pci);
err_free_msi:
- if (pp->has_msi_ctrl)
+ if (pp->use_imsi_rx)
dw_pcie_free_msi(pp);
err_deinit_host:
@@ -709,7 +710,7 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp)
dw_pcie_edma_remove(pci);
- if (pp->has_msi_ctrl)
+ if (pp->use_imsi_rx)
dw_pcie_free_msi(pp);
if (pp->ops->deinit)
@@ -846,19 +847,10 @@ static void __iomem *dw_pcie_ecam_conf_map_bus(struct pci_bus *bus, unsigned int
return pci->dbi_base + where;
}
-static int dw_pcie_op_assert_perst(struct pci_bus *bus, bool assert)
-{
- struct dw_pcie_rp *pp = bus->sysdata;
- struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
-
- return dw_pcie_assert_perst(pci, assert);
-}
-
static struct pci_ops dw_pcie_ops = {
.map_bus = dw_pcie_own_conf_map_bus,
.read = pci_generic_config_read,
.write = pci_generic_config_write,
- .assert_perst = dw_pcie_op_assert_perst,
};
static struct pci_ops dw_pcie_ecam_ops = {
@@ -872,9 +864,10 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct dw_pcie_ob_atu_cfg atu = { 0 };
struct resource_entry *entry;
+ int ob_iatu_index;
+ int ib_iatu_index;
int i, ret;
- /* Note the very first outbound ATU is used for CFG IOs */
if (!pci->num_ob_windows) {
dev_err(pci->dev, "No outbound iATU found\n");
return -EINVAL;
@@ -890,37 +883,74 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
for (i = 0; i < pci->num_ib_windows; i++)
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i);
- i = 0;
+ /*
+ * NOTE: For outbound address translation, outbound iATU at index 0 is
+ * reserved for CFG IOs (dw_pcie_other_conf_map_bus()), thus start at
+ * index 1.
+ *
+ * If using ECAM, outbound iATU at index 0 and index 1 is reserved for
+ * CFG IOs.
+ */
+ if (pp->ecam_enabled) {
+ ob_iatu_index = 2;
+ ret = dw_pcie_config_ecam_iatu(pp);
+ if (ret) {
+ dev_err(pci->dev, "Failed to configure iATU in ECAM mode\n");
+ return ret;
+ }
+ } else {
+ ob_iatu_index = 1;
+ }
+
resource_list_for_each_entry(entry, &pp->bridge->windows) {
+ resource_size_t res_size;
+
if (resource_type(entry->res) != IORESOURCE_MEM)
continue;
- if (pci->num_ob_windows <= ++i)
- break;
-
- atu.index = i;
atu.type = PCIE_ATU_TYPE_MEM;
atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset;
atu.pci_addr = entry->res->start - entry->offset;
/* Adjust iATU size if MSG TLP region was allocated before */
if (pp->msg_res && pp->msg_res->parent == entry->res)
- atu.size = resource_size(entry->res) -
+ res_size = resource_size(entry->res) -
resource_size(pp->msg_res);
else
- atu.size = resource_size(entry->res);
+ res_size = resource_size(entry->res);
+
+ while (res_size > 0) {
+ /*
+ * Return failure if we run out of windows in the
+ * middle. Otherwise, we would end up only partially
+ * mapping a single resource.
+ */
+ if (ob_iatu_index >= pci->num_ob_windows) {
+ dev_err(pci->dev, "Cannot add outbound window for region: %pr\n",
+ entry->res);
+ return -ENOMEM;
+ }
- ret = dw_pcie_prog_outbound_atu(pci, &atu);
- if (ret) {
- dev_err(pci->dev, "Failed to set MEM range %pr\n",
- entry->res);
- return ret;
+ atu.index = ob_iatu_index;
+ atu.size = MIN(pci->region_limit + 1, res_size);
+
+ ret = dw_pcie_prog_outbound_atu(pci, &atu);
+ if (ret) {
+ dev_err(pci->dev, "Failed to set MEM range %pr\n",
+ entry->res);
+ return ret;
+ }
+
+ ob_iatu_index++;
+ atu.parent_bus_addr += atu.size;
+ atu.pci_addr += atu.size;
+ res_size -= atu.size;
}
}
if (pp->io_size) {
- if (pci->num_ob_windows > ++i) {
- atu.index = i;
+ if (ob_iatu_index < pci->num_ob_windows) {
+ atu.index = ob_iatu_index;
atu.type = PCIE_ATU_TYPE_IO;
atu.parent_bus_addr = pp->io_base - pci->parent_bus_offset;
atu.pci_addr = pp->io_bus_addr;
@@ -932,40 +962,71 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
entry->res);
return ret;
}
+ ob_iatu_index++;
} else {
+ /*
+ * If there are not enough outbound windows to give I/O
+ * space its own iATU, the outbound iATU at index 0 will
+ * be shared between I/O space and CFG IOs, by
+ * temporarily reconfiguring the iATU to CFG space, in
+ * order to do a CFG IO, and then immediately restoring
+ * it to I/O space. This is only implemented when using
+ * dw_pcie_other_conf_map_bus(), which is not the case
+ * when using ECAM.
+ */
+ if (pp->ecam_enabled) {
+ dev_err(pci->dev, "Cannot add outbound window for I/O\n");
+ return -ENOMEM;
+ }
pp->cfg0_io_shared = true;
}
}
- if (pci->num_ob_windows <= i)
- dev_warn(pci->dev, "Ranges exceed outbound iATU size (%d)\n",
- pci->num_ob_windows);
-
- pp->msg_atu_index = i;
+ if (pp->use_atu_msg) {
+ if (ob_iatu_index >= pci->num_ob_windows) {
+ dev_err(pci->dev, "Cannot add outbound window for MSG TLP\n");
+ return -ENOMEM;
+ }
+ pp->msg_atu_index = ob_iatu_index++;
+ }
- i = 0;
+ ib_iatu_index = 0;
resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) {
+ resource_size_t res_start, res_size, window_size;
+
if (resource_type(entry->res) != IORESOURCE_MEM)
continue;
- if (pci->num_ib_windows <= i)
- break;
+ res_size = resource_size(entry->res);
+ res_start = entry->res->start;
+ while (res_size > 0) {
+ /*
+ * Return failure if we run out of windows in the
+ * middle. Otherwise, we would end up only partially
+ * mapping a single resource.
+ */
+ if (ib_iatu_index >= pci->num_ib_windows) {
+ dev_err(pci->dev, "Cannot add inbound window for region: %pr\n",
+ entry->res);
+ return -ENOMEM;
+ }
- ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM,
- entry->res->start,
- entry->res->start - entry->offset,
- resource_size(entry->res));
- if (ret) {
- dev_err(pci->dev, "Failed to set DMA range %pr\n",
- entry->res);
- return ret;
+ window_size = MIN(pci->region_limit + 1, res_size);
+ ret = dw_pcie_prog_inbound_atu(pci, ib_iatu_index,
+ PCIE_ATU_TYPE_MEM, res_start,
+ res_start - entry->offset, window_size);
+ if (ret) {
+ dev_err(pci->dev, "Failed to set DMA range %pr\n",
+ entry->res);
+ return ret;
+ }
+
+ ib_iatu_index++;
+ res_start += window_size;
+ res_size -= window_size;
}
}
- if (pci->num_ib_windows <= i)
- dev_warn(pci->dev, "Dma-ranges exceed inbound iATU size (%u)\n",
- pci->num_ib_windows);
-
return 0;
}
@@ -1087,7 +1148,7 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
* the platform uses its own address translation component rather than
* ATU, so we should not program the ATU here.
*/
- if (pp->bridge->child_ops == &dw_child_pcie_ops) {
+ if (pp->bridge->child_ops == &dw_child_pcie_ops || pp->ecam_enabled) {
ret = dw_pcie_iatu_setup(pp);
if (ret)
return ret;
@@ -1104,6 +1165,17 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
dw_pcie_dbi_ro_wr_dis(pci);
+ /*
+ * The iMSI-RX module does not support receiving MSI or MSI-X generated
+ * by the Root Port. If iMSI-RX is used as the MSI controller, remove
+ * the MSI and MSI-X capabilities of the Root Port to allow the drivers
+ * to fall back to INTx instead.
+ */
+ if (pp->use_imsi_rx) {
+ dw_pcie_remove_capability(pci, PCI_CAP_ID_MSI);
+ dw_pcie_remove_capability(pci, PCI_CAP_ID_MSIX);
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(dw_pcie_setup_rc);
@@ -1147,8 +1219,11 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
int dw_pcie_suspend_noirq(struct dw_pcie *pci)
{
u8 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+ int ret = 0;
u32 val;
- int ret;
+
+ if (!dw_pcie_link_up(pci))
+ goto stop_link;
/*
* If L1SS is supported, then do not put the link into L2 as some
@@ -1165,6 +1240,16 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci)
return ret;
}
+ /*
+ * Some SoCs do not support reading the LTSSM register after
+ * PME_Turn_Off broadcast. For those SoCs, skip waiting for L2/L3 Ready
+ * state and wait 10ms as recommended in PCIe spec r6.0, sec 5.3.3.2.1.
+ */
+ if (pci->pp.skip_l23_ready) {
+ mdelay(PCIE_PME_TO_L2_TIMEOUT_US/1000);
+ goto stop_link;
+ }
+
ret = read_poll_timeout(dw_pcie_get_ltssm, val,
val == DW_PCIE_LTSSM_L2_IDLE ||
val <= DW_PCIE_LTSSM_DETECT_WAIT,
@@ -1183,6 +1268,7 @@ int dw_pcie_suspend_noirq(struct dw_pcie *pci)
*/
udelay(1);
+stop_link:
dw_pcie_stop_link(pci);
if (pci->pp.ops->deinit)
pci->pp.ops->deinit(&pci->pp);
@@ -1220,6 +1306,9 @@ int dw_pcie_resume_noirq(struct dw_pcie *pci)
if (ret)
return ret;
+ if (pci->pp.ops->post_init)
+ pci->pp.ops->post_init(&pci->pp);
+
return ret;
}
EXPORT_SYMBOL_GPL(dw_pcie_resume_noirq);
diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c
index 12f41886c65d..8530746ec5cb 100644
--- a/drivers/pci/controller/dwc/pcie-designware-plat.c
+++ b/drivers/pci/controller/dwc/pcie-designware-plat.c
@@ -61,6 +61,7 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features dw_plat_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.msix_capable = true,
};
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 75fc8b767fcc..5741c09dde7f 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -226,16 +226,70 @@ void dw_pcie_version_detect(struct dw_pcie *pci)
u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap)
{
return PCI_FIND_NEXT_CAP(dw_pcie_read_cfg, PCI_CAPABILITY_LIST, cap,
- pci);
+ NULL, pci);
}
EXPORT_SYMBOL_GPL(dw_pcie_find_capability);
u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap)
{
- return PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, pci);
+ return PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, NULL, pci);
}
EXPORT_SYMBOL_GPL(dw_pcie_find_ext_capability);
+void dw_pcie_remove_capability(struct dw_pcie *pci, u8 cap)
+{
+ u8 cap_pos, pre_pos, next_pos;
+ u16 reg;
+
+ cap_pos = PCI_FIND_NEXT_CAP(dw_pcie_read_cfg, PCI_CAPABILITY_LIST, cap,
+ &pre_pos, pci);
+ if (!cap_pos)
+ return;
+
+ reg = dw_pcie_readw_dbi(pci, cap_pos);
+ next_pos = (reg & 0xff00) >> 8;
+
+ dw_pcie_dbi_ro_wr_en(pci);
+ if (pre_pos == PCI_CAPABILITY_LIST)
+ dw_pcie_writeb_dbi(pci, PCI_CAPABILITY_LIST, next_pos);
+ else
+ dw_pcie_writeb_dbi(pci, pre_pos + 1, next_pos);
+ dw_pcie_dbi_ro_wr_dis(pci);
+}
+EXPORT_SYMBOL_GPL(dw_pcie_remove_capability);
+
+void dw_pcie_remove_ext_capability(struct dw_pcie *pci, u8 cap)
+{
+ int cap_pos, next_pos, pre_pos;
+ u32 pre_header, header;
+
+ cap_pos = PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, &pre_pos, pci);
+ if (!cap_pos)
+ return;
+
+ header = dw_pcie_readl_dbi(pci, cap_pos);
+
+ /*
+ * If the first cap at offset PCI_CFG_SPACE_SIZE is removed,
+ * only set its capid to zero as it cannot be skipped.
+ */
+ if (cap_pos == PCI_CFG_SPACE_SIZE) {
+ dw_pcie_dbi_ro_wr_en(pci);
+ dw_pcie_writel_dbi(pci, cap_pos, header & 0xffff0000);
+ dw_pcie_dbi_ro_wr_dis(pci);
+ return;
+ }
+
+ pre_header = dw_pcie_readl_dbi(pci, pre_pos);
+ next_pos = PCI_EXT_CAP_NEXT(header);
+
+ dw_pcie_dbi_ro_wr_en(pci);
+ dw_pcie_writel_dbi(pci, pre_pos,
+ (pre_header & 0xfffff) | (next_pos << 20));
+ dw_pcie_dbi_ro_wr_dis(pci);
+}
+EXPORT_SYMBOL_GPL(dw_pcie_remove_ext_capability);
+
static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id,
u16 vsec_id)
{
@@ -246,7 +300,7 @@ static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id,
return 0;
while ((vsec = PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, vsec,
- PCI_EXT_CAP_ID_VNDR, pci))) {
+ PCI_EXT_CAP_ID_VNDR, NULL, pci))) {
header = dw_pcie_readl_dbi(pci, vsec + PCI_VNDR_HEADER);
if (PCI_VNDR_HEADER_ID(header) == vsec_id)
return vsec;
@@ -478,6 +532,9 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
u32 retries, val;
u64 limit_addr;
+ if (atu->index >= pci->num_ob_windows)
+ return -ENOSPC;
+
limit_addr = parent_bus_addr + atu->size - 1;
if ((limit_addr & ~pci->region_limit) != (parent_bus_addr & ~pci->region_limit) ||
@@ -551,6 +608,9 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
u64 limit_addr = pci_addr + size - 1;
u32 retries, val;
+ if (index >= pci->num_ib_windows)
+ return -ENOSPC;
+
if ((limit_addr & ~pci->region_limit) != (pci_addr & ~pci->region_limit) ||
!IS_ALIGNED(parent_bus_addr, pci->region_align) ||
!IS_ALIGNED(pci_addr, pci->region_align) || !size) {
@@ -639,9 +699,69 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index)
dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0);
}
+const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm)
+{
+ const char *str;
+
+ switch (ltssm) {
+#define DW_PCIE_LTSSM_NAME(n) case n: str = #n; break
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_QUIET);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_ACT);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_ACTIVE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_COMPLIANCE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_POLL_CONFIG);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_PRE_DETECT_QUIET);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DETECT_WAIT);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_START);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LINKWD_ACEPT);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_WAI);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_LANENUM_ACEPT);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_COMPLETE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_CFG_IDLE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_LOCK);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_SPEED);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_RCVRCFG);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_IDLE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L0S);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L123_SEND_EIDLE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_IDLE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_IDLE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L2_WAKE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_ENTRY);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED_IDLE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_DISABLED);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ENTRY);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_ACTIVE);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_LPBK_EXIT_TIMEOUT);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET_ENTRY);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_HOT_RESET);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ0);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ1);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ2);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_RCVRY_EQ3);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_1);
+ DW_PCIE_LTSSM_NAME(DW_PCIE_LTSSM_L1_2);
+ default:
+ str = "DW_PCIE_LTSSM_UNKNOWN";
+ break;
+ }
+
+ return str + strlen("DW_PCIE_LTSSM_");
+}
+
+/**
+ * dw_pcie_wait_for_link - Wait for the PCIe link to be up
+ * @pci: DWC instance
+ *
+ * Returns: 0 if link is up, -ENODEV if device is not found, -EIO if the device
+ * is found but not active and -ETIMEDOUT if the link fails to come up for other
+ * reasons.
+ */
int dw_pcie_wait_for_link(struct dw_pcie *pci)
{
- u32 offset, val;
+ u32 offset, val, ltssm;
int retries;
/* Check if the link is up or not */
@@ -653,7 +773,29 @@ int dw_pcie_wait_for_link(struct dw_pcie *pci)
}
if (retries >= PCIE_LINK_WAIT_MAX_RETRIES) {
- dev_info(pci->dev, "Phy link never came up\n");
+ /*
+ * If the link is in Detect.Quiet or Detect.Active state, it
+ * indicates that no device is detected.
+ */
+ ltssm = dw_pcie_get_ltssm(pci);
+ if (ltssm == DW_PCIE_LTSSM_DETECT_QUIET ||
+ ltssm == DW_PCIE_LTSSM_DETECT_ACT) {
+ dev_info(pci->dev, "Device not found\n");
+ return -ENODEV;
+
+ /*
+ * If the link is in POLL.{Active/Compliance} state, then the
+ * device is found to be connected to the bus, but it is not
+ * active i.e., the device firmware might not yet initialized.
+ */
+ } else if (ltssm == DW_PCIE_LTSSM_POLL_ACTIVE ||
+ ltssm == DW_PCIE_LTSSM_POLL_COMPLIANCE) {
+ dev_info(pci->dev, "Device found, but not active\n");
+ return -EIO;
+ }
+
+ dev_err(pci->dev, "Link failed to come up. LTSSM: %s\n",
+ dw_pcie_ltssm_status_string(ltssm));
return -ETIMEDOUT;
}
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 403f6cfe8191..ae6389dd9caa 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -305,6 +305,10 @@
/* Default eDMA LLP memory size */
#define DMA_LLP_MEM_SIZE PAGE_SIZE
+/* Common struct pci_epc_feature bits among DWC EP glue drivers */
+#define DWC_EPC_COMMON_FEATURES .dynamic_inbound_mapping = true, \
+ .subrange_mapping = true
+
struct dw_pcie;
struct dw_pcie_rp;
struct dw_pcie_ep;
@@ -388,6 +392,10 @@ enum dw_pcie_ltssm {
DW_PCIE_LTSSM_RCVRY_EQ2 = 0x22,
DW_PCIE_LTSSM_RCVRY_EQ3 = 0x23,
+ /* Vendor glue drivers provide pseudo L1 substates from get_ltssm() */
+ DW_PCIE_LTSSM_L1_1 = 0x141,
+ DW_PCIE_LTSSM_L1_2 = 0x142,
+
DW_PCIE_LTSSM_UNKNOWN = 0xFFFFFFFF,
};
@@ -412,7 +420,7 @@ struct dw_pcie_host_ops {
};
struct dw_pcie_rp {
- bool has_msi_ctrl:1;
+ bool use_imsi_rx:1;
bool cfg0_io_shared:1;
u64 cfg0_base;
void __iomem *va_cfg0_base;
@@ -434,11 +442,11 @@ struct dw_pcie_rp {
bool use_atu_msg;
int msg_atu_index;
struct resource *msg_res;
- bool use_linkup_irq;
struct pci_eq_presets presets;
struct pci_config_window *cfg;
bool ecam_enabled;
bool native_ecam;
+ bool skip_l23_ready;
};
struct dw_pcie_ep_ops {
@@ -463,6 +471,12 @@ struct dw_pcie_ep_func {
u8 func_no;
u8 msi_cap; /* MSI capability offset */
u8 msix_cap; /* MSI-X capability offset */
+ u8 bar_to_atu[PCI_STD_NUM_BARS];
+ struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS];
+
+ /* Only for Address Match Mode inbound iATU */
+ u32 *ib_atu_indexes[PCI_STD_NUM_BARS];
+ unsigned int num_ib_atu_indexes[PCI_STD_NUM_BARS];
};
struct dw_pcie_ep {
@@ -472,13 +486,16 @@ struct dw_pcie_ep {
phys_addr_t phys_base;
size_t addr_size;
size_t page_size;
- u8 bar_to_atu[PCI_STD_NUM_BARS];
phys_addr_t *outbound_addr;
unsigned long *ib_window_map;
unsigned long *ob_window_map;
void __iomem *msi_mem;
phys_addr_t msi_mem_phys;
- struct pci_epf_bar *epf_bar[PCI_STD_NUM_BARS];
+
+ /* MSI outbound iATU state */
+ bool msi_iatu_mapped;
+ u64 msi_msg_addr;
+ size_t msi_map_size;
};
struct dw_pcie_ops {
@@ -493,7 +510,6 @@ struct dw_pcie_ops {
enum dw_pcie_ltssm (*get_ltssm)(struct dw_pcie *pcie);
int (*start_link)(struct dw_pcie *pcie);
void (*stop_link)(struct dw_pcie *pcie);
- int (*assert_perst)(struct dw_pcie *pcie, bool assert);
};
struct debugfs_info {
@@ -562,6 +578,8 @@ void dw_pcie_version_detect(struct dw_pcie *pci);
u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap);
u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap);
+void dw_pcie_remove_capability(struct dw_pcie *pci, u8 cap);
+void dw_pcie_remove_ext_capability(struct dw_pcie *pci, u8 cap);
u16 dw_pcie_find_rasdes_capability(struct dw_pcie *pci);
u16 dw_pcie_find_ptm_capability(struct dw_pcie *pci);
@@ -798,14 +816,6 @@ static inline void dw_pcie_stop_link(struct dw_pcie *pci)
pci->ops->stop_link(pci);
}
-static inline int dw_pcie_assert_perst(struct dw_pcie *pci, bool assert)
-{
- if (pci->ops && pci->ops->assert_perst)
- return pci->ops->assert_perst(pci, assert);
-
- return 0;
-}
-
static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)
{
u32 val;
@@ -818,6 +828,8 @@ static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)
return (enum dw_pcie_ltssm)FIELD_GET(PORT_LOGIC_LTSSM_STATE_MASK, val);
}
+const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm);
+
#ifdef CONFIG_PCIE_DW_HOST
int dw_pcie_suspend_noirq(struct dw_pcie *pci);
int dw_pcie_resume_noirq(struct dw_pcie *pci);
@@ -896,7 +908,6 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no,
u16 interrupt_num);
void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar);
-int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci, u8 prev_cap, u8 cap);
struct dw_pcie_ep_func *
dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no);
#else
@@ -954,12 +965,6 @@ static inline void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar)
{
}
-static inline int dw_pcie_ep_hide_ext_capability(struct dw_pcie *pci,
- u8 prev_cap, u8 cap)
-{
- return 0;
-}
-
static inline struct dw_pcie_ep_func *
dw_pcie_ep_get_func_from_ep(struct dw_pcie_ep *ep, u8 func_no)
{
diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index f8605fe61a41..5b17da63151d 100644
--- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -68,6 +68,11 @@
#define PCIE_CLKREQ_NOT_READY FIELD_PREP_WM16(BIT(0), 0)
#define PCIE_CLKREQ_PULL_DOWN FIELD_PREP_WM16(GENMASK(13, 12), 1)
+/* RASDES TBA information */
+#define PCIE_CLIENT_CDM_RASDES_TBA_INFO_CMN 0x154
+#define PCIE_CLIENT_CDM_RASDES_TBA_L1_1 BIT(4)
+#define PCIE_CLIENT_CDM_RASDES_TBA_L1_2 BIT(5)
+
/* Hot Reset Control Register */
#define PCIE_CLIENT_HOT_RESET_CTRL 0x180
#define PCIE_LTSSM_APP_DLY2_EN BIT(1)
@@ -80,6 +85,8 @@
#define PCIE_LINKUP_MASK GENMASK(17, 16)
#define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0)
+#define PCIE_TYPE0_HDR_DBI2_OFFSET 0x100000
+
struct rockchip_pcie {
struct dw_pcie pci;
void __iomem *apb_base;
@@ -181,11 +188,26 @@ static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip)
return 0;
}
-static u32 rockchip_pcie_get_ltssm(struct rockchip_pcie *rockchip)
+static u32 rockchip_pcie_get_ltssm_reg(struct rockchip_pcie *rockchip)
{
return rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_LTSSM_STATUS);
}
+static enum dw_pcie_ltssm rockchip_pcie_get_ltssm(struct dw_pcie *pci)
+{
+ struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
+ u32 val = rockchip_pcie_readl_apb(rockchip,
+ PCIE_CLIENT_CDM_RASDES_TBA_INFO_CMN);
+
+ if (val & PCIE_CLIENT_CDM_RASDES_TBA_L1_1)
+ return DW_PCIE_LTSSM_L1_1;
+
+ if (val & PCIE_CLIENT_CDM_RASDES_TBA_L1_2)
+ return DW_PCIE_LTSSM_L1_2;
+
+ return rockchip_pcie_get_ltssm_reg(rockchip) & PCIE_LTSSM_STATUS_MASK;
+}
+
static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip)
{
rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM,
@@ -201,7 +223,7 @@ static void rockchip_pcie_disable_ltssm(struct rockchip_pcie *rockchip)
static bool rockchip_pcie_link_up(struct dw_pcie *pci)
{
struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
- u32 val = rockchip_pcie_get_ltssm(rockchip);
+ u32 val = rockchip_pcie_get_ltssm_reg(rockchip);
return FIELD_GET(PCIE_LINKUP_MASK, val) == PCIE_LINKUP;
}
@@ -292,6 +314,8 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
if (irq < 0)
return irq;
+ pci->dbi_base2 = pci->dbi_base + PCIE_TYPE0_HDR_DBI2_OFFSET;
+
ret = rockchip_pcie_init_irq_domain(rockchip);
if (ret < 0)
dev_err(dev, "failed to init irq domain\n");
@@ -302,6 +326,10 @@ static int rockchip_pcie_host_init(struct dw_pcie_rp *pp)
rockchip_pcie_configure_l1ss(pci);
rockchip_pcie_enable_l0s(pci);
+ /* Disable Root Ports BAR0 and BAR1 as they report bogus size */
+ dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_0, 0x0);
+ dw_pcie_writel_dbi2(pci, PCI_BASE_ADDRESS_1, 0x0);
+
return 0;
}
@@ -327,9 +355,7 @@ static void rockchip_pcie_ep_hide_broken_ats_cap_rk3588(struct dw_pcie_ep *ep)
if (!of_device_is_compatible(dev->of_node, "rockchip,rk3588-pcie-ep"))
return;
- if (dw_pcie_ep_hide_ext_capability(pci, PCI_EXT_CAP_ID_SECPCI,
- PCI_EXT_CAP_ID_ATS))
- dev_err(dev, "failed to hide ATS capability\n");
+ dw_pcie_remove_ext_capability(pci, PCI_EXT_CAP_ID_ATS);
}
static void rockchip_pcie_ep_init(struct dw_pcie_ep *ep)
@@ -364,6 +390,7 @@ static int rockchip_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
.msix_capable = true,
@@ -384,6 +411,7 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = {
* BARs) would be overwritten, resulting in (all other BARs) no longer working.
*/
static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
.msix_capable = true,
@@ -485,36 +513,9 @@ static const struct dw_pcie_ops dw_pcie_ops = {
.link_up = rockchip_pcie_link_up,
.start_link = rockchip_pcie_start_link,
.stop_link = rockchip_pcie_stop_link,
+ .get_ltssm = rockchip_pcie_get_ltssm,
};
-static irqreturn_t rockchip_pcie_rc_sys_irq_thread(int irq, void *arg)
-{
- struct rockchip_pcie *rockchip = arg;
- struct dw_pcie *pci = &rockchip->pci;
- struct dw_pcie_rp *pp = &pci->pp;
- struct device *dev = pci->dev;
- u32 reg;
-
- reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC);
- rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC);
-
- dev_dbg(dev, "PCIE_CLIENT_INTR_STATUS_MISC: %#x\n", reg);
- dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm(rockchip));
-
- if (reg & PCIE_RDLH_LINK_UP_CHGED) {
- if (rockchip_pcie_link_up(pci)) {
- msleep(PCIE_RESET_CONFIG_WAIT_MS);
- dev_dbg(dev, "Received Link up event. Starting enumeration!\n");
- /* Rescan the bus to enumerate endpoint devices */
- pci_lock_rescan_remove();
- pci_rescan_bus(pp->bridge->bus);
- pci_unlock_rescan_remove();
- }
- }
-
- return IRQ_HANDLED;
-}
-
static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg)
{
struct rockchip_pcie *rockchip = arg;
@@ -526,7 +527,7 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg)
rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC);
dev_dbg(dev, "PCIE_CLIENT_INTR_STATUS_MISC: %#x\n", reg);
- dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm(rockchip));
+ dev_dbg(dev, "LTSSM_STATUS: %#x\n", rockchip_pcie_get_ltssm_reg(rockchip));
if (reg & PCIE_LINK_REQ_RST_NOT_INT) {
dev_dbg(dev, "hot reset or link-down reset\n");
@@ -547,29 +548,14 @@ static irqreturn_t rockchip_pcie_ep_sys_irq_thread(int irq, void *arg)
return IRQ_HANDLED;
}
-static int rockchip_pcie_configure_rc(struct platform_device *pdev,
- struct rockchip_pcie *rockchip)
+static int rockchip_pcie_configure_rc(struct rockchip_pcie *rockchip)
{
- struct device *dev = &pdev->dev;
struct dw_pcie_rp *pp;
- int irq, ret;
u32 val;
if (!IS_ENABLED(CONFIG_PCIE_ROCKCHIP_DW_HOST))
return -ENODEV;
- irq = platform_get_irq_byname(pdev, "sys");
- if (irq < 0)
- return irq;
-
- ret = devm_request_threaded_irq(dev, irq, NULL,
- rockchip_pcie_rc_sys_irq_thread,
- IRQF_ONESHOT, "pcie-sys-rc", rockchip);
- if (ret) {
- dev_err(dev, "failed to request PCIe sys IRQ\n");
- return ret;
- }
-
/* LTSSM enable control mode */
val = FIELD_PREP_WM16(PCIE_LTSSM_ENABLE_ENHANCE, 1);
rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
@@ -580,19 +566,8 @@ static int rockchip_pcie_configure_rc(struct platform_device *pdev,
pp = &rockchip->pci.pp;
pp->ops = &rockchip_pcie_host_ops;
- pp->use_linkup_irq = true;
-
- ret = dw_pcie_host_init(pp);
- if (ret) {
- dev_err(dev, "failed to initialize host\n");
- return ret;
- }
- /* unmask DLL up/down indicator */
- val = FIELD_PREP_WM16(PCIE_RDLH_LINK_UP_CHGED, 0);
- rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_INTR_MASK_MISC);
-
- return ret;
+ return dw_pcie_host_init(pp);
}
static int rockchip_pcie_configure_ep(struct platform_device *pdev,
@@ -711,7 +686,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
switch (data->mode) {
case DW_PCIE_RC_TYPE:
- ret = rockchip_pcie_configure_rc(pdev, rockchip);
+ ret = rockchip_pcie_configure_rc(rockchip);
if (ret)
goto deinit_clk;
break;
diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c
index 60e74ac782af..2666a9c3d67e 100644
--- a/drivers/pci/controller/dwc/pcie-keembay.c
+++ b/drivers/pci/controller/dwc/pcie-keembay.c
@@ -309,6 +309,7 @@ static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features keembay_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.msix_capable = true,
.bar[BAR_0] = { .only_64bit = true, },
diff --git a/drivers/pci/controller/dwc/pcie-nxp-s32g.c b/drivers/pci/controller/dwc/pcie-nxp-s32g.c
index 47745749f75c..b3ec38099fa3 100644
--- a/drivers/pci/controller/dwc/pcie-nxp-s32g.c
+++ b/drivers/pci/controller/dwc/pcie-nxp-s32g.c
@@ -282,12 +282,12 @@ static int s32g_pcie_parse_ports(struct device *dev, struct s32g_pcie *s32g_pp)
ret = s32g_pcie_parse_port(s32g_pp, of_port);
if (ret)
- goto err_port;
+ break;
}
-err_port:
- list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list)
- list_del(&port->list);
+ if (ret)
+ list_for_each_entry_safe(port, tmp, &s32g_pp->ports, list)
+ list_del(&port->list);
return ret;
}
diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c
index f1bc0ac81a92..18460f01b2c6 100644
--- a/drivers/pci/controller/dwc/pcie-qcom-ep.c
+++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c
@@ -168,11 +168,13 @@ enum qcom_pcie_ep_link_status {
* @hdma_support: HDMA support on this SoC
* @override_no_snoop: Override NO_SNOOP attribute in TLP to enable cache snooping
* @disable_mhi_ram_parity_check: Disable MHI RAM data parity error check
+ * @firmware_managed: Set if the controller is firmware managed
*/
struct qcom_pcie_ep_cfg {
bool hdma_support;
bool override_no_snoop;
bool disable_mhi_ram_parity_check;
+ bool firmware_managed;
};
/**
@@ -377,6 +379,14 @@ err_disable_clk:
static void qcom_pcie_disable_resources(struct qcom_pcie_ep *pcie_ep)
{
+ struct device *dev = pcie_ep->pci.dev;
+
+ pm_runtime_put(dev);
+
+ /* Skip resource disablement if controller is firmware-managed */
+ if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed)
+ return;
+
icc_set_bw(pcie_ep->icc_mem, 0, 0);
phy_power_off(pcie_ep->phy);
phy_exit(pcie_ep->phy);
@@ -390,12 +400,24 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
u32 val, offset;
int ret;
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to enable device: %d\n", ret);
+ return ret;
+ }
+
+ /* Skip resource enablement if controller is firmware-managed */
+ if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed)
+ goto skip_resources_enable;
+
ret = qcom_pcie_enable_resources(pcie_ep);
if (ret) {
dev_err(dev, "Failed to enable resources: %d\n", ret);
+ pm_runtime_put(dev);
return ret;
}
+skip_resources_enable:
/* Perform cleanup that requires refclk */
pci_epc_deinit_notify(pci->ep.epc);
dw_pcie_ep_cleanup(&pci->ep);
@@ -630,6 +652,17 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev,
return ret;
}
+ pcie_ep->reset = devm_gpiod_get(dev, "reset", GPIOD_IN);
+ if (IS_ERR(pcie_ep->reset))
+ return PTR_ERR(pcie_ep->reset);
+
+ pcie_ep->wake = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW);
+ if (IS_ERR(pcie_ep->wake))
+ return PTR_ERR(pcie_ep->wake);
+
+ if (pcie_ep->cfg && pcie_ep->cfg->firmware_managed)
+ return 0;
+
pcie_ep->num_clks = devm_clk_bulk_get_all(dev, &pcie_ep->clks);
if (pcie_ep->num_clks < 0) {
dev_err(dev, "Failed to get clocks\n");
@@ -640,14 +673,6 @@ static int qcom_pcie_ep_get_resources(struct platform_device *pdev,
if (IS_ERR(pcie_ep->core_reset))
return PTR_ERR(pcie_ep->core_reset);
- pcie_ep->reset = devm_gpiod_get(dev, "reset", GPIOD_IN);
- if (IS_ERR(pcie_ep->reset))
- return PTR_ERR(pcie_ep->reset);
-
- pcie_ep->wake = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW);
- if (IS_ERR(pcie_ep->wake))
- return PTR_ERR(pcie_ep->wake);
-
pcie_ep->phy = devm_phy_optional_get(dev, "pciephy");
if (IS_ERR(pcie_ep->phy))
ret = PTR_ERR(pcie_ep->phy);
@@ -820,6 +845,7 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep)
}
static const struct pci_epc_features qcom_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
.align = SZ_4K,
@@ -874,6 +900,12 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pcie_ep);
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
ret = qcom_pcie_ep_get_resources(pdev, pcie_ep);
if (ret)
return ret;
@@ -894,6 +926,12 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)
goto err_disable_irqs;
}
+ ret = pm_runtime_put_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to suspend device: %d\n", ret);
+ goto err_disable_irqs;
+ }
+
pcie_ep->debugfs = debugfs_create_dir(name, NULL);
qcom_pcie_ep_init_debugfs(pcie_ep);
@@ -930,7 +968,15 @@ static const struct qcom_pcie_ep_cfg cfg_1_34_0 = {
.disable_mhi_ram_parity_check = true,
};
+static const struct qcom_pcie_ep_cfg cfg_1_34_0_fw_managed = {
+ .hdma_support = true,
+ .override_no_snoop = true,
+ .disable_mhi_ram_parity_check = true,
+ .firmware_managed = true,
+};
+
static const struct of_device_id qcom_pcie_ep_match[] = {
+ { .compatible = "qcom,sa8255p-pcie-ep", .data = &cfg_1_34_0_fw_managed},
{ .compatible = "qcom,sa8775p-pcie-ep", .data = &cfg_1_34_0},
{ .compatible = "qcom,sdx55-pcie-ep", },
{ .compatible = "qcom,sm8450-pcie-ep", },
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index 5a318487b2b3..67a16af69ddc 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -24,6 +24,7 @@
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pci-ecam.h>
+#include <linux/pci-pwrctrl.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
@@ -55,9 +56,6 @@
#define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8
#define PARF_Q2A_FLUSH 0x1ac
#define PARF_LTSSM 0x1b0
-#define PARF_INT_ALL_STATUS 0x224
-#define PARF_INT_ALL_CLEAR 0x228
-#define PARF_INT_ALL_MASK 0x22c
#define PARF_SID_OFFSET 0x234
#define PARF_BDF_TRANSLATE_CFG 0x24c
#define PARF_DBI_BASE_ADDR_V2 0x350
@@ -134,10 +132,6 @@
/* PARF_LTSSM register fields */
#define LTSSM_EN BIT(8)
-/* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */
-#define PARF_INT_ALL_LINK_UP BIT(13)
-#define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23)
-
/* PARF_NO_SNOOP_OVERRIDE register fields */
#define WR_NO_SNOOP_OVERRIDE_EN BIT(1)
#define RD_NO_SNOOP_OVERRIDE_EN BIT(3)
@@ -267,10 +261,15 @@ struct qcom_pcie_cfg {
bool no_l0s;
};
+struct qcom_pcie_perst {
+ struct list_head list;
+ struct gpio_desc *desc;
+};
+
struct qcom_pcie_port {
struct list_head list;
- struct gpio_desc *reset;
struct phy *phy;
+ struct list_head perst;
};
struct qcom_pcie {
@@ -289,27 +288,30 @@ struct qcom_pcie {
#define to_qcom_pcie(x) dev_get_drvdata((x)->dev)
-static void qcom_perst_assert(struct qcom_pcie *pcie, bool assert)
+static void __qcom_pcie_perst_assert(struct qcom_pcie *pcie, bool assert)
{
+ struct qcom_pcie_perst *perst;
struct qcom_pcie_port *port;
int val = assert ? 1 : 0;
- list_for_each_entry(port, &pcie->ports, list)
- gpiod_set_value_cansleep(port->reset, val);
+ list_for_each_entry(port, &pcie->ports, list) {
+ list_for_each_entry(perst, &port->perst, list)
+ gpiod_set_value_cansleep(perst->desc, val);
+ }
usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500);
}
-static void qcom_ep_reset_assert(struct qcom_pcie *pcie)
+static void qcom_pcie_perst_assert(struct qcom_pcie *pcie)
{
- qcom_perst_assert(pcie, true);
+ __qcom_pcie_perst_assert(pcie, true);
}
-static void qcom_ep_reset_deassert(struct qcom_pcie *pcie)
+static void qcom_pcie_perst_deassert(struct qcom_pcie *pcie)
{
- /* Ensure that PERST has been asserted for at least 100 ms */
+ /* Ensure that PERST# has been asserted for at least 100 ms */
msleep(PCIE_T_PVPERL_MS);
- qcom_perst_assert(pcie, false);
+ __qcom_pcie_perst_assert(pcie, false);
}
static int qcom_pcie_start_link(struct dw_pcie *pci)
@@ -641,18 +643,6 @@ static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie)
return 0;
}
-static int qcom_pcie_assert_perst(struct dw_pcie *pci, bool assert)
-{
- struct qcom_pcie *pcie = to_qcom_pcie(pci);
-
- if (assert)
- qcom_ep_reset_assert(pcie);
- else
- qcom_ep_reset_deassert(pcie);
-
- return 0;
-}
-
static void qcom_pcie_2_3_2_ltssm_enable(struct qcom_pcie *pcie)
{
u32 val;
@@ -1299,7 +1289,7 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
struct qcom_pcie *pcie = to_qcom_pcie(pci);
int ret;
- qcom_ep_reset_assert(pcie);
+ qcom_pcie_perst_assert(pcie);
ret = pcie->cfg->ops->init(pcie);
if (ret)
@@ -1309,15 +1299,25 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
if (ret)
goto err_deinit;
+ ret = pci_pwrctrl_create_devices(pci->dev);
+ if (ret)
+ goto err_disable_phy;
+
+ ret = pci_pwrctrl_power_on_devices(pci->dev);
+ if (ret)
+ goto err_pwrctrl_destroy;
+
if (pcie->cfg->ops->post_init) {
ret = pcie->cfg->ops->post_init(pcie);
if (ret)
- goto err_disable_phy;
+ goto err_pwrctrl_power_off;
}
qcom_pcie_clear_aspm_l0s(pcie->pci);
+ dw_pcie_remove_capability(pcie->pci, PCI_CAP_ID_MSIX);
+ dw_pcie_remove_ext_capability(pcie->pci, PCI_EXT_CAP_ID_DPC);
- qcom_ep_reset_deassert(pcie);
+ qcom_pcie_perst_deassert(pcie);
if (pcie->cfg->ops->config_sid) {
ret = pcie->cfg->ops->config_sid(pcie);
@@ -1328,7 +1328,12 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
return 0;
err_assert_reset:
- qcom_ep_reset_assert(pcie);
+ qcom_pcie_perst_assert(pcie);
+err_pwrctrl_power_off:
+ pci_pwrctrl_power_off_devices(pci->dev);
+err_pwrctrl_destroy:
+ if (ret != -EPROBE_DEFER)
+ pci_pwrctrl_destroy_devices(pci->dev);
err_disable_phy:
qcom_pcie_phy_power_off(pcie);
err_deinit:
@@ -1342,7 +1347,13 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp)
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct qcom_pcie *pcie = to_qcom_pcie(pci);
- qcom_ep_reset_assert(pcie);
+ qcom_pcie_perst_assert(pcie);
+
+ /*
+ * No need to destroy pwrctrl devices as this function only gets called
+ * during system suspend as of now.
+ */
+ pci_pwrctrl_power_off_devices(pci->dev);
qcom_pcie_phy_power_off(pcie);
pcie->cfg->ops->deinit(pcie);
}
@@ -1496,7 +1507,6 @@ static const struct qcom_pcie_cfg cfg_fw_managed = {
static const struct dw_pcie_ops dw_pcie_ops = {
.link_up = qcom_pcie_link_up,
.start_link = qcom_pcie_start_link,
- .assert_perst = qcom_pcie_assert_perst,
};
static int qcom_pcie_icc_init(struct qcom_pcie *pcie)
@@ -1635,37 +1645,11 @@ static void qcom_pcie_init_debugfs(struct qcom_pcie *pcie)
qcom_pcie_link_transition_count);
}
-static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data)
-{
- struct qcom_pcie *pcie = data;
- struct dw_pcie_rp *pp = &pcie->pci->pp;
- struct device *dev = pcie->pci->dev;
- u32 status = readl_relaxed(pcie->parf + PARF_INT_ALL_STATUS);
-
- writel_relaxed(status, pcie->parf + PARF_INT_ALL_CLEAR);
-
- if (FIELD_GET(PARF_INT_ALL_LINK_UP, status)) {
- msleep(PCIE_RESET_CONFIG_WAIT_MS);
- dev_dbg(dev, "Received Link up event. Starting enumeration!\n");
- /* Rescan the bus to enumerate endpoint devices */
- pci_lock_rescan_remove();
- pci_rescan_bus(pp->bridge->bus);
- pci_unlock_rescan_remove();
-
- qcom_pcie_icc_opp_update(pcie);
- } else {
- dev_WARN_ONCE(dev, 1, "Received unknown event. INT_STATUS: 0x%08x\n",
- status);
- }
-
- return IRQ_HANDLED;
-}
-
static void qcom_pci_free_msi(void *ptr)
{
struct dw_pcie_rp *pp = (struct dw_pcie_rp *)ptr;
- if (pp && pp->has_msi_ctrl)
+ if (pp && pp->use_imsi_rx)
dw_pcie_free_msi(pp);
}
@@ -1689,7 +1673,7 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg)
if (ret)
return ret;
- pp->has_msi_ctrl = true;
+ pp->use_imsi_rx = true;
dw_pcie_msi_init(pp);
return devm_add_action_or_reset(dev, qcom_pci_free_msi, pp);
@@ -1704,18 +1688,58 @@ static const struct pci_ecam_ops pci_qcom_ecam_ops = {
}
};
-static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node)
+/* Parse PERST# from all nodes in depth first manner starting from @np */
+static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
+ struct qcom_pcie_port *port,
+ struct device_node *np)
{
struct device *dev = pcie->pci->dev;
- struct qcom_pcie_port *port;
+ struct qcom_pcie_perst *perst;
struct gpio_desc *reset;
- struct phy *phy;
int ret;
- reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(node),
- "reset", GPIOD_OUT_HIGH, "PERST#");
- if (IS_ERR(reset))
+ if (!of_find_property(np, "reset-gpios", NULL))
+ goto parse_child_node;
+
+ reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(np), "reset",
+ GPIOD_OUT_HIGH, "PERST#");
+ if (IS_ERR(reset)) {
+ /*
+ * FIXME: GPIOLIB currently supports exclusive GPIO access only.
+ * Non exclusive access is broken. But shared PERST# requires
+ * non-exclusive access. So once GPIOLIB properly supports it,
+ * implement it here.
+ */
+ if (PTR_ERR(reset) == -EBUSY)
+ dev_err(dev, "Shared PERST# is not supported\n");
+
return PTR_ERR(reset);
+ }
+
+ perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
+ if (!perst)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&perst->list);
+ perst->desc = reset;
+ list_add_tail(&perst->list, &port->perst);
+
+parse_child_node:
+ for_each_available_child_of_node_scoped(np, child) {
+ ret = qcom_pcie_parse_perst(pcie, port, child);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node)
+{
+ struct device *dev = pcie->pci->dev;
+ struct qcom_pcie_port *port;
+ struct phy *phy;
+ int ret;
phy = devm_of_phy_get(dev, node, NULL);
if (IS_ERR(phy))
@@ -1729,7 +1753,12 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node
if (ret)
return ret;
- port->reset = reset;
+ INIT_LIST_HEAD(&port->perst);
+
+ ret = qcom_pcie_parse_perst(pcie, port, node);
+ if (ret)
+ return ret;
+
port->phy = phy;
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);
@@ -1739,9 +1768,10 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node
static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
{
+ struct qcom_pcie_perst *perst, *tmp_perst;
+ struct qcom_pcie_port *port, *tmp_port;
struct device *dev = pcie->pci->dev;
- struct qcom_pcie_port *port, *tmp;
- int ret = -ENOENT;
+ int ret = -ENODEV;
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
if (!of_node_is_type(of_port, "pci"))
@@ -1754,7 +1784,9 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie)
return ret;
err_port_del:
- list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) {
+ list_for_each_entry_safe(perst, tmp_perst, &port->perst, list)
+ list_del(&perst->list);
phy_exit(port->phy);
list_del(&port->list);
}
@@ -1765,6 +1797,7 @@ err_port_del:
static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
{
struct device *dev = pcie->pci->dev;
+ struct qcom_pcie_perst *perst;
struct qcom_pcie_port *port;
struct gpio_desc *reset;
struct phy *phy;
@@ -1786,27 +1819,35 @@ static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie)
if (!port)
return -ENOMEM;
- port->reset = reset;
+ perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
+ if (!perst)
+ return -ENOMEM;
+
port->phy = phy;
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);
+ perst->desc = reset;
+ INIT_LIST_HEAD(&port->perst);
+ INIT_LIST_HEAD(&perst->list);
+ list_add_tail(&perst->list, &port->perst);
+
return 0;
}
static int qcom_pcie_probe(struct platform_device *pdev)
{
+ struct qcom_pcie_perst *perst, *tmp_perst;
+ struct qcom_pcie_port *port, *tmp_port;
const struct qcom_pcie_cfg *pcie_cfg;
unsigned long max_freq = ULONG_MAX;
- struct qcom_pcie_port *port, *tmp;
struct device *dev = &pdev->dev;
struct dev_pm_opp *opp;
struct qcom_pcie *pcie;
struct dw_pcie_rp *pp;
struct resource *res;
struct dw_pcie *pci;
- int ret, irq;
- char *name;
+ int ret;
pcie_cfg = of_device_get_match_data(dev);
if (!pcie_cfg) {
@@ -1939,7 +1980,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
ret = qcom_pcie_parse_ports(pcie);
if (ret) {
- if (ret != -ENOENT) {
+ if (ret != -ENODEV) {
dev_err_probe(pci->dev, ret,
"Failed to parse Root Port: %d\n", ret);
goto err_pm_runtime_put;
@@ -1957,37 +1998,12 @@ static int qcom_pcie_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pcie);
- irq = platform_get_irq_byname_optional(pdev, "global");
- if (irq > 0)
- pp->use_linkup_irq = true;
-
ret = dw_pcie_host_init(pp);
if (ret) {
- dev_err(dev, "cannot initialize host\n");
+ dev_err_probe(dev, ret, "cannot initialize host\n");
goto err_phy_exit;
}
- name = devm_kasprintf(dev, GFP_KERNEL, "qcom_pcie_global_irq%d",
- pci_domain_nr(pp->bridge->bus));
- if (!name) {
- ret = -ENOMEM;
- goto err_host_deinit;
- }
-
- if (irq > 0) {
- ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
- qcom_pcie_global_irq_thread,
- IRQF_ONESHOT, name, pcie);
- if (ret) {
- dev_err_probe(&pdev->dev, ret,
- "Failed to request Global IRQ\n");
- goto err_host_deinit;
- }
-
- writel_relaxed(PARF_INT_ALL_LINK_UP | PARF_INT_MSI_DEV_0_7,
- pcie->parf + PARF_INT_ALL_MASK);
- }
-
qcom_pcie_icc_opp_update(pcie);
if (pcie->mhi)
@@ -1995,10 +2011,10 @@ static int qcom_pcie_probe(struct platform_device *pdev)
return 0;
-err_host_deinit:
- dw_pcie_host_deinit(pp);
err_phy_exit:
- list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ list_for_each_entry_safe(port, tmp_port, &pcie->ports, list) {
+ list_for_each_entry_safe(perst, tmp_perst, &port->perst, list)
+ list_del(&perst->list);
phy_exit(port->phy);
list_del(&port->list);
}
diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
index 80778917d2dd..a6912e85e4dd 100644
--- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c
+++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c
@@ -420,6 +420,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features rcar_gen4_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.bar[BAR_1] = { .type = BAR_RESERVED, },
.bar[BAR_3] = { .type = BAR_RESERVED, },
diff --git a/drivers/pci/controller/dwc/pcie-sophgo.c b/drivers/pci/controller/dwc/pcie-sophgo.c
index ad4baaa34ffa..044088898819 100644
--- a/drivers/pci/controller/dwc/pcie-sophgo.c
+++ b/drivers/pci/controller/dwc/pcie-sophgo.c
@@ -161,6 +161,22 @@ static void sophgo_pcie_msi_enable(struct dw_pcie_rp *pp)
raw_spin_unlock_irqrestore(&pp->lock, flags);
}
+static void sophgo_pcie_disable_l0s_l1(struct dw_pcie_rp *pp)
+{
+ struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+ u32 offset, val;
+
+ offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+
+ dw_pcie_dbi_ro_wr_en(pci);
+
+ val = dw_pcie_readl_dbi(pci, PCI_EXP_LNKCAP + offset);
+ val &= ~(PCI_EXP_LNKCAP_ASPM_L0S | PCI_EXP_LNKCAP_ASPM_L1);
+ dw_pcie_writel_dbi(pci, PCI_EXP_LNKCAP + offset, val);
+
+ dw_pcie_dbi_ro_wr_dis(pci);
+}
+
static int sophgo_pcie_host_init(struct dw_pcie_rp *pp)
{
int irq;
@@ -171,6 +187,8 @@ static int sophgo_pcie_host_init(struct dw_pcie_rp *pp)
irq_set_chained_handler_and_data(irq, sophgo_pcie_intx_handler, pp);
+ sophgo_pcie_disable_l0s_l1(pp);
+
sophgo_pcie_msi_enable(pp);
return 0;
diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c
index 2cecf32d2b0f..c1944b40ce02 100644
--- a/drivers/pci/controller/dwc/pcie-stm32-ep.c
+++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c
@@ -70,6 +70,7 @@ static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features stm32_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.msi_capable = true,
.align = SZ_64K,
};
diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c
index 0ddeef70726d..06571d806ab3 100644
--- a/drivers/pci/controller/dwc/pcie-tegra194.c
+++ b/drivers/pci/controller/dwc/pcie-tegra194.c
@@ -1988,6 +1988,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
}
static const struct pci_epc_features tegra_pcie_epc_features = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = true,
.msi_capable = true,
.bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M,
diff --git a/drivers/pci/controller/dwc/pcie-uniphier-ep.c b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
index d6e73811216e..d52753060970 100644
--- a/drivers/pci/controller/dwc/pcie-uniphier-ep.c
+++ b/drivers/pci/controller/dwc/pcie-uniphier-ep.c
@@ -420,6 +420,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_pro5_data = {
.init = uniphier_pcie_pro5_init_ep,
.wait = NULL,
.features = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = false,
.msi_capable = true,
.msix_capable = false,
@@ -438,6 +439,7 @@ static const struct uniphier_pcie_ep_soc_data uniphier_nx1_data = {
.init = uniphier_pcie_nx1_init_ep,
.wait = uniphier_pcie_nx1_wait_ep,
.features = {
+ DWC_EPC_COMMON_FEATURES,
.linkup_notifier = false,
.msi_capable = true,
.msix_capable = false,
diff --git a/drivers/pci/controller/pci-host-common.c b/drivers/pci/controller/pci-host-common.c
index c473e7c03bac..d6258c1cffe5 100644
--- a/drivers/pci/controller/pci-host-common.c
+++ b/drivers/pci/controller/pci-host-common.c
@@ -32,7 +32,7 @@ struct pci_config_window *pci_host_common_ecam_create(struct device *dev,
err = of_address_to_resource(dev->of_node, 0, &cfgres);
if (err) {
- dev_err(dev, "missing \"reg\" property\n");
+ dev_err(dev, "missing or malformed \"reg\" property\n");
return ERR_PTR(err);
}
diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c
index 942ddfca3bf6..512309763d1f 100644
--- a/drivers/pci/controller/pci-tegra.c
+++ b/drivers/pci/controller/pci-tegra.c
@@ -2545,12 +2545,6 @@ static const struct seq_operations tegra_pcie_ports_sops = {
DEFINE_SEQ_ATTRIBUTE(tegra_pcie_ports);
-static void tegra_pcie_debugfs_exit(struct tegra_pcie *pcie)
-{
- debugfs_remove_recursive(pcie->debugfs);
- pcie->debugfs = NULL;
-}
-
static void tegra_pcie_debugfs_init(struct tegra_pcie *pcie)
{
pcie->debugfs = debugfs_create_dir("pcie", NULL);
@@ -2624,29 +2618,6 @@ put_resources:
return err;
}
-static void tegra_pcie_remove(struct platform_device *pdev)
-{
- struct tegra_pcie *pcie = platform_get_drvdata(pdev);
- struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
- struct tegra_pcie_port *port, *tmp;
-
- if (IS_ENABLED(CONFIG_DEBUG_FS))
- tegra_pcie_debugfs_exit(pcie);
-
- pci_stop_root_bus(host->bus);
- pci_remove_root_bus(host->bus);
- pm_runtime_put_sync(pcie->dev);
- pm_runtime_disable(pcie->dev);
-
- if (IS_ENABLED(CONFIG_PCI_MSI))
- tegra_pcie_msi_teardown(pcie);
-
- tegra_pcie_put_resources(pcie);
-
- list_for_each_entry_safe(port, tmp, &pcie->ports, list)
- tegra_pcie_port_free(port);
-}
-
static int tegra_pcie_pm_suspend(struct device *dev)
{
struct tegra_pcie *pcie = dev_get_drvdata(dev);
@@ -2750,6 +2721,8 @@ static struct platform_driver tegra_pcie_driver = {
.pm = &tegra_pcie_pm_ops,
},
.probe = tegra_pcie_probe,
- .remove = tegra_pcie_remove,
};
-module_platform_driver(tegra_pcie_driver);
+builtin_platform_driver(tegra_pcie_driver);
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_DESCRIPTION("NVIDIA PCI host controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/controller/pcie-aspeed.c b/drivers/pci/controller/pcie-aspeed.c
new file mode 100644
index 000000000000..3e1a39d1e648
--- /dev/null
+++ b/drivers/pci/controller/pcie-aspeed.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 Aspeed Technology Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqchip/irq-msi-lib.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/phy/pcie.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include "../pci.h"
+
+#define MAX_MSI_HOST_IRQS 64
+#define ASPEED_RESET_RC_WAIT_MS 10
+
+/* AST2600 AHBC Registers */
+#define ASPEED_AHBC_KEY 0x00
+#define ASPEED_AHBC_UNLOCK_KEY 0xaeed1a03
+#define ASPEED_AHBC_UNLOCK 0x01
+#define ASPEED_AHBC_ADDR_MAPPING 0x8c
+#define ASPEED_PCIE_RC_MEMORY_EN BIT(5)
+
+/* AST2600 H2X Controller Registers */
+#define ASPEED_H2X_INT_STS 0x08
+#define ASPEED_PCIE_TX_IDLE_CLEAR BIT(0)
+#define ASPEED_PCIE_INTX_STS GENMASK(3, 0)
+#define ASPEED_H2X_HOST_RX_DESC_DATA 0x0c
+#define ASPEED_H2X_TX_DESC0 0x10
+#define ASPEED_H2X_TX_DESC1 0x14
+#define ASPEED_H2X_TX_DESC2 0x18
+#define ASPEED_H2X_TX_DESC3 0x1c
+#define ASPEED_H2X_TX_DESC_DATA 0x20
+#define ASPEED_H2X_STS 0x24
+#define ASPEED_PCIE_TX_IDLE BIT(31)
+#define ASPEED_PCIE_STATUS_OF_TX GENMASK(25, 24)
+#define ASPEED_PCIE_RC_H_TX_COMPLETE BIT(25)
+#define ASPEED_PCIE_TRIGGER_TX BIT(0)
+#define ASPEED_H2X_AHB_ADDR_CONFIG0 0x60
+#define ASPEED_AHB_REMAP_LO_ADDR(x) (x & GENMASK(15, 4))
+#define ASPEED_AHB_MASK_LO_ADDR(x) FIELD_PREP(GENMASK(31, 20), x)
+#define ASPEED_H2X_AHB_ADDR_CONFIG1 0x64
+#define ASPEED_AHB_REMAP_HI_ADDR(x) (x)
+#define ASPEED_H2X_AHB_ADDR_CONFIG2 0x68
+#define ASPEED_AHB_MASK_HI_ADDR(x) (x)
+#define ASPEED_H2X_DEV_CTRL 0xc0
+#define ASPEED_PCIE_RX_DMA_EN BIT(9)
+#define ASPEED_PCIE_RX_LINEAR BIT(8)
+#define ASPEED_PCIE_RX_MSI_SEL BIT(7)
+#define ASPEED_PCIE_RX_MSI_EN BIT(6)
+#define ASPEED_PCIE_UNLOCK_RX_BUFF BIT(4)
+#define ASPEED_PCIE_WAIT_RX_TLP_CLR BIT(2)
+#define ASPEED_PCIE_RC_RX_ENABLE BIT(1)
+#define ASPEED_PCIE_RC_ENABLE BIT(0)
+#define ASPEED_H2X_DEV_STS 0xc8
+#define ASPEED_PCIE_RC_RX_DONE_ISR BIT(4)
+#define ASPEED_H2X_DEV_RX_DESC_DATA 0xcc
+#define ASPEED_H2X_DEV_RX_DESC1 0xd4
+#define ASPEED_H2X_DEV_TX_TAG 0xfc
+#define ASPEED_RC_TLP_TX_TAG_NUM 0x28
+
+/* AST2700 H2X */
+#define ASPEED_H2X_CTRL 0x00
+#define ASPEED_H2X_BRIDGE_EN BIT(0)
+#define ASPEED_H2X_BRIDGE_DIRECT_EN BIT(1)
+#define ASPEED_H2X_CFGE_INT_STS 0x08
+#define ASPEED_CFGE_TX_IDLE BIT(0)
+#define ASPEED_CFGE_RX_BUSY BIT(1)
+#define ASPEED_H2X_CFGI_TLP 0x20
+#define ASPEED_CFGI_BYTE_EN_MASK GENMASK(19, 16)
+#define ASPEED_CFGI_BYTE_EN(x) \
+ FIELD_PREP(ASPEED_CFGI_BYTE_EN_MASK, (x))
+#define ASPEED_H2X_CFGI_WR_DATA 0x24
+#define ASPEED_CFGI_WRITE BIT(20)
+#define ASPEED_H2X_CFGI_CTRL 0x28
+#define ASPEED_CFGI_TLP_FIRE BIT(0)
+#define ASPEED_H2X_CFGI_RET_DATA 0x2c
+#define ASPEED_H2X_CFGE_TLP_1ST 0x30
+#define ASPEED_H2X_CFGE_TLP_NEXT 0x34
+#define ASPEED_H2X_CFGE_CTRL 0x38
+#define ASPEED_CFGE_TLP_FIRE BIT(0)
+#define ASPEED_H2X_CFGE_RET_DATA 0x3c
+#define ASPEED_H2X_REMAP_PREF_ADDR 0x70
+#define ASPEED_REMAP_PREF_ADDR_63_32(x) (x)
+#define ASPEED_H2X_REMAP_PCI_ADDR_HI 0x74
+#define ASPEED_REMAP_PCI_ADDR_63_32(x) (((x) >> 32) & GENMASK(31, 0))
+#define ASPEED_H2X_REMAP_PCI_ADDR_LO 0x78
+#define ASPEED_REMAP_PCI_ADDR_31_12(x) ((x) & GENMASK(31, 12))
+
+/* AST2700 SCU */
+#define ASPEED_SCU_60 0x60
+#define ASPEED_RC_E2M_PATH_EN BIT(0)
+#define ASPEED_RC_H2XS_PATH_EN BIT(16)
+#define ASPEED_RC_H2XD_PATH_EN BIT(17)
+#define ASPEED_RC_H2XX_PATH_EN BIT(18)
+#define ASPEED_RC_UPSTREAM_MEM_EN BIT(19)
+#define ASPEED_SCU_64 0x64
+#define ASPEED_RC0_DECODE_DMA_BASE(x) FIELD_PREP(GENMASK(7, 0), x)
+#define ASPEED_RC0_DECODE_DMA_LIMIT(x) FIELD_PREP(GENMASK(15, 8), x)
+#define ASPEED_RC1_DECODE_DMA_BASE(x) FIELD_PREP(GENMASK(23, 16), x)
+#define ASPEED_RC1_DECODE_DMA_LIMIT(x) FIELD_PREP(GENMASK(31, 24), x)
+#define ASPEED_SCU_70 0x70
+#define ASPEED_DISABLE_EP_FUNC 0
+
+/* Macro to combine Fmt and Type into the 8-bit field */
+#define ASPEED_TLP_FMT_TYPE(fmt, type) ((((fmt) & 0x7) << 5) | ((type) & 0x1f))
+#define ASPEED_TLP_COMMON_FIELDS GENMASK(31, 24)
+
+/* Completion status */
+#define CPL_STS(x) FIELD_GET(GENMASK(15, 13), (x))
+/* TLP configuration type 0 and type 1 */
+#define CFG0_READ_FMTTYPE \
+ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \
+ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \
+ PCIE_TLP_TYPE_CFG0_RD))
+#define CFG0_WRITE_FMTTYPE \
+ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \
+ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \
+ PCIE_TLP_TYPE_CFG0_WR))
+#define CFG1_READ_FMTTYPE \
+ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \
+ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_NO_DATA, \
+ PCIE_TLP_TYPE_CFG1_RD))
+#define CFG1_WRITE_FMTTYPE \
+ FIELD_PREP(ASPEED_TLP_COMMON_FIELDS, \
+ ASPEED_TLP_FMT_TYPE(PCIE_TLP_FMT_3DW_DATA, \
+ PCIE_TLP_TYPE_CFG1_WR))
+#define CFG_PAYLOAD_SIZE 0x01 /* 1 DWORD */
+#define TLP_HEADER_BYTE_EN(x, y) ((GENMASK((x) - 1, 0) << ((y) % 4)))
+#define TLP_GET_VALUE(x, y, z) \
+ (((x) >> ((((z) % 4)) * 8)) & GENMASK((8 * (y)) - 1, 0))
+#define TLP_SET_VALUE(x, y, z) \
+ ((((x) & GENMASK((8 * (y)) - 1, 0)) << ((((z) % 4)) * 8)))
+#define AST2600_TX_DESC1_VALUE 0x00002000
+#define AST2700_TX_DESC1_VALUE 0x00401000
+
+/**
+ * struct aspeed_pcie_port - PCIe port information
+ * @list: port list
+ * @pcie: pointer to PCIe host info
+ * @clk: pointer to the port clock gate
+ * @phy: pointer to PCIe PHY
+ * @perst: pointer to port reset control
+ * @slot: port slot
+ */
+struct aspeed_pcie_port {
+ struct list_head list;
+ struct aspeed_pcie *pcie;
+ struct clk *clk;
+ struct phy *phy;
+ struct reset_control *perst;
+ u32 slot;
+};
+
+/**
+ * struct aspeed_pcie - PCIe RC information
+ * @host: pointer to PCIe host bridge
+ * @dev: pointer to device structure
+ * @reg: PCIe host register base address
+ * @ahbc: pointer to AHHC register map
+ * @cfg: pointer to Aspeed PCIe configuration register map
+ * @platform: platform specific information
+ * @ports: list of PCIe ports
+ * @tx_tag: current TX tag for the port
+ * @root_bus_nr: bus number of the host bridge
+ * @h2xrst: pointer to H2X reset control
+ * @intx_domain: IRQ domain for INTx interrupts
+ * @msi_domain: IRQ domain for MSI interrupts
+ * @lock: mutex to protect MSI bitmap variable
+ * @msi_irq_in_use: bitmap to track used MSI host IRQs
+ * @clear_msi_twice: AST2700 workaround to clear MSI status twice
+ */
+struct aspeed_pcie {
+ struct pci_host_bridge *host;
+ struct device *dev;
+ void __iomem *reg;
+ struct regmap *ahbc;
+ struct regmap *cfg;
+ const struct aspeed_pcie_rc_platform *platform;
+ struct list_head ports;
+
+ u8 tx_tag;
+ u8 root_bus_nr;
+
+ struct reset_control *h2xrst;
+
+ struct irq_domain *intx_domain;
+ struct irq_domain *msi_domain;
+ struct mutex lock;
+ DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_HOST_IRQS);
+
+ bool clear_msi_twice; /* AST2700 workaround */
+};
+
+/**
+ * struct aspeed_pcie_rc_platform - Platform information
+ * @setup: initialization function
+ * @pcie_map_ranges: function to map PCIe address ranges
+ * @reg_intx_en: INTx enable register offset
+ * @reg_intx_sts: INTx status register offset
+ * @reg_msi_en: MSI enable register offset
+ * @reg_msi_sts: MSI enable register offset
+ * @msi_address: HW fixed MSI address
+ */
+struct aspeed_pcie_rc_platform {
+ int (*setup)(struct platform_device *pdev);
+ void (*pcie_map_ranges)(struct aspeed_pcie *pcie, u64 pci_addr);
+ int reg_intx_en;
+ int reg_intx_sts;
+ int reg_msi_en;
+ int reg_msi_sts;
+ u32 msi_address;
+};
+
+static void aspeed_pcie_intx_irq_ack(struct irq_data *d)
+{
+ struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d);
+ int intx_en = pcie->platform->reg_intx_en;
+ u32 en;
+
+ en = readl(pcie->reg + intx_en);
+ en |= BIT(d->hwirq);
+ writel(en, pcie->reg + intx_en);
+}
+
+static void aspeed_pcie_intx_irq_mask(struct irq_data *d)
+{
+ struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d);
+ int intx_en = pcie->platform->reg_intx_en;
+ u32 en;
+
+ en = readl(pcie->reg + intx_en);
+ en &= ~BIT(d->hwirq);
+ writel(en, pcie->reg + intx_en);
+}
+
+static void aspeed_pcie_intx_irq_unmask(struct irq_data *d)
+{
+ struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(d);
+ int intx_en = pcie->platform->reg_intx_en;
+ u32 en;
+
+ en = readl(pcie->reg + intx_en);
+ en |= BIT(d->hwirq);
+ writel(en, pcie->reg + intx_en);
+}
+
+static struct irq_chip aspeed_intx_irq_chip = {
+ .name = "INTx",
+ .irq_ack = aspeed_pcie_intx_irq_ack,
+ .irq_mask = aspeed_pcie_intx_irq_mask,
+ .irq_unmask = aspeed_pcie_intx_irq_unmask,
+};
+
+static int aspeed_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+ irq_hw_number_t hwirq)
+{
+ irq_set_chip_and_handler(irq, &aspeed_intx_irq_chip, handle_level_irq);
+ irq_set_chip_data(irq, domain->host_data);
+ irq_set_status_flags(irq, IRQ_LEVEL);
+
+ return 0;
+}
+
+static const struct irq_domain_ops aspeed_intx_domain_ops = {
+ .map = aspeed_pcie_intx_map,
+};
+
+static irqreturn_t aspeed_pcie_intr_handler(int irq, void *dev_id)
+{
+ struct aspeed_pcie *pcie = dev_id;
+ const struct aspeed_pcie_rc_platform *platform = pcie->platform;
+ unsigned long status;
+ unsigned long intx;
+ u32 bit;
+ int i;
+
+ intx = FIELD_GET(ASPEED_PCIE_INTX_STS,
+ readl(pcie->reg + platform->reg_intx_sts));
+ for_each_set_bit(bit, &intx, PCI_NUM_INTX)
+ generic_handle_domain_irq(pcie->intx_domain, bit);
+
+ for (i = 0; i < 2; i++) {
+ int msi_sts_reg = platform->reg_msi_sts + (i * 4);
+
+ status = readl(pcie->reg + msi_sts_reg);
+ writel(status, pcie->reg + msi_sts_reg);
+
+ /*
+ * AST2700 workaround:
+ * The MSI status needs to clear one more time.
+ */
+ if (pcie->clear_msi_twice)
+ writel(status, pcie->reg + msi_sts_reg);
+
+ for_each_set_bit(bit, &status, 32) {
+ bit += (i * 32);
+ generic_handle_domain_irq(pcie->msi_domain, bit);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static u32 aspeed_pcie_get_bdf_offset(struct pci_bus *bus, unsigned int devfn,
+ int where)
+{
+ return ((bus->number) << 24) | (PCI_SLOT(devfn) << 19) |
+ (PCI_FUNC(devfn) << 16) | (where & ~3);
+}
+
+static int aspeed_ast2600_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val, u32 fmt_type,
+ bool write)
+{
+ struct aspeed_pcie *pcie = bus->sysdata;
+ u32 bdf_offset, cfg_val, isr;
+ int ret;
+
+ bdf_offset = aspeed_pcie_get_bdf_offset(bus, devfn, where);
+
+ /* Driver may set unlock RX buffer before triggering next TX config */
+ cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_CTRL);
+ writel(ASPEED_PCIE_UNLOCK_RX_BUFF | cfg_val,
+ pcie->reg + ASPEED_H2X_DEV_CTRL);
+
+ cfg_val = fmt_type | CFG_PAYLOAD_SIZE;
+ writel(cfg_val, pcie->reg + ASPEED_H2X_TX_DESC0);
+
+ cfg_val = AST2600_TX_DESC1_VALUE |
+ FIELD_PREP(GENMASK(11, 8), pcie->tx_tag) |
+ TLP_HEADER_BYTE_EN(size, where);
+ writel(cfg_val, pcie->reg + ASPEED_H2X_TX_DESC1);
+
+ writel(bdf_offset, pcie->reg + ASPEED_H2X_TX_DESC2);
+ writel(0, pcie->reg + ASPEED_H2X_TX_DESC3);
+ if (write)
+ writel(TLP_SET_VALUE(*val, size, where),
+ pcie->reg + ASPEED_H2X_TX_DESC_DATA);
+
+ cfg_val = readl(pcie->reg + ASPEED_H2X_STS);
+ cfg_val |= ASPEED_PCIE_TRIGGER_TX;
+ writel(cfg_val, pcie->reg + ASPEED_H2X_STS);
+
+ ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_STS, cfg_val,
+ (cfg_val & ASPEED_PCIE_TX_IDLE), 0, 50);
+ if (ret) {
+ dev_err(pcie->dev,
+ "%02x:%02x.%d CR tx timeout sts: 0x%08x\n",
+ bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), cfg_val);
+ ret = PCIBIOS_SET_FAILED;
+ PCI_SET_ERROR_RESPONSE(val);
+ goto out;
+ }
+
+ cfg_val = readl(pcie->reg + ASPEED_H2X_INT_STS);
+ cfg_val |= ASPEED_PCIE_TX_IDLE_CLEAR;
+ writel(cfg_val, pcie->reg + ASPEED_H2X_INT_STS);
+
+ cfg_val = readl(pcie->reg + ASPEED_H2X_STS);
+ switch (cfg_val & ASPEED_PCIE_STATUS_OF_TX) {
+ case ASPEED_PCIE_RC_H_TX_COMPLETE:
+ ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_DEV_STS, isr,
+ (isr & ASPEED_PCIE_RC_RX_DONE_ISR), 0,
+ 50);
+ if (ret) {
+ dev_err(pcie->dev,
+ "%02x:%02x.%d CR rx timeout sts: 0x%08x\n",
+ bus->number, PCI_SLOT(devfn),
+ PCI_FUNC(devfn), isr);
+ ret = PCIBIOS_SET_FAILED;
+ PCI_SET_ERROR_RESPONSE(val);
+ goto out;
+ }
+ if (!write) {
+ cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_RX_DESC1);
+ if (CPL_STS(cfg_val) != PCIE_CPL_STS_SUCCESS) {
+ ret = PCIBIOS_SET_FAILED;
+ PCI_SET_ERROR_RESPONSE(val);
+ goto out;
+ } else {
+ *val = readl(pcie->reg +
+ ASPEED_H2X_DEV_RX_DESC_DATA);
+ }
+ }
+ break;
+ case ASPEED_PCIE_STATUS_OF_TX:
+ ret = PCIBIOS_SET_FAILED;
+ PCI_SET_ERROR_RESPONSE(val);
+ goto out;
+ default:
+ *val = readl(pcie->reg + ASPEED_H2X_HOST_RX_DESC_DATA);
+ break;
+ }
+
+ cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_CTRL);
+ cfg_val |= ASPEED_PCIE_UNLOCK_RX_BUFF;
+ writel(cfg_val, pcie->reg + ASPEED_H2X_DEV_CTRL);
+
+ *val = TLP_GET_VALUE(*val, size, where);
+
+ ret = PCIBIOS_SUCCESSFUL;
+out:
+ cfg_val = readl(pcie->reg + ASPEED_H2X_DEV_STS);
+ writel(cfg_val, pcie->reg + ASPEED_H2X_DEV_STS);
+ pcie->tx_tag = (pcie->tx_tag + 1) % 0x8;
+ return ret;
+}
+
+static int aspeed_ast2600_rd_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ /*
+ * AST2600 has only one Root Port on the root bus.
+ */
+ if (PCI_SLOT(devfn) != 8)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ return aspeed_ast2600_conf(bus, devfn, where, size, val,
+ CFG0_READ_FMTTYPE, false);
+}
+
+static int aspeed_ast2600_child_rd_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ return aspeed_ast2600_conf(bus, devfn, where, size, val,
+ CFG1_READ_FMTTYPE, false);
+}
+
+static int aspeed_ast2600_wr_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ /*
+ * AST2600 has only one Root Port on the root bus.
+ */
+ if (PCI_SLOT(devfn) != 8)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ return aspeed_ast2600_conf(bus, devfn, where, size, &val,
+ CFG0_WRITE_FMTTYPE, true);
+}
+
+static int aspeed_ast2600_child_wr_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ return aspeed_ast2600_conf(bus, devfn, where, size, &val,
+ CFG1_WRITE_FMTTYPE, true);
+}
+
+static int aspeed_ast2700_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val, bool write)
+{
+ struct aspeed_pcie *pcie = bus->sysdata;
+ u32 cfg_val;
+
+ cfg_val = ASPEED_CFGI_BYTE_EN(TLP_HEADER_BYTE_EN(size, where)) |
+ (where & ~3);
+ if (write)
+ cfg_val |= ASPEED_CFGI_WRITE;
+ writel(cfg_val, pcie->reg + ASPEED_H2X_CFGI_TLP);
+
+ writel(TLP_SET_VALUE(*val, size, where),
+ pcie->reg + ASPEED_H2X_CFGI_WR_DATA);
+ writel(ASPEED_CFGI_TLP_FIRE, pcie->reg + ASPEED_H2X_CFGI_CTRL);
+ *val = readl(pcie->reg + ASPEED_H2X_CFGI_RET_DATA);
+ *val = TLP_GET_VALUE(*val, size, where);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int aspeed_ast2700_child_config(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val,
+ bool write)
+{
+ struct aspeed_pcie *pcie = bus->sysdata;
+ u32 bdf_offset, status, cfg_val;
+ int ret;
+
+ bdf_offset = aspeed_pcie_get_bdf_offset(bus, devfn, where);
+
+ cfg_val = CFG_PAYLOAD_SIZE;
+ if (write)
+ cfg_val |= (bus->number == (pcie->root_bus_nr + 1)) ?
+ CFG0_WRITE_FMTTYPE :
+ CFG1_WRITE_FMTTYPE;
+ else
+ cfg_val |= (bus->number == (pcie->root_bus_nr + 1)) ?
+ CFG0_READ_FMTTYPE :
+ CFG1_READ_FMTTYPE;
+ writel(cfg_val, pcie->reg + ASPEED_H2X_CFGE_TLP_1ST);
+
+ cfg_val = AST2700_TX_DESC1_VALUE |
+ FIELD_PREP(GENMASK(11, 8), pcie->tx_tag) |
+ TLP_HEADER_BYTE_EN(size, where);
+ writel(cfg_val, pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT);
+
+ writel(bdf_offset, pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT);
+ if (write)
+ writel(TLP_SET_VALUE(*val, size, where),
+ pcie->reg + ASPEED_H2X_CFGE_TLP_NEXT);
+ writel(ASPEED_CFGE_TX_IDLE | ASPEED_CFGE_RX_BUSY,
+ pcie->reg + ASPEED_H2X_CFGE_INT_STS);
+ writel(ASPEED_CFGE_TLP_FIRE, pcie->reg + ASPEED_H2X_CFGE_CTRL);
+
+ ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_CFGE_INT_STS, status,
+ (status & ASPEED_CFGE_TX_IDLE), 0, 50);
+ if (ret) {
+ dev_err(pcie->dev,
+ "%02x:%02x.%d CR tx timeout sts: 0x%08x\n",
+ bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), status);
+ ret = PCIBIOS_SET_FAILED;
+ PCI_SET_ERROR_RESPONSE(val);
+ goto out;
+ }
+
+ ret = readl_poll_timeout(pcie->reg + ASPEED_H2X_CFGE_INT_STS, status,
+ (status & ASPEED_CFGE_RX_BUSY), 0, 50);
+ if (ret) {
+ dev_err(pcie->dev,
+ "%02x:%02x.%d CR rx timeout sts: 0x%08x\n",
+ bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), status);
+ ret = PCIBIOS_SET_FAILED;
+ PCI_SET_ERROR_RESPONSE(val);
+ goto out;
+ }
+ *val = readl(pcie->reg + ASPEED_H2X_CFGE_RET_DATA);
+ *val = TLP_GET_VALUE(*val, size, where);
+
+ ret = PCIBIOS_SUCCESSFUL;
+out:
+ writel(status, pcie->reg + ASPEED_H2X_CFGE_INT_STS);
+ pcie->tx_tag = (pcie->tx_tag + 1) % 0xf;
+ return ret;
+}
+
+static int aspeed_ast2700_rd_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ /*
+ * AST2700 has only one Root Port on the root bus.
+ */
+ if (devfn != 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ return aspeed_ast2700_config(bus, devfn, where, size, val, false);
+}
+
+static int aspeed_ast2700_child_rd_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ return aspeed_ast2700_child_config(bus, devfn, where, size, val, false);
+}
+
+static int aspeed_ast2700_wr_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ /*
+ * AST2700 has only one Root Port on the root bus.
+ */
+ if (devfn != 0)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ return aspeed_ast2700_config(bus, devfn, where, size, &val, true);
+}
+
+static int aspeed_ast2700_child_wr_conf(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ return aspeed_ast2700_child_config(bus, devfn, where, size, &val, true);
+}
+
+static struct pci_ops aspeed_ast2600_pcie_ops = {
+ .read = aspeed_ast2600_rd_conf,
+ .write = aspeed_ast2600_wr_conf,
+};
+
+static struct pci_ops aspeed_ast2600_pcie_child_ops = {
+ .read = aspeed_ast2600_child_rd_conf,
+ .write = aspeed_ast2600_child_wr_conf,
+};
+
+static struct pci_ops aspeed_ast2700_pcie_ops = {
+ .read = aspeed_ast2700_rd_conf,
+ .write = aspeed_ast2700_wr_conf,
+};
+
+static struct pci_ops aspeed_ast2700_pcie_child_ops = {
+ .read = aspeed_ast2700_child_rd_conf,
+ .write = aspeed_ast2700_child_wr_conf,
+};
+
+static void aspeed_irq_compose_msi_msg(struct irq_data *data,
+ struct msi_msg *msg)
+{
+ struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(data);
+
+ msg->address_hi = 0;
+ msg->address_lo = pcie->platform->msi_address;
+ msg->data = data->hwirq;
+}
+
+static struct irq_chip aspeed_msi_bottom_irq_chip = {
+ .name = "ASPEED MSI",
+ .irq_compose_msi_msg = aspeed_irq_compose_msi_msg,
+};
+
+static int aspeed_irq_msi_domain_alloc(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs,
+ void *args)
+{
+ struct aspeed_pcie *pcie = domain->host_data;
+ int bit;
+ int i;
+
+ guard(mutex)(&pcie->lock);
+
+ bit = bitmap_find_free_region(pcie->msi_irq_in_use, MAX_MSI_HOST_IRQS,
+ get_count_order(nr_irqs));
+
+ if (bit < 0)
+ return -ENOSPC;
+
+ for (i = 0; i < nr_irqs; i++) {
+ irq_domain_set_info(domain, virq + i, bit + i,
+ &aspeed_msi_bottom_irq_chip,
+ domain->host_data, handle_simple_irq, NULL,
+ NULL);
+ }
+
+ return 0;
+}
+
+static void aspeed_irq_msi_domain_free(struct irq_domain *domain,
+ unsigned int virq, unsigned int nr_irqs)
+{
+ struct irq_data *data = irq_domain_get_irq_data(domain, virq);
+ struct aspeed_pcie *pcie = irq_data_get_irq_chip_data(data);
+
+ guard(mutex)(&pcie->lock);
+
+ bitmap_release_region(pcie->msi_irq_in_use, data->hwirq,
+ get_count_order(nr_irqs));
+}
+
+static const struct irq_domain_ops aspeed_msi_domain_ops = {
+ .alloc = aspeed_irq_msi_domain_alloc,
+ .free = aspeed_irq_msi_domain_free,
+};
+
+#define ASPEED_MSI_FLAGS_REQUIRED (MSI_FLAG_USE_DEF_DOM_OPS | \
+ MSI_FLAG_USE_DEF_CHIP_OPS | \
+ MSI_FLAG_NO_AFFINITY)
+
+#define ASPEED_MSI_FLAGS_SUPPORTED (MSI_GENERIC_FLAGS_MASK | \
+ MSI_FLAG_MULTI_PCI_MSI | \
+ MSI_FLAG_PCI_MSIX)
+
+static const struct msi_parent_ops aspeed_msi_parent_ops = {
+ .required_flags = ASPEED_MSI_FLAGS_REQUIRED,
+ .supported_flags = ASPEED_MSI_FLAGS_SUPPORTED,
+ .bus_select_token = DOMAIN_BUS_PCI_MSI,
+ .chip_flags = MSI_CHIP_FLAG_SET_ACK,
+ .prefix = "ASPEED-",
+ .init_dev_msi_info = msi_lib_init_dev_msi_info,
+};
+
+static int aspeed_pcie_msi_init(struct aspeed_pcie *pcie)
+{
+ writel(~0, pcie->reg + pcie->platform->reg_msi_en);
+ writel(~0, pcie->reg + pcie->platform->reg_msi_en + 0x04);
+ writel(~0, pcie->reg + pcie->platform->reg_msi_sts);
+ writel(~0, pcie->reg + pcie->platform->reg_msi_sts + 0x04);
+
+ struct irq_domain_info info = {
+ .fwnode = dev_fwnode(pcie->dev),
+ .ops = &aspeed_msi_domain_ops,
+ .host_data = pcie,
+ .size = MAX_MSI_HOST_IRQS,
+ };
+
+ pcie->msi_domain = msi_create_parent_irq_domain(&info,
+ &aspeed_msi_parent_ops);
+ if (!pcie->msi_domain)
+ return dev_err_probe(pcie->dev, -ENOMEM,
+ "failed to create MSI domain\n");
+
+ return 0;
+}
+
+static void aspeed_pcie_msi_free(struct aspeed_pcie *pcie)
+{
+ if (pcie->msi_domain) {
+ irq_domain_remove(pcie->msi_domain);
+ pcie->msi_domain = NULL;
+ }
+}
+
+static void aspeed_pcie_irq_domain_free(void *d)
+{
+ struct aspeed_pcie *pcie = d;
+
+ if (pcie->intx_domain) {
+ irq_domain_remove(pcie->intx_domain);
+ pcie->intx_domain = NULL;
+ }
+ aspeed_pcie_msi_free(pcie);
+}
+
+static int aspeed_pcie_init_irq_domain(struct aspeed_pcie *pcie)
+{
+ int ret;
+
+ pcie->intx_domain = irq_domain_add_linear(pcie->dev->of_node,
+ PCI_NUM_INTX,
+ &aspeed_intx_domain_ops,
+ pcie);
+ if (!pcie->intx_domain) {
+ ret = dev_err_probe(pcie->dev, -ENOMEM,
+ "failed to get INTx IRQ domain\n");
+ goto err;
+ }
+
+ writel(0, pcie->reg + pcie->platform->reg_intx_en);
+ writel(~0, pcie->reg + pcie->platform->reg_intx_sts);
+
+ ret = aspeed_pcie_msi_init(pcie);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ aspeed_pcie_irq_domain_free(pcie);
+ return ret;
+}
+
+static int aspeed_pcie_port_init(struct aspeed_pcie_port *port)
+{
+ struct aspeed_pcie *pcie = port->pcie;
+ struct device *dev = pcie->dev;
+ int ret;
+
+ ret = clk_prepare_enable(port->clk);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to set clock for slot (%d)\n",
+ port->slot);
+
+ ret = phy_init(port->phy);
+ if (ret)
+ 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)
+ 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);
+
+ return 0;
+}
+
+static void aspeed_host_reset(struct aspeed_pcie *pcie)
+{
+ reset_control_assert(pcie->h2xrst);
+ mdelay(ASPEED_RESET_RC_WAIT_MS);
+ reset_control_deassert(pcie->h2xrst);
+}
+
+static void aspeed_pcie_map_ranges(struct aspeed_pcie *pcie)
+{
+ struct pci_host_bridge *bridge = pcie->host;
+ struct resource_entry *window;
+
+ resource_list_for_each_entry(window, &bridge->windows) {
+ u64 pci_addr;
+
+ if (resource_type(window->res) != IORESOURCE_MEM)
+ continue;
+
+ pci_addr = window->res->start - window->offset;
+ pcie->platform->pcie_map_ranges(pcie, pci_addr);
+ break;
+ }
+}
+
+static void aspeed_ast2600_pcie_map_ranges(struct aspeed_pcie *pcie,
+ u64 pci_addr)
+{
+ u32 pci_addr_lo = pci_addr & GENMASK(31, 0);
+ u32 pci_addr_hi = (pci_addr >> 32) & GENMASK(31, 0);
+
+ pci_addr_lo >>= 16;
+ writel(ASPEED_AHB_REMAP_LO_ADDR(pci_addr_lo) |
+ ASPEED_AHB_MASK_LO_ADDR(0xe00),
+ pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG0);
+ writel(ASPEED_AHB_REMAP_HI_ADDR(pci_addr_hi),
+ pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG1);
+ writel(ASPEED_AHB_MASK_HI_ADDR(~0),
+ pcie->reg + ASPEED_H2X_AHB_ADDR_CONFIG2);
+}
+
+static int aspeed_ast2600_setup(struct platform_device *pdev)
+{
+ struct aspeed_pcie *pcie = platform_get_drvdata(pdev);
+ struct device *dev = pcie->dev;
+
+ pcie->ahbc = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "aspeed,ahbc");
+ if (IS_ERR(pcie->ahbc))
+ return dev_err_probe(dev, PTR_ERR(pcie->ahbc),
+ "failed to map ahbc base\n");
+
+ aspeed_host_reset(pcie);
+
+ regmap_write(pcie->ahbc, ASPEED_AHBC_KEY, ASPEED_AHBC_UNLOCK_KEY);
+ regmap_update_bits(pcie->ahbc, ASPEED_AHBC_ADDR_MAPPING,
+ ASPEED_PCIE_RC_MEMORY_EN, ASPEED_PCIE_RC_MEMORY_EN);
+ regmap_write(pcie->ahbc, ASPEED_AHBC_KEY, ASPEED_AHBC_UNLOCK);
+
+ writel(ASPEED_H2X_BRIDGE_EN, pcie->reg + ASPEED_H2X_CTRL);
+
+ writel(ASPEED_PCIE_RX_DMA_EN | ASPEED_PCIE_RX_LINEAR |
+ ASPEED_PCIE_RX_MSI_SEL | ASPEED_PCIE_RX_MSI_EN |
+ ASPEED_PCIE_WAIT_RX_TLP_CLR | ASPEED_PCIE_RC_RX_ENABLE |
+ ASPEED_PCIE_RC_ENABLE,
+ pcie->reg + ASPEED_H2X_DEV_CTRL);
+
+ writel(ASPEED_RC_TLP_TX_TAG_NUM, pcie->reg + ASPEED_H2X_DEV_TX_TAG);
+
+ pcie->host->ops = &aspeed_ast2600_pcie_ops;
+ pcie->host->child_ops = &aspeed_ast2600_pcie_child_ops;
+
+ return 0;
+}
+
+static void aspeed_ast2700_pcie_map_ranges(struct aspeed_pcie *pcie,
+ u64 pci_addr)
+{
+ writel(ASPEED_REMAP_PCI_ADDR_31_12(pci_addr),
+ pcie->reg + ASPEED_H2X_REMAP_PCI_ADDR_LO);
+ writel(ASPEED_REMAP_PCI_ADDR_63_32(pci_addr),
+ pcie->reg + ASPEED_H2X_REMAP_PCI_ADDR_HI);
+}
+
+static int aspeed_ast2700_setup(struct platform_device *pdev)
+{
+ struct aspeed_pcie *pcie = platform_get_drvdata(pdev);
+ struct device *dev = pcie->dev;
+
+ pcie->cfg = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "aspeed,pciecfg");
+ if (IS_ERR(pcie->cfg))
+ return dev_err_probe(dev, PTR_ERR(pcie->cfg),
+ "failed to map pciecfg base\n");
+
+ regmap_update_bits(pcie->cfg, ASPEED_SCU_60,
+ ASPEED_RC_E2M_PATH_EN | ASPEED_RC_H2XS_PATH_EN |
+ ASPEED_RC_H2XD_PATH_EN | ASPEED_RC_H2XX_PATH_EN |
+ ASPEED_RC_UPSTREAM_MEM_EN,
+ ASPEED_RC_E2M_PATH_EN | ASPEED_RC_H2XS_PATH_EN |
+ ASPEED_RC_H2XD_PATH_EN | ASPEED_RC_H2XX_PATH_EN |
+ ASPEED_RC_UPSTREAM_MEM_EN);
+ regmap_write(pcie->cfg, ASPEED_SCU_64,
+ ASPEED_RC0_DECODE_DMA_BASE(0) |
+ ASPEED_RC0_DECODE_DMA_LIMIT(0xff) |
+ ASPEED_RC1_DECODE_DMA_BASE(0) |
+ ASPEED_RC1_DECODE_DMA_LIMIT(0xff));
+ regmap_write(pcie->cfg, ASPEED_SCU_70, ASPEED_DISABLE_EP_FUNC);
+
+ aspeed_host_reset(pcie);
+
+ writel(0, pcie->reg + ASPEED_H2X_CTRL);
+ writel(ASPEED_H2X_BRIDGE_EN | ASPEED_H2X_BRIDGE_DIRECT_EN,
+ pcie->reg + ASPEED_H2X_CTRL);
+
+ /* Prepare for 64-bit BAR pref */
+ writel(ASPEED_REMAP_PREF_ADDR_63_32(0x3),
+ pcie->reg + ASPEED_H2X_REMAP_PREF_ADDR);
+
+ pcie->host->ops = &aspeed_ast2700_pcie_ops;
+ pcie->host->child_ops = &aspeed_ast2700_pcie_child_ops;
+ pcie->clear_msi_twice = true;
+
+ return 0;
+}
+
+static void aspeed_pcie_reset_release(void *d)
+{
+ struct reset_control *perst = d;
+
+ if (!perst)
+ return;
+
+ reset_control_put(perst);
+}
+
+static int aspeed_pcie_parse_port(struct aspeed_pcie *pcie,
+ struct device_node *node,
+ int slot)
+{
+ struct aspeed_pcie_port *port;
+ struct device *dev = pcie->dev;
+ int ret;
+
+ port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->clk = devm_get_clk_from_child(dev, node, NULL);
+ if (IS_ERR(port->clk))
+ return dev_err_probe(dev, PTR_ERR(port->clk),
+ "failed to get pcie%d clock\n", slot);
+
+ port->phy = devm_of_phy_get(dev, node, NULL);
+ if (IS_ERR(port->phy))
+ return dev_err_probe(dev, PTR_ERR(port->phy),
+ "failed to get phy pcie%d\n", slot);
+
+ port->perst = of_reset_control_get_exclusive(node, "perst");
+ if (IS_ERR(port->perst))
+ return dev_err_probe(dev, PTR_ERR(port->perst),
+ "failed to get pcie%d reset control\n",
+ slot);
+ ret = devm_add_action_or_reset(dev, aspeed_pcie_reset_release,
+ port->perst);
+ if (ret)
+ return ret;
+ reset_control_assert(port->perst);
+
+ port->slot = slot;
+ port->pcie = pcie;
+
+ INIT_LIST_HEAD(&port->list);
+ list_add_tail(&port->list, &pcie->ports);
+
+ ret = aspeed_pcie_port_init(port);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int aspeed_pcie_parse_dt(struct aspeed_pcie *pcie)
+{
+ struct device *dev = pcie->dev;
+ struct device_node *node = dev->of_node;
+ int ret;
+
+ for_each_available_child_of_node_scoped(node, child) {
+ int slot;
+ const char *type;
+
+ ret = of_property_read_string(child, "device_type", &type);
+ if (ret || strcmp(type, "pci"))
+ continue;
+
+ ret = of_pci_get_devfn(child);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to parse devfn\n");
+
+ slot = PCI_SLOT(ret);
+
+ ret = aspeed_pcie_parse_port(pcie, child, slot);
+ if (ret)
+ return ret;
+ }
+
+ if (list_empty(&pcie->ports))
+ return dev_err_probe(dev, -ENODEV,
+ "No PCIe port found in DT\n");
+
+ return 0;
+}
+
+static int aspeed_pcie_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pci_host_bridge *host;
+ struct aspeed_pcie *pcie;
+ struct resource_entry *entry;
+ const struct aspeed_pcie_rc_platform *md;
+ int irq, ret;
+
+ md = of_device_get_match_data(dev);
+ if (!md)
+ return -ENODEV;
+
+ host = devm_pci_alloc_host_bridge(dev, sizeof(*pcie));
+ if (!host)
+ return -ENOMEM;
+
+ pcie = pci_host_bridge_priv(host);
+ pcie->dev = dev;
+ pcie->tx_tag = 0;
+ platform_set_drvdata(pdev, pcie);
+
+ pcie->platform = md;
+ pcie->host = host;
+ INIT_LIST_HEAD(&pcie->ports);
+
+ /* Get root bus num for cfg command to decide tlp type 0 or type 1 */
+ entry = resource_list_first_type(&host->windows, IORESOURCE_BUS);
+ if (entry)
+ pcie->root_bus_nr = entry->res->start;
+
+ pcie->reg = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pcie->reg))
+ return PTR_ERR(pcie->reg);
+
+ pcie->h2xrst = devm_reset_control_get_exclusive(dev, "h2x");
+ if (IS_ERR(pcie->h2xrst))
+ return dev_err_probe(dev, PTR_ERR(pcie->h2xrst),
+ "failed to get h2x reset\n");
+
+ ret = devm_mutex_init(dev, &pcie->lock);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init mutex\n");
+
+ ret = pcie->platform->setup(pdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to setup PCIe RC\n");
+
+ aspeed_pcie_map_ranges(pcie);
+
+ ret = aspeed_pcie_parse_dt(pcie);
+ if (ret)
+ return ret;
+
+ host->sysdata = pcie;
+
+ ret = aspeed_pcie_init_irq_domain(pcie);
+ if (ret)
+ return ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_add_action_or_reset(dev, aspeed_pcie_irq_domain_free, pcie);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(dev, irq, aspeed_pcie_intr_handler, IRQF_SHARED,
+ dev_name(dev), pcie);
+ if (ret)
+ return ret;
+
+ return pci_host_probe(host);
+}
+
+static const struct aspeed_pcie_rc_platform pcie_rc_ast2600 = {
+ .setup = aspeed_ast2600_setup,
+ .pcie_map_ranges = aspeed_ast2600_pcie_map_ranges,
+ .reg_intx_en = 0xc4,
+ .reg_intx_sts = 0xc8,
+ .reg_msi_en = 0xe0,
+ .reg_msi_sts = 0xe8,
+ .msi_address = 0x1e77005c,
+};
+
+static const struct aspeed_pcie_rc_platform pcie_rc_ast2700 = {
+ .setup = aspeed_ast2700_setup,
+ .pcie_map_ranges = aspeed_ast2700_pcie_map_ranges,
+ .reg_intx_en = 0x40,
+ .reg_intx_sts = 0x48,
+ .reg_msi_en = 0x50,
+ .reg_msi_sts = 0x58,
+ .msi_address = 0x000000f0,
+};
+
+static const struct of_device_id aspeed_pcie_of_match[] = {
+ { .compatible = "aspeed,ast2600-pcie", .data = &pcie_rc_ast2600 },
+ { .compatible = "aspeed,ast2700-pcie", .data = &pcie_rc_ast2700 },
+ {}
+};
+
+static struct platform_driver aspeed_pcie_driver = {
+ .driver = {
+ .name = "aspeed-pcie",
+ .of_match_table = aspeed_pcie_of_match,
+ .suppress_bind_attrs = true,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = aspeed_pcie_probe,
+};
+
+builtin_platform_driver(aspeed_pcie_driver);
+
+MODULE_AUTHOR("Jacky Chou <jacky_chou@aspeedtech.com>");
+MODULE_DESCRIPTION("ASPEED PCIe Root Complex");
+MODULE_LICENSE("GPL");
diff --git a/drivers/pci/controller/pcie-mediatek.c b/drivers/pci/controller/pcie-mediatek.c
index 4b78b6528f9f..5defa5cc4c2b 100644
--- a/drivers/pci/controller/pcie-mediatek.c
+++ b/drivers/pci/controller/pcie-mediatek.c
@@ -585,8 +585,10 @@ static int mtk_pcie_init_irq_domain(struct mtk_pcie_port *port,
if (IS_ENABLED(CONFIG_PCI_MSI)) {
ret = mtk_pcie_allocate_msi_domains(port);
- if (ret)
+ if (ret) {
+ irq_domain_remove(port->irq_domain);
return ret;
+ }
}
return 0;
diff --git a/drivers/pci/controller/pcie-rzg3s-host.c b/drivers/pci/controller/pcie-rzg3s-host.c
index 83ec66a70823..2809112e6317 100644
--- a/drivers/pci/controller/pcie-rzg3s-host.c
+++ b/drivers/pci/controller/pcie-rzg3s-host.c
@@ -73,6 +73,7 @@
#define RZG3S_PCI_PINTRCVIE_INTX(i) BIT(i)
#define RZG3S_PCI_PINTRCVIE_MSI BIT(4)
+/* Register is R/W1C, it doesn't require locking. */
#define RZG3S_PCI_PINTRCVIS 0x114
#define RZG3S_PCI_PINTRCVIS_INTX(i) BIT(i)
#define RZG3S_PCI_PINTRCVIS_MSI BIT(4)
@@ -114,6 +115,8 @@
#define RZG3S_PCI_MSIRE_ENA BIT(0)
#define RZG3S_PCI_MSIRM(id) (0x608 + (id) * 0x10)
+
+/* Register is R/W1C, it doesn't require locking. */
#define RZG3S_PCI_MSIRS(id) (0x60c + (id) * 0x10)
#define RZG3S_PCI_AWBASEL(id) (0x1000 + (id) * 0x20)
@@ -439,28 +442,9 @@ static void __iomem *rzg3s_pcie_root_map_bus(struct pci_bus *bus,
return host->pcie + where;
}
-/* Serialized by 'pci_lock' */
-static int rzg3s_pcie_root_write(struct pci_bus *bus, unsigned int devfn,
- int where, int size, u32 val)
-{
- struct rzg3s_pcie_host *host = bus->sysdata;
- int ret;
-
- /* Enable access control to the CFGU */
- writel_relaxed(RZG3S_PCI_PERM_CFG_HWINIT_EN,
- host->axi + RZG3S_PCI_PERM);
-
- ret = pci_generic_config_write(bus, devfn, where, size, val);
-
- /* Disable access control to the CFGU */
- writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
-
- return ret;
-}
-
static struct pci_ops rzg3s_pcie_root_ops = {
.read = pci_generic_config_read,
- .write = rzg3s_pcie_root_write,
+ .write = pci_generic_config_write,
.map_bus = rzg3s_pcie_root_map_bus,
};
@@ -526,8 +510,6 @@ static void rzg3s_pcie_msi_irq_ack(struct irq_data *d)
u8 reg_bit = d->hwirq % RZG3S_PCI_MSI_INT_PER_REG;
u8 reg_id = d->hwirq / RZG3S_PCI_MSI_INT_PER_REG;
- guard(raw_spinlock_irqsave)(&host->hw_lock);
-
writel_relaxed(BIT(reg_bit), host->axi + RZG3S_PCI_MSIRS(reg_id));
}
@@ -859,8 +841,6 @@ static void rzg3s_pcie_intx_irq_ack(struct irq_data *d)
{
struct rzg3s_pcie_host *host = irq_data_get_irq_chip_data(d);
- guard(raw_spinlock_irqsave)(&host->hw_lock);
-
rzg3s_pcie_update_bits(host->axi, RZG3S_PCI_PINTRCVIS,
RZG3S_PCI_PINTRCVIS_INTX(d->hwirq),
RZG3S_PCI_PINTRCVIS_INTX(d->hwirq));
@@ -1065,14 +1045,14 @@ static int rzg3s_pcie_config_init(struct rzg3s_pcie_host *host)
writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00L);
writel_relaxed(0xffffffff, host->pcie + RZG3S_PCI_CFG_BARMSK00U);
+ /* Disable access control to the CFGU */
+ writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
+
/* Update bus info */
writeb_relaxed(primary_bus, host->pcie + PCI_PRIMARY_BUS);
writeb_relaxed(secondary_bus, host->pcie + PCI_SECONDARY_BUS);
writeb_relaxed(subordinate_bus, host->pcie + PCI_SUBORDINATE_BUS);
- /* Disable access control to the CFGU */
- writel_relaxed(0, host->axi + RZG3S_PCI_PERM);
-
return 0;
}
@@ -1162,7 +1142,8 @@ static int rzg3s_pcie_resets_prepare_and_get(struct rzg3s_pcie_host *host)
static int rzg3s_pcie_host_parse_port(struct rzg3s_pcie_host *host)
{
- struct device_node *of_port = of_get_next_child(host->dev->of_node, NULL);
+ struct device_node *of_port __free(device_node) =
+ of_get_next_child(host->dev->of_node, NULL);
struct rzg3s_pcie_port *port = &host->port;
int ret;
diff --git a/drivers/pci/controller/pcie-xilinx.c b/drivers/pci/controller/pcie-xilinx.c
index 937ea6ae1ac4..4aa139abac16 100644
--- a/drivers/pci/controller/pcie-xilinx.c
+++ b/drivers/pci/controller/pcie-xilinx.c
@@ -302,9 +302,10 @@ static int xilinx_allocate_msi_domains(struct xilinx_pcie *pcie)
return 0;
}
-static void xilinx_free_msi_domains(struct xilinx_pcie *pcie)
+static void xilinx_free_irq_domains(struct xilinx_pcie *pcie)
{
irq_domain_remove(pcie->msi_domain);
+ irq_domain_remove(pcie->leg_domain);
}
/* INTx Functions */
@@ -480,8 +481,10 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie *pcie)
phys_addr_t pa = ALIGN_DOWN(virt_to_phys(pcie), SZ_4K);
ret = xilinx_allocate_msi_domains(pcie);
- if (ret)
+ if (ret) {
+ irq_domain_remove(pcie->leg_domain);
return ret;
+ }
pcie_write(pcie, upper_32_bits(pa), XILINX_PCIE_REG_MSIBASE1);
pcie_write(pcie, lower_32_bits(pa), XILINX_PCIE_REG_MSIBASE2);
@@ -600,7 +603,7 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
err = pci_host_probe(bridge);
if (err)
- xilinx_free_msi_domains(pcie);
+ xilinx_free_irq_domains(pcie);
return err;
}
diff --git a/drivers/pci/controller/plda/pcie-starfive.c b/drivers/pci/controller/plda/pcie-starfive.c
index 3caf53c6c082..298036c3e7f9 100644
--- a/drivers/pci/controller/plda/pcie-starfive.c
+++ b/drivers/pci/controller/plda/pcie-starfive.c
@@ -55,7 +55,7 @@ struct starfive_jh7110_pcie {
struct reset_control *resets;
struct clk_bulk_data *clks;
struct regmap *reg_syscon;
- struct gpio_desc *power_gpio;
+ struct regulator *vpcie3v3;
struct gpio_desc *reset_gpio;
struct phy *phy;
@@ -153,11 +153,13 @@ static int starfive_pcie_parse_dt(struct starfive_jh7110_pcie *pcie,
return dev_err_probe(dev, PTR_ERR(pcie->reset_gpio),
"failed to get perst-gpio\n");
- pcie->power_gpio = devm_gpiod_get_optional(dev, "enable",
- GPIOD_OUT_LOW);
- if (IS_ERR(pcie->power_gpio))
- return dev_err_probe(dev, PTR_ERR(pcie->power_gpio),
- "failed to get power-gpio\n");
+ pcie->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
+ if (IS_ERR(pcie->vpcie3v3)) {
+ if (PTR_ERR(pcie->vpcie3v3) != -ENODEV)
+ return dev_err_probe(dev, PTR_ERR(pcie->vpcie3v3),
+ "failed to get vpcie3v3 regulator\n");
+ pcie->vpcie3v3 = NULL;
+ }
return 0;
}
@@ -270,8 +272,8 @@ static void starfive_pcie_host_deinit(struct plda_pcie_rp *plda)
container_of(plda, struct starfive_jh7110_pcie, plda);
starfive_pcie_clk_rst_deinit(pcie);
- if (pcie->power_gpio)
- gpiod_set_value_cansleep(pcie->power_gpio, 0);
+ if (pcie->vpcie3v3)
+ regulator_disable(pcie->vpcie3v3);
starfive_pcie_disable_phy(pcie);
}
@@ -304,8 +306,11 @@ static int starfive_pcie_host_init(struct plda_pcie_rp *plda)
if (ret)
return ret;
- if (pcie->power_gpio)
- gpiod_set_value_cansleep(pcie->power_gpio, 1);
+ if (pcie->vpcie3v3) {
+ ret = regulator_enable(pcie->vpcie3v3);
+ if (ret)
+ dev_err_probe(dev, ret, "failed to enable vpcie3v3 regulator\n");
+ }
if (pcie->reset_gpio)
gpiod_set_value_cansleep(pcie->reset_gpio, 1);
diff --git a/drivers/pci/devres.c b/drivers/pci/devres.c
index 9f4190501395..f075e7881c3a 100644
--- a/drivers/pci/devres.c
+++ b/drivers/pci/devres.c
@@ -469,9 +469,6 @@ static int pcim_add_mapping_to_legacy_table(struct pci_dev *pdev,
if (!legacy_iomap_table)
return -ENOMEM;
- /* The legacy mechanism doesn't allow for duplicate mappings. */
- WARN_ON(legacy_iomap_table[bar]);
-
legacy_iomap_table[bar] = mapping;
return 0;
diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c
index 6643a88c7a0c..27de533f0571 100644
--- a/drivers/pci/endpoint/functions/pci-epf-mhi.c
+++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c
@@ -686,7 +686,7 @@ static int pci_epf_mhi_dma_init(struct pci_epf_mhi *epf_mhi)
goto err_release_tx;
}
- epf_mhi->dma_wq = alloc_workqueue("pci_epf_mhi_dma_wq", 0, 0);
+ epf_mhi->dma_wq = alloc_workqueue("pci_epf_mhi_dma_wq", WQ_PERCPU, 0);
if (!epf_mhi->dma_wq) {
ret = -ENOMEM;
goto err_release_rx;
diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c
index e01a98e74d21..a3a588e522e7 100644
--- a/drivers/pci/endpoint/functions/pci-epf-ntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c
@@ -2124,8 +2124,13 @@ static int __init epf_ntb_init(void)
{
int ret;
- kpcintb_workqueue = alloc_workqueue("kpcintb", WQ_MEM_RECLAIM |
- WQ_HIGHPRI, 0);
+ kpcintb_workqueue = alloc_workqueue("kpcintb",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, 0);
+ if (!kpcintb_workqueue) {
+ pr_err("Failed to allocate kpcintb workqueue\n");
+ return -ENOMEM;
+ }
+
ret = pci_epf_register_driver(&epf_ntb_driver);
if (ret) {
destroy_workqueue(kpcintb_workqueue);
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index debd235253c5..0cb7af0919dc 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -33,6 +33,8 @@
#define COMMAND_COPY BIT(5)
#define COMMAND_ENABLE_DOORBELL BIT(6)
#define COMMAND_DISABLE_DOORBELL BIT(7)
+#define COMMAND_BAR_SUBRANGE_SETUP BIT(8)
+#define COMMAND_BAR_SUBRANGE_CLEAR BIT(9)
#define STATUS_READ_SUCCESS BIT(0)
#define STATUS_READ_FAIL BIT(1)
@@ -48,6 +50,10 @@
#define STATUS_DOORBELL_ENABLE_FAIL BIT(11)
#define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12)
#define STATUS_DOORBELL_DISABLE_FAIL BIT(13)
+#define STATUS_BAR_SUBRANGE_SETUP_SUCCESS BIT(14)
+#define STATUS_BAR_SUBRANGE_SETUP_FAIL BIT(15)
+#define STATUS_BAR_SUBRANGE_CLEAR_SUCCESS BIT(16)
+#define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17)
#define FLAG_USE_DMA BIT(0)
@@ -57,12 +63,16 @@
#define CAP_MSI BIT(1)
#define CAP_MSIX BIT(2)
#define CAP_INTX BIT(3)
+#define CAP_SUBRANGE_MAPPING BIT(4)
+
+#define PCI_EPF_TEST_BAR_SUBRANGE_NSUB 2
static struct workqueue_struct *kpcitest_workqueue;
struct pci_epf_test {
void *reg[PCI_STD_NUM_BARS];
struct pci_epf *epf;
+ struct config_group group;
enum pci_barno test_reg_bar;
size_t msix_table_offset;
struct delayed_work cmd_handler;
@@ -76,6 +86,7 @@ struct pci_epf_test {
bool dma_private;
const struct pci_epc_features *epc_features;
struct pci_epf_bar db_bar;
+ size_t bar_size[PCI_STD_NUM_BARS];
};
struct pci_epf_test_reg {
@@ -102,7 +113,8 @@ static struct pci_epf_header test_header = {
.interrupt_pin = PCI_INTERRUPT_INTA,
};
-static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
+/* default BAR sizes, can be overridden by the user using configfs */
+static size_t default_bar_size[] = { 131072, 131072, 131072, 131072, 131072, 1048576 };
static void pci_epf_test_dma_callback(void *param)
{
@@ -806,6 +818,155 @@ set_status_err:
reg->status = cpu_to_le32(status);
}
+static u8 pci_epf_test_subrange_sig_byte(enum pci_barno barno,
+ unsigned int subno)
+{
+ return 0x50 + (barno * 8) + subno;
+}
+
+static void pci_epf_test_bar_subrange_setup(struct pci_epf_test *epf_test,
+ struct pci_epf_test_reg *reg)
+{
+ struct pci_epf_bar_submap *submap, *old_submap;
+ struct pci_epf *epf = epf_test->epf;
+ struct pci_epc *epc = epf->epc;
+ struct pci_epf_bar *bar;
+ unsigned int nsub = PCI_EPF_TEST_BAR_SUBRANGE_NSUB, old_nsub;
+ /* reg->size carries BAR number for BAR_SUBRANGE_* commands. */
+ enum pci_barno barno = le32_to_cpu(reg->size);
+ u32 status = le32_to_cpu(reg->status);
+ unsigned int i, phys_idx;
+ size_t sub_size;
+ u8 *addr;
+ int ret;
+
+ if (barno >= PCI_STD_NUM_BARS) {
+ dev_err(&epf->dev, "Invalid barno: %d\n", barno);
+ goto err;
+ }
+
+ /* Host side should've avoided test_reg_bar, this is a safeguard. */
+ if (barno == epf_test->test_reg_bar) {
+ dev_err(&epf->dev, "test_reg_bar cannot be used for subrange test\n");
+ goto err;
+ }
+
+ if (!epf_test->epc_features->dynamic_inbound_mapping ||
+ !epf_test->epc_features->subrange_mapping) {
+ dev_err(&epf->dev, "epc driver does not support subrange mapping\n");
+ goto err;
+ }
+
+ bar = &epf->bar[barno];
+ if (!bar->size || !bar->addr) {
+ dev_err(&epf->dev, "bar size/addr (%zu/%p) is invalid\n",
+ bar->size, bar->addr);
+ goto err;
+ }
+
+ if (bar->size % nsub) {
+ dev_err(&epf->dev, "BAR size %zu is not divisible by %u\n",
+ bar->size, nsub);
+ goto err;
+ }
+
+ sub_size = bar->size / nsub;
+
+ submap = kcalloc(nsub, sizeof(*submap), GFP_KERNEL);
+ if (!submap)
+ goto err;
+
+ for (i = 0; i < nsub; i++) {
+ /* Swap the two halves so RC can verify ordering. */
+ phys_idx = i ^ 1;
+ submap[i].phys_addr = bar->phys_addr + (phys_idx * sub_size);
+ submap[i].size = sub_size;
+ }
+
+ old_submap = bar->submap;
+ old_nsub = bar->num_submap;
+
+ bar->submap = submap;
+ bar->num_submap = nsub;
+
+ ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, bar);
+ if (ret) {
+ dev_err(&epf->dev, "pci_epc_set_bar() failed: %d\n", ret);
+ bar->submap = old_submap;
+ bar->num_submap = old_nsub;
+ kfree(submap);
+ goto err;
+ }
+ kfree(old_submap);
+
+ /*
+ * Fill deterministic signatures into the physical regions that
+ * each BAR subrange maps to. RC verifies these to ensure the
+ * submap order is really applied.
+ */
+ addr = (u8 *)bar->addr;
+ for (i = 0; i < nsub; i++) {
+ phys_idx = i ^ 1;
+ memset(addr + (phys_idx * sub_size),
+ pci_epf_test_subrange_sig_byte(barno, i),
+ sub_size);
+ }
+
+ status |= STATUS_BAR_SUBRANGE_SETUP_SUCCESS;
+ reg->status = cpu_to_le32(status);
+ return;
+
+err:
+ status |= STATUS_BAR_SUBRANGE_SETUP_FAIL;
+ reg->status = cpu_to_le32(status);
+}
+
+static void pci_epf_test_bar_subrange_clear(struct pci_epf_test *epf_test,
+ struct pci_epf_test_reg *reg)
+{
+ struct pci_epf *epf = epf_test->epf;
+ struct pci_epf_bar_submap *submap;
+ struct pci_epc *epc = epf->epc;
+ /* reg->size carries BAR number for BAR_SUBRANGE_* commands. */
+ enum pci_barno barno = le32_to_cpu(reg->size);
+ u32 status = le32_to_cpu(reg->status);
+ struct pci_epf_bar *bar;
+ unsigned int nsub;
+ int ret;
+
+ if (barno >= PCI_STD_NUM_BARS) {
+ dev_err(&epf->dev, "Invalid barno: %d\n", barno);
+ goto err;
+ }
+
+ bar = &epf->bar[barno];
+ submap = bar->submap;
+ nsub = bar->num_submap;
+
+ if (!submap || !nsub)
+ goto err;
+
+ bar->submap = NULL;
+ bar->num_submap = 0;
+
+ ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, bar);
+ if (ret) {
+ bar->submap = submap;
+ bar->num_submap = nsub;
+ dev_err(&epf->dev, "pci_epc_set_bar() failed: %d\n", ret);
+ goto err;
+ }
+ kfree(submap);
+
+ status |= STATUS_BAR_SUBRANGE_CLEAR_SUCCESS;
+ reg->status = cpu_to_le32(status);
+ return;
+
+err:
+ status |= STATUS_BAR_SUBRANGE_CLEAR_FAIL;
+ reg->status = cpu_to_le32(status);
+}
+
static void pci_epf_test_cmd_handler(struct work_struct *work)
{
u32 command;
@@ -861,6 +1022,14 @@ static void pci_epf_test_cmd_handler(struct work_struct *work)
pci_epf_test_disable_doorbell(epf_test, reg);
pci_epf_test_raise_irq(epf_test, reg);
break;
+ case COMMAND_BAR_SUBRANGE_SETUP:
+ pci_epf_test_bar_subrange_setup(epf_test, reg);
+ pci_epf_test_raise_irq(epf_test, reg);
+ break;
+ case COMMAND_BAR_SUBRANGE_CLEAR:
+ pci_epf_test_bar_subrange_clear(epf_test, reg);
+ pci_epf_test_raise_irq(epf_test, reg);
+ break;
default:
dev_err(dev, "Invalid command 0x%x\n", command);
break;
@@ -933,6 +1102,10 @@ static void pci_epf_test_set_capabilities(struct pci_epf *epf)
if (epf_test->epc_features->intx_capable)
caps |= CAP_INTX;
+ if (epf_test->epc_features->dynamic_inbound_mapping &&
+ epf_test->epc_features->subrange_mapping)
+ caps |= CAP_SUBRANGE_MAPPING;
+
reg->caps = cpu_to_le32(caps);
}
@@ -1070,7 +1243,7 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
if (epc_features->bar[bar].type == BAR_FIXED)
test_reg_size = epc_features->bar[bar].fixed_size;
else
- test_reg_size = bar_size[bar];
+ test_reg_size = epf_test->bar_size[bar];
base = pci_epf_alloc_space(epf, test_reg_size, bar,
epc_features, PRIMARY_INTERFACE);
@@ -1142,6 +1315,94 @@ static void pci_epf_test_unbind(struct pci_epf *epf)
pci_epf_test_free_space(epf);
}
+#define PCI_EPF_TEST_BAR_SIZE_R(_name, _id) \
+static ssize_t pci_epf_test_##_name##_show(struct config_item *item, \
+ char *page) \
+{ \
+ struct config_group *group = to_config_group(item); \
+ struct pci_epf_test *epf_test = \
+ container_of(group, struct pci_epf_test, group); \
+ \
+ return sysfs_emit(page, "%zu\n", epf_test->bar_size[_id]); \
+}
+
+#define PCI_EPF_TEST_BAR_SIZE_W(_name, _id) \
+static ssize_t pci_epf_test_##_name##_store(struct config_item *item, \
+ const char *page, \
+ size_t len) \
+{ \
+ struct config_group *group = to_config_group(item); \
+ struct pci_epf_test *epf_test = \
+ container_of(group, struct pci_epf_test, group); \
+ int val, ret; \
+ \
+ /* \
+ * BAR sizes can only be modified before binding to an EPC, \
+ * because pci_epf_test_alloc_space() is called in .bind(). \
+ */ \
+ if (epf_test->epf->epc) \
+ return -EOPNOTSUPP; \
+ \
+ ret = kstrtouint(page, 0, &val); \
+ if (ret) \
+ return ret; \
+ \
+ if (!is_power_of_2(val)) \
+ return -EINVAL; \
+ \
+ epf_test->bar_size[_id] = val; \
+ \
+ return len; \
+}
+
+PCI_EPF_TEST_BAR_SIZE_R(bar0_size, BAR_0)
+PCI_EPF_TEST_BAR_SIZE_W(bar0_size, BAR_0)
+PCI_EPF_TEST_BAR_SIZE_R(bar1_size, BAR_1)
+PCI_EPF_TEST_BAR_SIZE_W(bar1_size, BAR_1)
+PCI_EPF_TEST_BAR_SIZE_R(bar2_size, BAR_2)
+PCI_EPF_TEST_BAR_SIZE_W(bar2_size, BAR_2)
+PCI_EPF_TEST_BAR_SIZE_R(bar3_size, BAR_3)
+PCI_EPF_TEST_BAR_SIZE_W(bar3_size, BAR_3)
+PCI_EPF_TEST_BAR_SIZE_R(bar4_size, BAR_4)
+PCI_EPF_TEST_BAR_SIZE_W(bar4_size, BAR_4)
+PCI_EPF_TEST_BAR_SIZE_R(bar5_size, BAR_5)
+PCI_EPF_TEST_BAR_SIZE_W(bar5_size, BAR_5)
+
+CONFIGFS_ATTR(pci_epf_test_, bar0_size);
+CONFIGFS_ATTR(pci_epf_test_, bar1_size);
+CONFIGFS_ATTR(pci_epf_test_, bar2_size);
+CONFIGFS_ATTR(pci_epf_test_, bar3_size);
+CONFIGFS_ATTR(pci_epf_test_, bar4_size);
+CONFIGFS_ATTR(pci_epf_test_, bar5_size);
+
+static struct configfs_attribute *pci_epf_test_attrs[] = {
+ &pci_epf_test_attr_bar0_size,
+ &pci_epf_test_attr_bar1_size,
+ &pci_epf_test_attr_bar2_size,
+ &pci_epf_test_attr_bar3_size,
+ &pci_epf_test_attr_bar4_size,
+ &pci_epf_test_attr_bar5_size,
+ NULL,
+};
+
+static const struct config_item_type pci_epf_test_group_type = {
+ .ct_attrs = pci_epf_test_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *pci_epf_test_add_cfs(struct pci_epf *epf,
+ struct config_group *group)
+{
+ struct pci_epf_test *epf_test = epf_get_drvdata(epf);
+ struct config_group *epf_group = &epf_test->group;
+ struct device *dev = &epf->dev;
+
+ config_group_init_type_name(epf_group, dev_name(dev),
+ &pci_epf_test_group_type);
+
+ return epf_group;
+}
+
static const struct pci_epf_device_id pci_epf_test_ids[] = {
{
.name = "pci_epf_test",
@@ -1154,6 +1415,7 @@ static int pci_epf_test_probe(struct pci_epf *epf,
{
struct pci_epf_test *epf_test;
struct device *dev = &epf->dev;
+ enum pci_barno bar;
epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL);
if (!epf_test)
@@ -1161,6 +1423,8 @@ static int pci_epf_test_probe(struct pci_epf *epf,
epf->header = &test_header;
epf_test->epf = epf;
+ for (bar = BAR_0; bar < PCI_STD_NUM_BARS; bar++)
+ epf_test->bar_size[bar] = default_bar_size[bar];
INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);
@@ -1173,6 +1437,7 @@ static int pci_epf_test_probe(struct pci_epf *epf,
static const struct pci_epf_ops ops = {
.unbind = pci_epf_test_unbind,
.bind = pci_epf_test_bind,
+ .add_cfs = pci_epf_test_add_cfs,
};
static struct pci_epf_driver test_driver = {
@@ -1188,7 +1453,7 @@ static int __init pci_epf_test_init(void)
int ret;
kpcitest_workqueue = alloc_workqueue("kpcitest",
- WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
+ WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, 0);
if (!kpcitest_workqueue) {
pr_err("Failed to allocate the kpcitest work queue\n");
return -ENOMEM;
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index 3ecc5059f92b..20a400e83439 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -1651,8 +1651,13 @@ static int __init epf_ntb_init(void)
{
int ret;
- kpcintb_workqueue = alloc_workqueue("kpcintb", WQ_MEM_RECLAIM |
- WQ_HIGHPRI, 0);
+ kpcintb_workqueue = alloc_workqueue("kpcintb",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU, 0);
+ if (!kpcintb_workqueue) {
+ pr_err("Failed to allocate kpcintb workqueue\n");
+ return -ENOMEM;
+ }
+
ret = pci_epf_register_driver(&epf_ntb_driver);
if (ret) {
destroy_workqueue(kpcintb_workqueue);
diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c
index ef50c82e647f..8b392a8363bb 100644
--- a/drivers/pci/endpoint/pci-ep-cfs.c
+++ b/drivers/pci/endpoint/pci-ep-cfs.c
@@ -23,7 +23,6 @@ struct pci_epf_group {
struct config_group group;
struct config_group primary_epc_group;
struct config_group secondary_epc_group;
- struct delayed_work cfs_work;
struct pci_epf *epf;
int index;
};
@@ -69,8 +68,8 @@ static int pci_secondary_epc_epf_link(struct config_item *epf_item,
return 0;
}
-static void pci_secondary_epc_epf_unlink(struct config_item *epc_item,
- struct config_item *epf_item)
+static void pci_secondary_epc_epf_unlink(struct config_item *epf_item,
+ struct config_item *epc_item)
{
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
@@ -103,7 +102,7 @@ static struct config_group
secondary_epc_group = &epf_group->secondary_epc_group;
config_group_init_type_name(secondary_epc_group, "secondary",
&pci_secondary_epc_type);
- configfs_register_group(&epf_group->group, secondary_epc_group);
+ configfs_add_default_group(secondary_epc_group, &epf_group->group);
return secondary_epc_group;
}
@@ -133,8 +132,8 @@ static int pci_primary_epc_epf_link(struct config_item *epf_item,
return 0;
}
-static void pci_primary_epc_epf_unlink(struct config_item *epc_item,
- struct config_item *epf_item)
+static void pci_primary_epc_epf_unlink(struct config_item *epf_item,
+ struct config_item *epc_item)
{
struct pci_epf_group *epf_group = to_pci_epf_group(epf_item->ci_parent);
struct pci_epc_group *epc_group = to_pci_epc_group(epc_item);
@@ -166,7 +165,7 @@ static struct config_group
config_group_init_type_name(primary_epc_group, "primary",
&pci_primary_epc_type);
- configfs_register_group(&epf_group->group, primary_epc_group);
+ configfs_add_default_group(primary_epc_group, &epf_group->group);
return primary_epc_group;
}
@@ -570,15 +569,13 @@ static void pci_ep_cfs_add_type_group(struct pci_epf_group *epf_group)
return;
}
- configfs_register_group(&epf_group->group, group);
+ configfs_add_default_group(group, &epf_group->group);
}
-static void pci_epf_cfs_work(struct work_struct *work)
+static void pci_epf_cfs_add_sub_groups(struct pci_epf_group *epf_group)
{
- struct pci_epf_group *epf_group;
struct config_group *group;
- epf_group = container_of(work, struct pci_epf_group, cfs_work.work);
group = pci_ep_cfs_add_primary_group(epf_group);
if (IS_ERR(group)) {
pr_err("failed to create 'primary' EPC interface\n");
@@ -637,9 +634,7 @@ static struct config_group *pci_epf_make(struct config_group *group,
kfree(epf_name);
- INIT_DELAYED_WORK(&epf_group->cfs_work, pci_epf_cfs_work);
- queue_delayed_work(system_wq, &epf_group->cfs_work,
- msecs_to_jiffies(1));
+ pci_epf_cfs_add_sub_groups(epf_group);
return &epf_group->group;
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index ca7f19cc973a..068155819c57 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -596,6 +596,14 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
if (!epc_features)
return -EINVAL;
+ if (epf_bar->num_submap && !epf_bar->submap)
+ return -EINVAL;
+
+ if (epf_bar->num_submap &&
+ !(epc_features->dynamic_inbound_mapping &&
+ epc_features->subrange_mapping))
+ return -EINVAL;
+
if (epc_features->bar[bar].type == BAR_RESIZABLE &&
(epf_bar->size < SZ_1M || (u64)epf_bar->size > (SZ_128G * 1024)))
return -EINVAL;
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index bcc938d4420f..7805f697a02c 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -19,6 +19,7 @@
#include <linux/types.h>
#include <linux/pm_runtime.h>
#include <linux/pci.h>
+#include <trace/events/pci.h>
#include "../pci.h"
#include "pciehp.h"
@@ -244,12 +245,20 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
case ON_STATE:
ctrl->state = POWEROFF_STATE;
mutex_unlock(&ctrl->state_lock);
- if (events & PCI_EXP_SLTSTA_DLLSC)
+ if (events & PCI_EXP_SLTSTA_DLLSC) {
ctrl_info(ctrl, "Slot(%s): Link Down\n",
slot_name(ctrl));
- if (events & PCI_EXP_SLTSTA_PDC)
+ trace_pci_hp_event(pci_name(ctrl->pcie->port),
+ slot_name(ctrl),
+ PCI_HOTPLUG_LINK_DOWN);
+ }
+ if (events & PCI_EXP_SLTSTA_PDC) {
ctrl_info(ctrl, "Slot(%s): Card not present\n",
slot_name(ctrl));
+ trace_pci_hp_event(pci_name(ctrl->pcie->port),
+ slot_name(ctrl),
+ PCI_HOTPLUG_CARD_NOT_PRESENT);
+ }
pciehp_disable_slot(ctrl, SURPRISE_REMOVAL);
break;
default:
@@ -269,6 +278,9 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
INDICATOR_NOOP);
ctrl_info(ctrl, "Slot(%s): Card not present\n",
slot_name(ctrl));
+ trace_pci_hp_event(pci_name(ctrl->pcie->port),
+ slot_name(ctrl),
+ PCI_HOTPLUG_CARD_NOT_PRESENT);
}
mutex_unlock(&ctrl->state_lock);
return;
@@ -281,12 +293,19 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
case OFF_STATE:
ctrl->state = POWERON_STATE;
mutex_unlock(&ctrl->state_lock);
- if (present)
+ if (present) {
ctrl_info(ctrl, "Slot(%s): Card present\n",
slot_name(ctrl));
- if (link_active)
- ctrl_info(ctrl, "Slot(%s): Link Up\n",
- slot_name(ctrl));
+ trace_pci_hp_event(pci_name(ctrl->pcie->port),
+ slot_name(ctrl),
+ PCI_HOTPLUG_CARD_PRESENT);
+ }
+ if (link_active) {
+ ctrl_info(ctrl, "Slot(%s): Link Up\n", slot_name(ctrl));
+ trace_pci_hp_event(pci_name(ctrl->pcie->port),
+ slot_name(ctrl),
+ PCI_HOTPLUG_LINK_UP);
+ }
ctrl->request_result = pciehp_enable_slot(ctrl);
break;
default:
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index bcc51b26d03d..ad5f28f6a8b1 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -320,7 +320,8 @@ int pciehp_check_link_status(struct controller *ctrl)
}
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA2, &linksta2);
- __pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status, linksta2);
+ __pcie_update_link_speed(ctrl->pcie->port->subordinate, PCIE_HOTPLUG,
+ lnk_status, linksta2);
if (!found) {
ctrl_info(ctrl, "Slot(%s): No device found\n",
diff --git a/drivers/pci/hotplug/pnv_php.c b/drivers/pci/hotplug/pnv_php.c
index c5345bff9a55..35f1758126c6 100644
--- a/drivers/pci/hotplug/pnv_php.c
+++ b/drivers/pci/hotplug/pnv_php.c
@@ -802,7 +802,7 @@ static struct pnv_php_slot *pnv_php_alloc_slot(struct device_node *dn)
}
/* Allocate workqueue for this slot's interrupt handling */
- php_slot->wq = alloc_workqueue("pciehp-%s", 0, 0, php_slot->name);
+ php_slot->wq = alloc_workqueue("pciehp-%s", WQ_PERCPU, 0, php_slot->name);
if (!php_slot->wq) {
SLOT_WARN(php_slot, "Cannot alloc workqueue\n");
kfree(php_slot->name);
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index 0c341453afc6..56308515ecba 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -80,7 +80,8 @@ static int init_slots(struct controller *ctrl)
slot->device = ctrl->slot_device_offset + i;
slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i);
- slot->wq = alloc_workqueue("shpchp-%d", 0, 0, slot->number);
+ slot->wq = alloc_workqueue("shpchp-%d", WQ_PERCPU, 0,
+ slot->number);
if (!slot->wq) {
retval = -ENOMEM;
goto error_slot;
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 00784a60ba80..4a659c34935e 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -495,7 +495,9 @@ static ssize_t sriov_numvfs_store(struct device *dev,
if (num_vfs == 0) {
/* disable VFs */
+ pci_lock_rescan_remove();
ret = pdev->driver->sriov_configure(pdev, 0);
+ pci_unlock_rescan_remove();
goto exit;
}
@@ -507,7 +509,9 @@ static ssize_t sriov_numvfs_store(struct device *dev,
goto exit;
}
+ pci_lock_rescan_remove();
ret = pdev->driver->sriov_configure(pdev, num_vfs);
+ pci_unlock_rescan_remove();
if (ret < 0)
goto exit;
@@ -629,18 +633,15 @@ static int sriov_add_vfs(struct pci_dev *dev, u16 num_vfs)
if (dev->no_vf_scan)
return 0;
- pci_lock_rescan_remove();
for (i = 0; i < num_vfs; i++) {
rc = pci_iov_add_virtfn(dev, i);
if (rc)
goto failed;
}
- pci_unlock_rescan_remove();
return 0;
failed:
while (i--)
pci_iov_remove_virtfn(dev, i);
- pci_unlock_rescan_remove();
return rc;
}
@@ -765,10 +766,8 @@ static void sriov_del_vfs(struct pci_dev *dev)
struct pci_sriov *iov = dev->sriov;
int i;
- pci_lock_rescan_remove();
for (i = 0; i < iov->num_VFs; i++)
pci_iov_remove_virtfn(dev, i);
- pci_unlock_rescan_remove();
}
static void sriov_disable(struct pci_dev *dev)
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
index 3579265f1198..9bb5f258759b 100644
--- a/drivers/pci/of.c
+++ b/drivers/pci/of.c
@@ -867,6 +867,7 @@ bool of_pci_supply_present(struct device_node *np)
return false;
}
+EXPORT_SYMBOL_GPL(of_pci_supply_present);
#endif /* CONFIG_PCI */
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index 4a2fc7ab42c3..79a414fd6623 100644
--- a/drivers/pci/p2pdma.c
+++ b/drivers/pci/p2pdma.c
@@ -147,11 +147,19 @@ static int p2pmem_alloc_mmap(struct file *filp, struct kobject *kobj,
* we have just allocated the page no one else should be
* using it.
*/
- VM_WARN_ON_ONCE_PAGE(!page_ref_count(page), page);
+ VM_WARN_ON_ONCE_PAGE(page_ref_count(page), page);
set_page_count(page, 1);
ret = vm_insert_page(vma, vaddr, page);
if (ret) {
gen_pool_free(p2pdma->pool, (uintptr_t)kaddr, len);
+
+ /*
+ * Reset the page count. We don't use put_page()
+ * because we don't want to trigger the
+ * p2pdma_folio_free() path.
+ */
+ set_page_count(page, 0);
+ percpu_ref_put(ref);
return ret;
}
percpu_ref_get(ref);
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 72c101c2926f..218e1fc1c1e2 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -272,21 +272,6 @@ static acpi_status decode_type1_hpx_record(union acpi_object *record,
return AE_OK;
}
-static bool pcie_root_rcb_set(struct pci_dev *dev)
-{
- struct pci_dev *rp = pcie_find_root_port(dev);
- u16 lnkctl;
-
- if (!rp)
- return false;
-
- pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &lnkctl);
- if (lnkctl & PCI_EXP_LNKCTL_RCB)
- return true;
-
- return false;
-}
-
/* _HPX PCI Express Setting Record (Type 2) */
struct hpx_type2 {
u32 revision;
@@ -312,6 +297,7 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx)
{
int pos;
u32 reg32;
+ const struct pci_host_bridge *host;
if (!hpx)
return;
@@ -319,6 +305,15 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx)
if (!pci_is_pcie(dev))
return;
+ host = pci_find_host_bridge(dev->bus);
+
+ /*
+ * Only do the _HPX Type 2 programming if OS owns PCIe native
+ * hotplug but not AER.
+ */
+ if (!host->native_pcie_hotplug || host->native_aer)
+ return;
+
if (hpx->revision > 1) {
pci_warn(dev, "PCIe settings rev %d not supported\n",
hpx->revision);
@@ -326,33 +321,27 @@ static void program_hpx_type2(struct pci_dev *dev, struct hpx_type2 *hpx)
}
/*
- * Don't allow _HPX to change MPS or MRRS settings. We manage
- * those to make sure they're consistent with the rest of the
- * platform.
+ * We only allow _HPX to program DEVCTL bits related to AER, namely
+ * PCI_EXP_DEVCTL_CERE, PCI_EXP_DEVCTL_NFERE, PCI_EXP_DEVCTL_FERE,
+ * and PCI_EXP_DEVCTL_URRE.
+ *
+ * The rest of DEVCTL is managed by the OS to make sure it's
+ * consistent with the rest of the platform.
*/
- hpx->pci_exp_devctl_and |= PCI_EXP_DEVCTL_PAYLOAD |
- PCI_EXP_DEVCTL_READRQ;
- hpx->pci_exp_devctl_or &= ~(PCI_EXP_DEVCTL_PAYLOAD |
- PCI_EXP_DEVCTL_READRQ);
+ hpx->pci_exp_devctl_and |= ~PCI_EXP_AER_FLAGS;
+ hpx->pci_exp_devctl_or &= PCI_EXP_AER_FLAGS;
/* Initialize Device Control Register */
pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
~hpx->pci_exp_devctl_and, hpx->pci_exp_devctl_or);
- /* Initialize Link Control Register */
+ /* Log if _HPX attempts to modify Link Control Register */
if (pcie_cap_has_lnkctl(dev)) {
-
- /*
- * If the Root Port supports Read Completion Boundary of
- * 128, set RCB to 128. Otherwise, clear it.
- */
- hpx->pci_exp_lnkctl_and |= PCI_EXP_LNKCTL_RCB;
- hpx->pci_exp_lnkctl_or &= ~PCI_EXP_LNKCTL_RCB;
- if (pcie_root_rcb_set(dev))
- hpx->pci_exp_lnkctl_or |= PCI_EXP_LNKCTL_RCB;
-
- pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
- ~hpx->pci_exp_lnkctl_and, hpx->pci_exp_lnkctl_or);
+ if (hpx->pci_exp_lnkctl_and != 0xffff ||
+ hpx->pci_exp_lnkctl_or != 0)
+ pci_info(dev, "_HPX attempts Link Control setting (AND %#06x OR %#06x)\n",
+ hpx->pci_exp_lnkctl_and,
+ hpx->pci_exp_lnkctl_or);
}
/* Find Advanced Error Reporting Enhanced Capability */
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index a9590601835a..47887d55994a 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -1679,6 +1679,14 @@ static int pci_dma_configure(struct device *dev)
ret = acpi_dma_configure(dev, acpi_get_dma_attr(adev));
}
+ /*
+ * Attempt to enable ACS regardless of capability because some Root
+ * Ports (e.g. those quirked with *_intel_pch_acs_*) do not have
+ * the standard ACS capability but still support ACS via those
+ * quirks.
+ */
+ pci_enable_acs(to_pci_dev(dev));
+
pci_put_host_bridge_device(bridge);
/* @drv may not be valid when we're called from the IOMMU layer */
@@ -1730,34 +1738,6 @@ const struct bus_type pci_bus_type = {
};
EXPORT_SYMBOL(pci_bus_type);
-#ifdef CONFIG_PCIEPORTBUS
-static int pcie_port_bus_match(struct device *dev, const struct device_driver *drv)
-{
- struct pcie_device *pciedev;
- const struct pcie_port_service_driver *driver;
-
- if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
- return 0;
-
- pciedev = to_pcie_device(dev);
- driver = to_service_driver(drv);
-
- if (driver->service != pciedev->service)
- return 0;
-
- if (driver->port_type != PCIE_ANY_PORT &&
- driver->port_type != pci_pcie_type(pciedev->port))
- return 0;
-
- return 1;
-}
-
-const struct bus_type pcie_port_bus_type = {
- .name = "pci_express",
- .match = pcie_port_bus_match,
-};
-#endif
-
static int __init pci_driver_init(void)
{
int ret;
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index c2df915ad2d2..363187ba4f56 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -181,7 +181,7 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
struct resource zerores = {};
/* For backwards compatibility */
- if (i >= PCI_BRIDGE_RESOURCES && i <= PCI_BRIDGE_RESOURCE_END &&
+ if (pci_resource_is_bridge_win(i) &&
res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED))
res = &zerores;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a0ba42ae7ee0..f3244630bfd0 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -14,6 +14,7 @@
#include <linux/dmi.h>
#include <linux/init.h>
#include <linux/iommu.h>
+#include <linux/lockdep.h>
#include <linux/msi.h>
#include <linux/of.h>
#include <linux/pci.h>
@@ -101,12 +102,6 @@ bool pci_reset_supported(struct pci_dev *dev)
int pci_domains_supported = 1;
#endif
-#define DEFAULT_CARDBUS_IO_SIZE (256)
-#define DEFAULT_CARDBUS_MEM_SIZE (64*1024*1024)
-/* pci=cbmemsize=nnM,cbiosize=nn can override this */
-unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE;
-unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE;
-
#define DEFAULT_HOTPLUG_IO_SIZE (256)
#define DEFAULT_HOTPLUG_MMIO_SIZE (2*1024*1024)
#define DEFAULT_HOTPLUG_MMIO_PREF_SIZE (2*1024*1024)
@@ -428,7 +423,7 @@ found:
static u8 __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn,
u8 pos, int cap)
{
- return PCI_FIND_NEXT_CAP(pci_bus_read_config, pos, cap, bus, devfn);
+ return PCI_FIND_NEXT_CAP(pci_bus_read_config, pos, cap, NULL, bus, devfn);
}
u8 pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap)
@@ -533,7 +528,7 @@ u16 pci_find_next_ext_capability(struct pci_dev *dev, u16 start, int cap)
return 0;
return PCI_FIND_NEXT_EXT_CAP(pci_bus_read_config, start, cap,
- dev->bus, dev->devfn);
+ NULL, dev->bus, dev->devfn);
}
EXPORT_SYMBOL_GPL(pci_find_next_ext_capability);
@@ -602,7 +597,7 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap)
mask = HT_5BIT_CAP_MASK;
pos = PCI_FIND_NEXT_CAP(pci_bus_read_config, pos,
- PCI_CAP_ID_HT, dev->bus, dev->devfn);
+ PCI_CAP_ID_HT, NULL, dev->bus, dev->devfn);
while (pos) {
rc = pci_read_config_byte(dev, pos + 3, &cap);
if (rc != PCIBIOS_SUCCESSFUL)
@@ -613,7 +608,7 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap)
pos = PCI_FIND_NEXT_CAP(pci_bus_read_config,
pos + PCI_CAP_LIST_NEXT,
- PCI_CAP_ID_HT, dev->bus,
+ PCI_CAP_ID_HT, NULL, dev->bus,
dev->devfn);
}
@@ -894,7 +889,6 @@ static const char *disable_acs_redir_param;
static const char *config_acs_param;
struct pci_acs {
- u16 cap;
u16 ctrl;
u16 fw_ctrl;
};
@@ -997,27 +991,27 @@ static void __pci_config_acs(struct pci_dev *dev, struct pci_acs *caps,
static void pci_std_enable_acs(struct pci_dev *dev, struct pci_acs *caps)
{
/* Source Validation */
- caps->ctrl |= (caps->cap & PCI_ACS_SV);
+ caps->ctrl |= (dev->acs_capabilities & PCI_ACS_SV);
/* P2P Request Redirect */
- caps->ctrl |= (caps->cap & PCI_ACS_RR);
+ caps->ctrl |= (dev->acs_capabilities & PCI_ACS_RR);
/* P2P Completion Redirect */
- caps->ctrl |= (caps->cap & PCI_ACS_CR);
+ caps->ctrl |= (dev->acs_capabilities & PCI_ACS_CR);
/* Upstream Forwarding */
- caps->ctrl |= (caps->cap & PCI_ACS_UF);
+ caps->ctrl |= (dev->acs_capabilities & PCI_ACS_UF);
/* Enable Translation Blocking for external devices and noats */
if (pci_ats_disabled() || dev->external_facing || dev->untrusted)
- caps->ctrl |= (caps->cap & PCI_ACS_TB);
+ caps->ctrl |= (dev->acs_capabilities & PCI_ACS_TB);
}
/**
* pci_enable_acs - enable ACS if hardware support it
* @dev: the PCI device
*/
-static void pci_enable_acs(struct pci_dev *dev)
+void pci_enable_acs(struct pci_dev *dev)
{
struct pci_acs caps;
bool enable_acs = false;
@@ -1033,7 +1027,6 @@ static void pci_enable_acs(struct pci_dev *dev)
if (!pos)
return;
- pci_read_config_word(dev, pos + PCI_ACS_CAP, &caps.cap);
pci_read_config_word(dev, pos + PCI_ACS_CTRL, &caps.ctrl);
caps.fw_ctrl = caps.ctrl;
@@ -1490,6 +1483,9 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state, bool
|| (state == PCI_D2 && !dev->d2_support))
return -EIO;
+ if (dev->current_state == state)
+ return 0;
+
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
if (PCI_POSSIBLE_ERROR(pmcsr)) {
pci_err(dev, "Unable to change power state from %s to %s, device inaccessible\n",
@@ -2258,7 +2254,7 @@ void pcie_clear_device_status(struct pci_dev *dev)
*/
void pcie_clear_root_pme_status(struct pci_dev *dev)
{
- pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
+ pcie_capability_write_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
}
/**
@@ -3198,8 +3194,14 @@ void pci_pm_init(struct pci_dev *dev)
poweron:
pci_pm_power_up_and_verify_state(dev);
pm_runtime_forbid(&dev->dev);
+
+ /*
+ * Runtime PM will be enabled for the device when it has been fully
+ * configured, but since its parent and suppliers may suspend in
+ * the meantime, prevent them from doing so by changing the
+ * device's runtime PM status to "active".
+ */
pm_runtime_set_active(&dev->dev);
- pm_runtime_enable(&dev->dev);
}
static unsigned long pci_ea_flags(struct pci_dev *dev, u8 prop)
@@ -3516,7 +3518,7 @@ void pci_configure_ari(struct pci_dev *dev)
static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
{
int pos;
- u16 cap, ctrl;
+ u16 ctrl;
pos = pdev->acs_cap;
if (!pos)
@@ -3527,8 +3529,7 @@ static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
* or only required if controllable. Features missing from the
* capability field can therefore be assumed as hard-wired enabled.
*/
- pci_read_config_word(pdev, pos + PCI_ACS_CAP, &cap);
- acs_flags &= (cap | PCI_ACS_EC);
+ acs_flags &= (pdev->acs_capabilities | PCI_ACS_EC);
pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl);
return (ctrl & acs_flags) == acs_flags;
@@ -3649,15 +3650,15 @@ bool pci_acs_path_enabled(struct pci_dev *start,
*/
void pci_acs_init(struct pci_dev *dev)
{
+ int pos;
+
dev->acs_cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
+ pos = dev->acs_cap;
+ if (!pos)
+ return;
- /*
- * Attempt to enable ACS regardless of capability because some Root
- * Ports (e.g. those quirked with *_intel_pch_acs_*) do not have
- * the standard ACS capability but still support ACS via those
- * quirks.
- */
- pci_enable_acs(dev);
+ pci_read_config_word(dev, pos + PCI_ACS_CAP, &dev->acs_capabilities);
+ pci_disable_broken_acs_cap(dev);
}
/**
@@ -4584,7 +4585,7 @@ int pcie_retrain_link(struct pci_dev *pdev, bool use_lt)
* Link Speed.
*/
if (pdev->subordinate)
- pcie_update_link_speed(pdev->subordinate);
+ pcie_update_link_speed(pdev->subordinate, PCIE_LINK_RETRAIN);
return rc;
}
@@ -4656,7 +4657,7 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active)
* spec says 100 ms, but firmware can lower it and we allow drivers to
* increase it as well.
*
- * Called with @pci_bus_sem locked for reading.
+ * Context: Called with @pci_bus_sem locked for reading.
*/
static int pci_bus_max_d3cold_delay(const struct pci_bus *bus)
{
@@ -4664,6 +4665,8 @@ static int pci_bus_max_d3cold_delay(const struct pci_bus *bus)
int min_delay = 100;
int max_delay = 0;
+ lockdep_assert_held(&pci_bus_sem);
+
list_for_each_entry(pdev, &bus->devices, bus_list) {
if (pdev->d3cold_delay < min_delay)
min_delay = pdev->d3cold_delay;
@@ -5018,6 +5021,7 @@ static void pci_dev_save_and_disable(struct pci_dev *dev)
* races with ->remove() by the device lock, which must be held by
* the caller.
*/
+ device_lock_assert(&dev->dev);
if (err_handler && err_handler->reset_prepare)
err_handler->reset_prepare(dev);
else if (dev->driver)
@@ -5088,7 +5092,9 @@ const struct pci_reset_fn_method pci_reset_fn_methods[] = {
* device including MSI, bus mastering, BARs, decoding IO and memory spaces,
* etc.
*
- * Returns 0 if the device function was successfully reset or negative if the
+ * Context: The caller must hold the device lock.
+ *
+ * Return: 0 if the device function was successfully reset or negative if the
* device doesn't support resetting a single function.
*/
int __pci_reset_function_locked(struct pci_dev *dev)
@@ -5097,6 +5103,7 @@ int __pci_reset_function_locked(struct pci_dev *dev)
const struct pci_reset_fn_method *method;
might_sleep();
+ device_lock_assert(&dev->dev);
/*
* A reset method returns -ENOTTY if it doesn't support this device and
@@ -5219,13 +5226,17 @@ EXPORT_SYMBOL_GPL(pci_reset_function);
* over the reset. It also differs from pci_reset_function() in that it
* requires the PCI device lock to be held.
*
- * Returns 0 if the device function was successfully reset or negative if the
+ * Context: The caller must hold the device lock.
+ *
+ * Return: 0 if the device function was successfully reset or negative if the
* device doesn't support resetting a single function.
*/
int pci_reset_function_locked(struct pci_dev *dev)
{
int rc;
+ device_lock_assert(&dev->dev);
+
if (!pci_reset_supported(dev))
return -ENOTTY;
@@ -5341,10 +5352,9 @@ unlock:
/* Do any devices on or below this slot prevent a bus reset? */
static bool pci_slot_resettable(struct pci_slot *slot)
{
- struct pci_dev *dev;
+ struct pci_dev *dev, *bridge = slot->bus->self;
- if (slot->bus->self &&
- (slot->bus->self->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET))
+ if (bridge && (bridge->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET))
return false;
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
@@ -5361,7 +5371,10 @@ static bool pci_slot_resettable(struct pci_slot *slot)
/* Lock devices from the top of the tree down */
static void pci_slot_lock(struct pci_slot *slot)
{
- struct pci_dev *dev;
+ struct pci_dev *dev, *bridge = slot->bus->self;
+
+ if (bridge)
+ pci_dev_lock(bridge);
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
if (!dev->slot || dev->slot != slot)
@@ -5376,7 +5389,7 @@ static void pci_slot_lock(struct pci_slot *slot)
/* Unlock devices from the bottom of the tree up */
static void pci_slot_unlock(struct pci_slot *slot)
{
- struct pci_dev *dev;
+ struct pci_dev *dev, *bridge = slot->bus->self;
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
if (!dev->slot || dev->slot != slot)
@@ -5386,21 +5399,25 @@ static void pci_slot_unlock(struct pci_slot *slot)
else
pci_dev_unlock(dev);
}
+
+ if (bridge)
+ pci_dev_unlock(bridge);
}
/* Return 1 on successful lock, 0 on contention */
static int pci_slot_trylock(struct pci_slot *slot)
{
- struct pci_dev *dev;
+ struct pci_dev *dev, *bridge = slot->bus->self;
+
+ if (bridge && !pci_dev_trylock(bridge))
+ return 0;
list_for_each_entry(dev, &slot->bus->devices, bus_list) {
if (!dev->slot || dev->slot != slot)
continue;
if (dev->subordinate) {
- if (!pci_bus_trylock(dev->subordinate)) {
- pci_dev_unlock(dev);
+ if (!pci_bus_trylock(dev->subordinate))
goto unlock;
- }
} else if (!pci_dev_trylock(dev))
goto unlock;
}
@@ -5416,6 +5433,9 @@ unlock:
else
pci_dev_unlock(dev);
}
+
+ if (bridge)
+ pci_dev_unlock(bridge);
return 0;
}
@@ -6642,7 +6662,7 @@ static void of_pci_bus_release_domain_nr(struct device *parent, int domain_nr)
return;
/* Release domain from IDA where it was allocated. */
- if (of_get_pci_domain_nr(parent->of_node) == domain_nr)
+ if (parent && of_get_pci_domain_nr(parent->of_node) == domain_nr)
ida_free(&pci_domain_nr_static_ida, domain_nr);
else
ida_free(&pci_domain_nr_dynamic_ida, domain_nr);
@@ -6681,7 +6701,9 @@ static int __init pci_setup(char *str)
if (k)
*k++ = 0;
if (*str && (str = pcibios_setup(str)) && *str) {
- if (!strcmp(str, "nomsi")) {
+ if (!pci_setup_cardbus(str)) {
+ /* Function handled the parameters */
+ } else if (!strcmp(str, "nomsi")) {
pci_no_msi();
} else if (!strncmp(str, "noats", 5)) {
pr_info("PCIe: ATS is disabled\n");
@@ -6700,10 +6722,6 @@ static int __init pci_setup(char *str)
pcie_ari_disabled = true;
} else if (!strncmp(str, "notph", 5)) {
pci_no_tph();
- } else if (!strncmp(str, "cbiosize=", 9)) {
- pci_cardbus_io_size = memparse(str + 9, &str);
- } else if (!strncmp(str, "cbmemsize=", 10)) {
- pci_cardbus_mem_size = memparse(str + 10, &str);
} else if (!strncmp(str, "resource_alignment=", 19)) {
resource_alignment_param = str + 19;
} else if (!strncmp(str, "ecrc=", 5)) {
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 0e67014aa001..c8a0522e2e1f 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -5,6 +5,7 @@
#include <linux/align.h>
#include <linux/bitfield.h>
#include <linux/pci.h>
+#include <trace/events/pci.h>
struct pcie_tlp_log;
@@ -63,6 +64,18 @@ struct pcie_tlp_log;
#define PCIE_LINK_WAIT_MAX_RETRIES 10
#define PCIE_LINK_WAIT_SLEEP_MS 90
+/* Format of TLP; PCIe r7.0, sec 2.2.1 */
+#define PCIE_TLP_FMT_3DW_NO_DATA 0x00 /* 3DW header, no data */
+#define PCIE_TLP_FMT_4DW_NO_DATA 0x01 /* 4DW header, no data */
+#define PCIE_TLP_FMT_3DW_DATA 0x02 /* 3DW header, with data */
+#define PCIE_TLP_FMT_4DW_DATA 0x03 /* 4DW header, with data */
+
+/* Type of TLP; PCIe r7.0, sec 2.2.1 */
+#define PCIE_TLP_TYPE_CFG0_RD 0x04 /* Config Type 0 Read Request */
+#define PCIE_TLP_TYPE_CFG0_WR 0x04 /* Config Type 0 Write Request */
+#define PCIE_TLP_TYPE_CFG1_RD 0x05 /* Config Type 1 Read Request */
+#define PCIE_TLP_TYPE_CFG1_WR 0x05 /* Config Type 1 Write Request */
+
/* Message Routing (r[2:0]); PCIe r6.0, sec 2.2.8 */
#define PCIE_MSG_TYPE_R_RC 0
#define PCIE_MSG_TYPE_R_ADDR 1
@@ -84,10 +97,16 @@ struct pcie_tlp_log;
#define PCIE_MSG_CODE_DEASSERT_INTC 0x26
#define PCIE_MSG_CODE_DEASSERT_INTD 0x27
+/* Cpl. status of Complete; PCIe r7.0, sec 2.2.9.1 */
+#define PCIE_CPL_STS_SUCCESS 0x00 /* Successful Completion */
+
#define PCI_BUS_BRIDGE_IO_WINDOW 0
#define PCI_BUS_BRIDGE_MEM_WINDOW 1
#define PCI_BUS_BRIDGE_PREF_MEM_WINDOW 2
+#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
+
extern const unsigned char pcie_link_speed[];
extern bool pci_early_dump;
@@ -103,17 +122,21 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev);
* @read_cfg: Function pointer for reading PCI config space
* @start: Starting position to begin search
* @cap: Capability ID to find
+ * @prev_ptr: Pointer to store position of preceding capability (optional)
* @args: Arguments to pass to read_cfg function
*
- * Search the capability list in PCI config space to find @cap.
+ * Search the capability list in PCI config space to find @cap. If
+ * found, update *prev_ptr with the position of the preceding capability
+ * (if prev_ptr != NULL)
* Implements TTL (time-to-live) protection against infinite loops.
*
* Return: Position of the capability if found, 0 otherwise.
*/
-#define PCI_FIND_NEXT_CAP(read_cfg, start, cap, args...) \
+#define PCI_FIND_NEXT_CAP(read_cfg, start, cap, prev_ptr, args...) \
({ \
int __ttl = PCI_FIND_CAP_TTL; \
- u8 __id, __found_pos = 0; \
+ u8 __id, __found_pos = 0; \
+ u8 __prev_pos = (start); \
u8 __pos = (start); \
u16 __ent; \
\
@@ -132,9 +155,12 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev);
\
if (__id == (cap)) { \
__found_pos = __pos; \
+ if (prev_ptr != NULL) \
+ *(u8 *)prev_ptr = __prev_pos; \
break; \
} \
\
+ __prev_pos = __pos; \
__pos = FIELD_GET(PCI_CAP_LIST_NEXT_MASK, __ent); \
} \
__found_pos; \
@@ -146,21 +172,26 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev);
* @read_cfg: Function pointer for reading PCI config space
* @start: Starting position to begin search (0 for initial search)
* @cap: Extended capability ID to find
+ * @prev_ptr: Pointer to store position of preceding capability (optional)
* @args: Arguments to pass to read_cfg function
*
* Search the extended capability list in PCI config space to find @cap.
+ * If found, update *prev_ptr with the position of the preceding capability
+ * (if prev_ptr != NULL)
* Implements TTL protection against infinite loops using a calculated
* maximum search count.
*
* Return: Position of the capability if found, 0 otherwise.
*/
-#define PCI_FIND_NEXT_EXT_CAP(read_cfg, start, cap, args...) \
+#define PCI_FIND_NEXT_EXT_CAP(read_cfg, start, cap, prev_ptr, args...) \
({ \
u16 __pos = (start) ?: PCI_CFG_SPACE_SIZE; \
u16 __found_pos = 0; \
+ u16 __prev_pos; \
int __ttl, __ret; \
u32 __header; \
\
+ __prev_pos = __pos; \
__ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; \
while (__ttl-- > 0 && __pos >= PCI_CFG_SPACE_SIZE) { \
__ret = read_cfg##_dword(args, __pos, &__header); \
@@ -172,9 +203,12 @@ bool pcie_cap_has_rtctl(const struct pci_dev *dev);
\
if (PCI_EXT_CAP_ID(__header) == (cap) && __pos != start) {\
__found_pos = __pos; \
+ if (prev_ptr != NULL) \
+ *(u16 *)prev_ptr = __prev_pos; \
break; \
} \
\
+ __prev_pos = __pos; \
__pos = PCI_EXT_CAP_NEXT(__header); \
} \
__found_pos; \
@@ -242,6 +276,7 @@ void pci_config_pm_runtime_put(struct pci_dev *dev);
void pci_pm_power_up_and_verify_state(struct pci_dev *pci_dev);
void pci_pm_init(struct pci_dev *dev);
void pci_ea_init(struct pci_dev *dev);
+bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub);
void pci_msi_init(struct pci_dev *dev);
void pci_msix_init(struct pci_dev *dev);
bool pci_bridge_d3_possible(struct pci_dev *dev);
@@ -376,8 +411,40 @@ extern unsigned long pci_hotplug_io_size;
extern unsigned long pci_hotplug_mmio_size;
extern unsigned long pci_hotplug_mmio_pref_size;
extern unsigned long pci_hotplug_bus_size;
-extern unsigned long pci_cardbus_io_size;
-extern unsigned long pci_cardbus_mem_size;
+
+static inline bool pci_is_cardbus_bridge(struct pci_dev *dev)
+{
+ return dev->hdr_type == PCI_HEADER_TYPE_CARDBUS;
+}
+#ifdef CONFIG_CARDBUS
+unsigned long pci_cardbus_resource_alignment(struct resource *res);
+int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
+ struct list_head *realloc_head);
+int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
+ u32 buses, int max,
+ unsigned int available_buses, int pass);
+int pci_setup_cardbus(char *str);
+
+#else
+static inline unsigned long pci_cardbus_resource_alignment(struct resource *res)
+{
+ return 0;
+}
+static inline int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
+ struct list_head *realloc_head)
+{
+ return -EOPNOTSUPP;
+}
+static inline int pci_cardbus_scan_bridge_extend(struct pci_bus *bus,
+ struct pci_dev *dev,
+ u32 buses, int max,
+ unsigned int available_buses,
+ int pass)
+{
+ return max;
+}
+static inline int pci_setup_cardbus(char *str) { return -ENOENT; }
+#endif /* CONFIG_CARDBUS */
/**
* pci_match_one_device - Tell if a PCI device structure has a matching
@@ -432,7 +499,6 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
int rrs_timeout);
bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
int rrs_timeout);
-int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *pl, int rrs_timeout);
int pci_setup_device(struct pci_dev *dev);
void __pci_size_stdbars(struct pci_dev *dev, int count,
@@ -440,6 +506,10 @@ void __pci_size_stdbars(struct pci_dev *dev, int count,
int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
struct resource *res, unsigned int reg, u32 *sizes);
void pci_configure_ari(struct pci_dev *dev);
+
+int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev,
+ struct resource *res, resource_size_t add_size,
+ resource_size_t min_align);
void __pci_bus_size_bridges(struct pci_bus *bus,
struct list_head *realloc_head);
void __pci_bus_assign_resources(const struct pci_bus *bus,
@@ -452,6 +522,11 @@ void pci_walk_bus_locked(struct pci_bus *top,
const char *pci_resource_name(struct pci_dev *dev, unsigned int i);
bool pci_resource_is_optional(const struct pci_dev *dev, int resno);
+static inline bool pci_resource_is_bridge_win(int resno)
+{
+ return resno >= PCI_BRIDGE_RESOURCES &&
+ resno <= PCI_BRIDGE_RESOURCE_END;
+}
/**
* pci_resource_num - Reverse lookup resource number from device resources
@@ -475,6 +550,7 @@ static inline int pci_resource_num(const struct pci_dev *dev,
return resno;
}
+void pbus_validate_busn(struct pci_bus *bus);
struct resource *pbus_select_window(struct pci_bus *bus,
const struct resource *res);
void pci_reassigndev_resource_alignment(struct pci_dev *dev);
@@ -555,12 +631,28 @@ const char *pci_speed_string(enum pci_bus_speed speed);
void __pcie_print_link_status(struct pci_dev *dev, bool verbose);
void pcie_report_downtraining(struct pci_dev *dev);
-static inline void __pcie_update_link_speed(struct pci_bus *bus, u16 linksta, u16 linksta2)
+enum pcie_link_change_reason {
+ PCIE_LINK_RETRAIN,
+ PCIE_ADD_BUS,
+ PCIE_BWCTRL_ENABLE,
+ PCIE_BWCTRL_IRQ,
+ PCIE_HOTPLUG,
+};
+
+static inline void __pcie_update_link_speed(struct pci_bus *bus,
+ enum pcie_link_change_reason reason,
+ u16 linksta, u16 linksta2)
{
bus->cur_bus_speed = pcie_link_speed[linksta & PCI_EXP_LNKSTA_CLS];
bus->flit_mode = (linksta2 & PCI_EXP_LNKSTA2_FLIT) ? 1 : 0;
+
+ trace_pcie_link_event(bus,
+ reason,
+ FIELD_GET(PCI_EXP_LNKSTA_NLW, linksta),
+ linksta & PCI_EXP_LNKSTA_LINK_STATUS_MASK);
}
-void pcie_update_link_speed(struct pci_bus *bus);
+
+void pcie_update_link_speed(struct pci_bus *bus, enum pcie_link_change_reason reason);
/* Single Root I/O Virtualization */
struct pci_sriov {
@@ -924,8 +1016,6 @@ static inline void pci_suspend_ptm(struct pci_dev *dev) { }
static inline void pci_resume_ptm(struct pci_dev *dev) { }
#endif
-unsigned long pci_cardbus_resource_alignment(struct resource *);
-
static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
struct resource *res)
{
@@ -939,10 +1029,12 @@ static inline resource_size_t pci_resource_alignment(struct pci_dev *dev,
}
void pci_acs_init(struct pci_dev *dev);
+void pci_enable_acs(struct pci_dev *dev);
#ifdef CONFIG_PCI_QUIRKS
int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags);
int pci_dev_specific_enable_acs(struct pci_dev *dev);
int pci_dev_specific_disable_acs_redir(struct pci_dev *dev);
+void pci_disable_broken_acs_cap(struct pci_dev *pdev);
int pcie_failed_link_retrain(struct pci_dev *dev);
#else
static inline int pci_dev_specific_acs_enabled(struct pci_dev *dev,
@@ -958,6 +1050,7 @@ static inline int pci_dev_specific_disable_acs_redir(struct pci_dev *dev)
{
return -ENOTTY;
}
+static inline void pci_disable_broken_acs_cap(struct pci_dev *dev) { }
static inline int pcie_failed_link_retrain(struct pci_dev *dev)
{
return -ENOTTY;
diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c
index 71ee4f5064de..2b0ac6bfdd76 100644
--- a/drivers/pci/pcie/aer.c
+++ b/drivers/pci/pcie/aer.c
@@ -239,9 +239,6 @@ void pcie_ecrc_get_policy(char *str)
}
#endif /* CONFIG_PCIE_ECRC */
-#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
- PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
-
int pcie_aer_is_native(struct pci_dev *dev)
{
struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
@@ -1608,6 +1605,20 @@ static void aer_disable_irq(struct pci_dev *pdev)
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_COMMAND, reg32);
}
+static int clear_status_iter(struct pci_dev *dev, void *data)
+{
+ u16 devctl;
+
+ /* Skip if pci_enable_pcie_error_reporting() hasn't been called yet */
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &devctl);
+ if (!(devctl & PCI_EXP_AER_FLAGS))
+ return 0;
+
+ pci_aer_clear_status(dev);
+ pcie_clear_device_status(dev);
+ return 0;
+}
+
/**
* aer_enable_rootport - enable Root Port's interrupts when receiving messages
* @rpc: pointer to a Root Port data structure
@@ -1629,9 +1640,19 @@ static void aer_enable_rootport(struct aer_rpc *rpc)
pcie_capability_clear_word(pdev, PCI_EXP_RTCTL,
SYSTEM_ERROR_INTR_ON_MESG_MASK);
- /* Clear error status */
+ /* Clear error status of this Root Port or RCEC */
pci_read_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, &reg32);
pci_write_config_dword(pdev, aer + PCI_ERR_ROOT_STATUS, reg32);
+
+ /* Clear error status of agents reporting to this Root Port or RCEC */
+ if (reg32 & AER_ERR_STATUS_MASK) {
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_EC)
+ pcie_walk_rcec(pdev, clear_status_iter, NULL);
+ else if (pdev->subordinate)
+ pci_walk_bus(pdev->subordinate, clear_status_iter,
+ NULL);
+ }
+
pci_read_config_dword(pdev, aer + PCI_ERR_COR_STATUS, &reg32);
pci_write_config_dword(pdev, aer + PCI_ERR_COR_STATUS, reg32);
pci_read_config_dword(pdev, aer + PCI_ERR_UNCOR_STATUS, &reg32);
diff --git a/drivers/pci/pcie/bwctrl.c b/drivers/pci/pcie/bwctrl.c
index 36f939f23d34..c4c8d260bf96 100644
--- a/drivers/pci/pcie/bwctrl.c
+++ b/drivers/pci/pcie/bwctrl.c
@@ -199,7 +199,7 @@ static void pcie_bwnotif_enable(struct pcie_device *srv)
* Update after enabling notifications & clearing status bits ensures
* link speed is up to date.
*/
- pcie_update_link_speed(port->subordinate);
+ pcie_update_link_speed(port->subordinate, PCIE_BWCTRL_ENABLE);
}
static void pcie_bwnotif_disable(struct pci_dev *port)
@@ -234,7 +234,7 @@ static irqreturn_t pcie_bwnotif_irq(int irq, void *context)
* speed (inside pcie_update_link_speed()) after LBMS has been
* cleared to avoid missing link speed changes.
*/
- pcie_update_link_speed(port->subordinate);
+ pcie_update_link_speed(port->subordinate, PCIE_BWCTRL_IRQ);
return IRQ_HANDLED;
}
@@ -250,6 +250,9 @@ static int pcie_bwnotif_probe(struct pcie_device *srv)
struct pci_dev *port = srv->port;
int ret;
+ if (port->no_bw_notif)
+ return -ENODEV;
+
/* Can happen if we run out of bus numbers during enumeration. */
if (!port->subordinate)
return -ENODEV;
diff --git a/drivers/pci/pcie/portdrv.c b/drivers/pci/pcie/portdrv.c
index 38a41ccf79b9..88af0dacf351 100644
--- a/drivers/pci/pcie/portdrv.c
+++ b/drivers/pci/pcie/portdrv.c
@@ -508,23 +508,35 @@ static void pcie_port_device_remove(struct pci_dev *dev)
pci_free_irq_vectors(dev);
}
+static int pcie_port_bus_match(struct device *dev, const struct device_driver *drv)
+{
+ struct pcie_device *pciedev = to_pcie_device(dev);
+ const struct pcie_port_service_driver *driver = to_service_driver(drv);
+
+ if (driver->service != pciedev->service)
+ return 0;
+
+ if (driver->port_type != PCIE_ANY_PORT &&
+ driver->port_type != pci_pcie_type(pciedev->port))
+ return 0;
+
+ return 1;
+}
+
/**
- * pcie_port_probe_service - probe driver for given PCI Express port service
+ * pcie_port_bus_probe - probe driver for given PCI Express port service
* @dev: PCI Express port service device to probe against
*
* If PCI Express port service driver is registered with
* pcie_port_service_register(), this function will be called by the driver core
* whenever match is found between the driver and a port service device.
*/
-static int pcie_port_probe_service(struct device *dev)
+static int pcie_port_bus_probe(struct device *dev)
{
struct pcie_device *pciedev;
struct pcie_port_service_driver *driver;
int status;
- if (!dev || !dev->driver)
- return -ENODEV;
-
driver = to_service_driver(dev->driver);
if (!driver || !driver->probe)
return -ENODEV;
@@ -539,7 +551,7 @@ static int pcie_port_probe_service(struct device *dev)
}
/**
- * pcie_port_remove_service - detach driver from given PCI Express port service
+ * pcie_port_bus_remove - detach driver from given PCI Express port service
* @dev: PCI Express port service device to handle
*
* If PCI Express port service driver is registered with
@@ -547,33 +559,25 @@ static int pcie_port_probe_service(struct device *dev)
* when device_unregister() is called for the port service device associated
* with the driver.
*/
-static int pcie_port_remove_service(struct device *dev)
+static void pcie_port_bus_remove(struct device *dev)
{
struct pcie_device *pciedev;
struct pcie_port_service_driver *driver;
- if (!dev || !dev->driver)
- return 0;
-
pciedev = to_pcie_device(dev);
driver = to_service_driver(dev->driver);
- if (driver && driver->remove) {
+ if (driver && driver->remove)
driver->remove(pciedev);
- put_device(dev);
- }
- return 0;
+
+ put_device(dev);
}
-/**
- * pcie_port_shutdown_service - shut down given PCI Express port service
- * @dev: PCI Express port service device to handle
- *
- * If PCI Express port service driver is registered with
- * pcie_port_service_register(), this function will be called by the driver core
- * when device_shutdown() is called for the port service device associated
- * with the driver.
- */
-static void pcie_port_shutdown_service(struct device *dev) {}
+const struct bus_type pcie_port_bus_type = {
+ .name = "pci_express",
+ .match = pcie_port_bus_match,
+ .probe = pcie_port_bus_probe,
+ .remove = pcie_port_bus_remove,
+};
/**
* pcie_port_service_register - register PCI Express port service driver
@@ -586,9 +590,6 @@ int pcie_port_service_register(struct pcie_port_service_driver *new)
new->driver.name = new->name;
new->driver.bus = &pcie_port_bus_type;
- new->driver.probe = pcie_port_probe_service;
- new->driver.remove = pcie_port_remove_service;
- new->driver.shutdown = pcie_port_shutdown_service;
return driver_register(&new->driver);
}
diff --git a/drivers/pci/pcie/ptm.c b/drivers/pci/pcie/ptm.c
index ed0f9691e7d1..c7c61869bc9c 100644
--- a/drivers/pci/pcie/ptm.c
+++ b/drivers/pci/pcie/ptm.c
@@ -542,8 +542,10 @@ struct pci_ptm_debugfs *pcie_ptm_create_debugfs(struct device *dev, void *pdata,
return NULL;
dirname = devm_kasprintf(dev, GFP_KERNEL, "pcie_ptm_%s", dev_name(dev));
- if (!dirname)
+ if (!dirname) {
+ kfree(ptm_debugfs);
return NULL;
+ }
ptm_debugfs->debugfs = debugfs_create_dir(dirname, NULL);
ptm_debugfs->pdata = pdata;
@@ -574,6 +576,7 @@ void pcie_ptm_destroy_debugfs(struct pci_ptm_debugfs *ptm_debugfs)
mutex_destroy(&ptm_debugfs->lock);
debugfs_remove_recursive(ptm_debugfs->debugfs);
+ kfree(ptm_debugfs);
}
EXPORT_SYMBOL_GPL(pcie_ptm_destroy_debugfs);
#endif
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index a2bff57176a3..7711f579fa1d 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/pci_hotplug.h>
#include <linux/slab.h>
+#include <linux/sprintf.h>
#include <linux/module.h>
#include <linux/cpumask.h>
#include <linux/aer.h>
@@ -22,11 +23,9 @@
#include <linux/irqdomain.h>
#include <linux/pm_runtime.h>
#include <linux/bitfield.h>
+#include <trace/events/pci.h>
#include "pci.h"
-#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
-#define CARDBUS_RESERVE_BUSNR 3
-
static struct resource busn_resource = {
.name = "PCI busn",
.start = 0,
@@ -287,8 +286,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
if ((sizeof(pci_bus_addr_t) < 8 || sizeof(resource_size_t) < 8)
&& sz64 > 0x100000000ULL) {
res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED;
- res->start = 0;
- res->end = 0;
+ resource_set_range(res, 0, 0);
pci_err(dev, "%s: can't handle BAR larger than 4GB (size %#010llx)\n",
res_name, (unsigned long long)sz64);
goto out;
@@ -297,8 +295,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
if ((sizeof(pci_bus_addr_t) < 8) && l) {
/* Above 32-bit boundary; try to reallocate */
res->flags |= IORESOURCE_UNSET;
- res->start = 0;
- res->end = sz64 - 1;
+ resource_set_range(res, 0, sz64);
pci_info(dev, "%s: can't handle BAR above 4GB (bus address %#010llx)\n",
res_name, (unsigned long long)l64);
goto out;
@@ -525,8 +522,8 @@ static void pci_read_bridge_windows(struct pci_dev *bridge)
pci_read_config_dword(bridge, PCI_PRIMARY_BUS, &buses);
res.flags = IORESOURCE_BUS;
- res.start = (buses >> 8) & 0xff;
- res.end = (buses >> 16) & 0xff;
+ res.start = FIELD_GET(PCI_SECONDARY_BUS_MASK, buses);
+ res.end = FIELD_GET(PCI_SUBORDINATE_BUS_MASK, buses);
pci_info(bridge, "PCI bridge to %pR%s\n", &res,
bridge->transparent ? " (subtractive decode)" : "");
@@ -824,14 +821,16 @@ const char *pci_speed_string(enum pci_bus_speed speed)
}
EXPORT_SYMBOL_GPL(pci_speed_string);
-void pcie_update_link_speed(struct pci_bus *bus)
+void pcie_update_link_speed(struct pci_bus *bus,
+ enum pcie_link_change_reason reason)
{
struct pci_dev *bridge = bus->self;
u16 linksta, linksta2;
pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta);
pcie_capability_read_word(bridge, PCI_EXP_LNKSTA2, &linksta2);
- __pcie_update_link_speed(bus, linksta, linksta2);
+
+ __pcie_update_link_speed(bus, reason, linksta, linksta2);
}
EXPORT_SYMBOL_GPL(pcie_update_link_speed);
@@ -918,7 +917,7 @@ static void pci_set_bus_speed(struct pci_bus *bus)
pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap);
bus->max_bus_speed = pcie_link_speed[linkcap & PCI_EXP_LNKCAP_SLS];
- pcie_update_link_speed(bus);
+ pcie_update_link_speed(bus, PCIE_ADD_BUS);
}
}
@@ -1313,6 +1312,26 @@ static void pci_enable_rrs_sv(struct pci_dev *pdev)
static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
unsigned int available_buses);
+
+void pbus_validate_busn(struct pci_bus *bus)
+{
+ struct pci_bus *upstream = bus->parent;
+ struct pci_dev *bridge = bus->self;
+
+ /* Check that all devices are accessible */
+ while (upstream->parent) {
+ if ((bus->busn_res.end > upstream->busn_res.end) ||
+ (bus->number > upstream->busn_res.end) ||
+ (bus->number < upstream->number) ||
+ (bus->busn_res.end < upstream->number)) {
+ pci_info(bridge, "devices behind bridge are unusable because %pR cannot be assigned for them\n",
+ &bus->busn_res);
+ break;
+ }
+ upstream = upstream->parent;
+ }
+}
+
/**
* pci_ea_fixed_busnrs() - Read fixed Secondary and Subordinate bus
* numbers from EA capability.
@@ -1324,7 +1343,7 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus,
* and subordinate bus numbers, return true with the bus numbers in @sec
* and @sub. Otherwise return false.
*/
-static bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub)
+bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub)
{
int ea, offset;
u32 dw;
@@ -1378,8 +1397,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
int pass)
{
struct pci_bus *child;
- int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS);
- u32 buses, i, j = 0;
+ u32 buses;
u16 bctl;
u8 primary, secondary, subordinate;
int broken = 0;
@@ -1394,9 +1412,9 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
pm_runtime_get_sync(&dev->dev);
pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
- primary = buses & 0xFF;
- secondary = (buses >> 8) & 0xFF;
- subordinate = (buses >> 16) & 0xFF;
+ primary = FIELD_GET(PCI_PRIMARY_BUS_MASK, buses);
+ secondary = FIELD_GET(PCI_SECONDARY_BUS_MASK, buses);
+ subordinate = FIELD_GET(PCI_SUBORDINATE_BUS_MASK, buses);
pci_dbg(dev, "scanning [bus %02x-%02x] behind bridge, pass %d\n",
secondary, subordinate, pass);
@@ -1423,8 +1441,15 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
- if ((secondary || subordinate) && !pcibios_assign_all_busses() &&
- !is_cardbus && !broken) {
+ if (pci_is_cardbus_bridge(dev)) {
+ max = pci_cardbus_scan_bridge_extend(bus, dev, buses, max,
+ available_buses,
+ pass);
+ goto out;
+ }
+
+ if ((secondary || subordinate) &&
+ !pcibios_assign_all_busses() && !broken) {
unsigned int cmax, buses;
/*
@@ -1466,7 +1491,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
* do in the second pass.
*/
if (!pass) {
- if (pcibios_assign_all_busses() || broken || is_cardbus)
+ if (pcibios_assign_all_busses() || broken)
/*
* Temporarily disable forwarding of the
@@ -1477,7 +1502,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
* ranges.
*/
pci_write_config_dword(dev, PCI_PRIMARY_BUS,
- buses & ~0xffffff);
+ buses & PCI_SEC_LATENCY_TIMER_MASK);
goto out;
}
@@ -1508,59 +1533,16 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
if (available_buses)
available_buses--;
- buses = (buses & 0xff000000)
- | ((unsigned int)(child->primary) << 0)
- | ((unsigned int)(child->busn_res.start) << 8)
- | ((unsigned int)(child->busn_res.end) << 16);
-
- /*
- * yenta.c forces a secondary latency timer of 176.
- * Copy that behaviour here.
- */
- if (is_cardbus) {
- buses &= ~0xff000000;
- buses |= CARDBUS_LATENCY_TIMER << 24;
- }
+ buses = (buses & PCI_SEC_LATENCY_TIMER_MASK) |
+ FIELD_PREP(PCI_PRIMARY_BUS_MASK, child->primary) |
+ FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) |
+ FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end);
/* We need to blast all three values with a single write */
pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
- if (!is_cardbus) {
- child->bridge_ctl = bctl;
- max = pci_scan_child_bus_extend(child, available_buses);
- } else {
-
- /*
- * For CardBus bridges, we leave 4 bus numbers as
- * cards with a PCI-to-PCI bridge can be inserted
- * later.
- */
- for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) {
- struct pci_bus *parent = bus;
- if (pci_find_bus(pci_domain_nr(bus),
- max+i+1))
- break;
- while (parent->parent) {
- if ((!pcibios_assign_all_busses()) &&
- (parent->busn_res.end > max) &&
- (parent->busn_res.end <= max+i)) {
- j = 1;
- }
- parent = parent->parent;
- }
- if (j) {
-
- /*
- * Often, there are two CardBus
- * bridges -- try to leave one
- * valid bus number for each one.
- */
- i /= 2;
- break;
- }
- }
- max += i;
- }
+ child->bridge_ctl = bctl;
+ max = pci_scan_child_bus_extend(child, available_buses);
/*
* Set subordinate bus number to its real value.
@@ -1572,23 +1554,10 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
pci_bus_update_busn_res_end(child, max);
pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
}
+ scnprintf(child->name, sizeof(child->name), "PCI Bus %04x:%02x",
+ pci_domain_nr(bus), child->number);
- sprintf(child->name,
- (is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"),
- pci_domain_nr(bus), child->number);
-
- /* Check that all devices are accessible */
- while (bus->parent) {
- if ((child->busn_res.end > bus->busn_res.end) ||
- (child->number > bus->busn_res.end) ||
- (child->number < bus->number) ||
- (child->busn_res.end < bus->number)) {
- dev_info(&dev->dev, "devices behind bridge are unusable because %pR cannot be assigned for them\n",
- &child->busn_res);
- break;
- }
- bus = bus->parent;
- }
+ pbus_validate_busn(child);
out:
/* Clear errors in the Secondary Status Register */
@@ -2277,7 +2246,8 @@ int pci_configure_extended_tags(struct pci_dev *dev, void *ign)
u16 ctl;
int ret;
- if (!pci_is_pcie(dev))
+ /* PCI_EXP_DEVCTL_EXT_TAG is RsvdP in VFs */
+ if (!pci_is_pcie(dev) || dev->is_virtfn)
return 0;
ret = pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
@@ -2417,6 +2387,37 @@ static void pci_configure_serr(struct pci_dev *dev)
}
}
+static void pci_configure_rcb(struct pci_dev *dev)
+{
+ struct pci_dev *rp;
+ u16 rp_lnkctl;
+
+ /*
+ * Per PCIe r7.0, sec 7.5.3.7, RCB is only meaningful in Root Ports
+ * (where it is read-only), Endpoints, and Bridges. It may only be
+ * set for Endpoints and Bridges if it is set in the Root Port. For
+ * Endpoints, it is 'RsvdP' for Virtual Functions.
+ */
+ if (!pci_is_pcie(dev) ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_UPSTREAM ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC ||
+ dev->is_virtfn)
+ return;
+
+ /* Root Port often not visible to virtualized guests */
+ rp = pcie_find_root_port(dev);
+ if (!rp)
+ return;
+
+ pcie_capability_read_word(rp, PCI_EXP_LNKCTL, &rp_lnkctl);
+ pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_RCB,
+ (rp_lnkctl & PCI_EXP_LNKCTL_RCB) ?
+ PCI_EXP_LNKCTL_RCB : 0);
+}
+
static void pci_configure_device(struct pci_dev *dev)
{
pci_configure_mps(dev);
@@ -2426,6 +2427,7 @@ static void pci_configure_device(struct pci_dev *dev)
pci_configure_aspm_l1ss(dev);
pci_configure_eetlp_prefix(dev);
pci_configure_serr(dev);
+ pci_configure_rcb(dev);
pci_acpi_program_hp_params(dev);
}
@@ -2554,72 +2556,10 @@ bool pci_bus_generic_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
int timeout)
{
-#ifdef CONFIG_PCI_QUIRKS
- struct pci_dev *bridge = bus->self;
-
- /*
- * Certain IDT switches have an issue where they improperly trigger
- * ACS Source Validation errors on completions for config reads.
- */
- if (bridge && bridge->vendor == PCI_VENDOR_ID_IDT &&
- bridge->device == 0x80b5)
- return pci_idt_bus_quirk(bus, devfn, l, timeout);
-#endif
-
return pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
}
EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
-#if IS_ENABLED(CONFIG_PCI_PWRCTRL)
-static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn)
-{
- struct pci_host_bridge *host = pci_find_host_bridge(bus);
- struct platform_device *pdev;
- struct device_node *np;
-
- np = of_pci_find_child_device(dev_of_node(&bus->dev), devfn);
- if (!np)
- return NULL;
-
- pdev = of_find_device_by_node(np);
- if (pdev) {
- put_device(&pdev->dev);
- goto err_put_of_node;
- }
-
- /*
- * First check whether the pwrctrl device really needs to be created or
- * not. This is decided based on at least one of the power supplies
- * being defined in the devicetree node of the device.
- */
- if (!of_pci_supply_present(np)) {
- pr_debug("PCI/pwrctrl: Skipping OF node: %s\n", np->name);
- goto err_put_of_node;
- }
-
- /* Now create the pwrctrl device */
- pdev = of_platform_device_create(np, NULL, &host->dev);
- if (!pdev) {
- pr_err("PCI/pwrctrl: Failed to create pwrctrl device for node: %s\n", np->name);
- goto err_put_of_node;
- }
-
- of_node_put(np);
-
- return pdev;
-
-err_put_of_node:
- of_node_put(np);
-
- return NULL;
-}
-#else
-static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn)
-{
- return NULL;
-}
-#endif
-
/*
* Read the config data for a PCI device, sanity-check it,
* and fill in the dev structure.
@@ -2629,15 +2569,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
struct pci_dev *dev;
u32 l;
- /*
- * Create pwrctrl device (if required) for the PCI device to handle the
- * power state. If the pwrctrl device is created, then skip scanning
- * further as the pwrctrl core will rescan the bus after powering on
- * the device.
- */
- if (pci_pwrctrl_create_device(bus, devfn))
- return NULL;
-
if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
return NULL;
diff --git a/drivers/pci/pwrctrl/Kconfig b/drivers/pci/pwrctrl/Kconfig
index e0f999f299bb..cd3aa15bad00 100644
--- a/drivers/pci/pwrctrl/Kconfig
+++ b/drivers/pci/pwrctrl/Kconfig
@@ -13,6 +13,7 @@ config PCI_PWRCTRL_PWRSEQ
config PCI_PWRCTRL_SLOT
tristate "PCI Power Control driver for PCI slots"
+ select POWER_SEQUENCING
select PCI_PWRCTRL
help
Say Y here to enable the PCI Power Control driver to control the power
diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
index 6bdbfed584d6..6f7dea6746e0 100644
--- a/drivers/pci/pwrctrl/core.c
+++ b/drivers/pci/pwrctrl/core.c
@@ -3,14 +3,22 @@
* Copyright (C) 2024 Linaro Ltd.
*/
+#define dev_fmt(fmt) "pwrctrl: " fmt
+
#include <linux/device.h>
#include <linux/export.h>
#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/pci-pwrctrl.h>
+#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/slab.h>
+#include "../pci.h"
+
static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
void *data)
{
@@ -38,16 +46,6 @@ static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
return NOTIFY_DONE;
}
-static void rescan_work_func(struct work_struct *work)
-{
- struct pci_pwrctrl *pwrctrl = container_of(work,
- struct pci_pwrctrl, work);
-
- pci_lock_rescan_remove();
- pci_rescan_bus(to_pci_host_bridge(pwrctrl->dev->parent)->bus);
- pci_unlock_rescan_remove();
-}
-
/**
* pci_pwrctrl_init() - Initialize the PCI power control context struct
*
@@ -57,7 +55,7 @@ static void rescan_work_func(struct work_struct *work)
void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev)
{
pwrctrl->dev = dev;
- INIT_WORK(&pwrctrl->work, rescan_work_func);
+ dev_set_drvdata(dev, pwrctrl);
}
EXPORT_SYMBOL_GPL(pci_pwrctrl_init);
@@ -87,8 +85,6 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl)
if (ret)
return ret;
- schedule_work(&pwrctrl->work);
-
return 0;
}
EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready);
@@ -101,8 +97,6 @@ EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready);
*/
void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl)
{
- cancel_work_sync(&pwrctrl->work);
-
/*
* We don't have to delete the link here. Typically, this function
* is only called when the power control device is being detached. If
@@ -145,6 +139,242 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
+static int __pci_pwrctrl_power_off_device(struct device *dev)
+{
+ struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev);
+
+ if (!pwrctrl)
+ return 0;
+
+ return pwrctrl->power_off(pwrctrl);
+}
+
+static void pci_pwrctrl_power_off_device(struct device_node *np)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ for_each_available_child_of_node_scoped(np, child)
+ pci_pwrctrl_power_off_device(child);
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+ return;
+
+ if (device_is_bound(&pdev->dev)) {
+ ret = __pci_pwrctrl_power_off_device(&pdev->dev);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to power off device: %d", ret);
+ }
+
+ platform_device_put(pdev);
+}
+
+/**
+ * pci_pwrctrl_power_off_devices - Power off pwrctrl devices
+ *
+ * @parent: PCI host controller device
+ *
+ * Recursively traverse all pwrctrl devices for the devicetree hierarchy
+ * below the specified PCI host controller and power them off in a depth
+ * first manner.
+ */
+void pci_pwrctrl_power_off_devices(struct device *parent)
+{
+ struct device_node *np = parent->of_node;
+
+ for_each_available_child_of_node_scoped(np, child)
+ pci_pwrctrl_power_off_device(child);
+}
+EXPORT_SYMBOL_GPL(pci_pwrctrl_power_off_devices);
+
+static int __pci_pwrctrl_power_on_device(struct device *dev)
+{
+ struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev);
+
+ if (!pwrctrl)
+ return 0;
+
+ return pwrctrl->power_on(pwrctrl);
+}
+
+/*
+ * Power on the devices in a depth first manner. Before powering on the device,
+ * make sure its driver is bound.
+ */
+static int pci_pwrctrl_power_on_device(struct device_node *np)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ for_each_available_child_of_node_scoped(np, child) {
+ ret = pci_pwrctrl_power_on_device(child);
+ if (ret)
+ return ret;
+ }
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+ return 0;
+
+ if (device_is_bound(&pdev->dev)) {
+ ret = __pci_pwrctrl_power_on_device(&pdev->dev);
+ } else {
+ /* FIXME: Use blocking wait instead of probe deferral */
+ dev_dbg(&pdev->dev, "driver is not bound\n");
+ ret = -EPROBE_DEFER;
+ }
+
+ platform_device_put(pdev);
+
+ return ret;
+}
+
+/**
+ * pci_pwrctrl_power_on_devices - Power on pwrctrl devices
+ *
+ * @parent: PCI host controller device
+ *
+ * Recursively traverse all pwrctrl devices for the devicetree hierarchy
+ * below the specified PCI host controller and power them on in a depth
+ * first manner. On error, all powered on devices will be powered off.
+ *
+ * Return: 0 on success, -EPROBE_DEFER if any pwrctrl driver is not bound, an
+ * appropriate error code otherwise.
+ */
+int pci_pwrctrl_power_on_devices(struct device *parent)
+{
+ struct device_node *np = parent->of_node;
+ struct device_node *child = NULL;
+ int ret;
+
+ for_each_available_child_of_node(np, child) {
+ ret = pci_pwrctrl_power_on_device(child);
+ if (ret)
+ goto err_power_off;
+ }
+
+ return 0;
+
+err_power_off:
+ for_each_available_child_of_node_scoped(np, tmp) {
+ if (tmp == child)
+ break;
+ pci_pwrctrl_power_off_device(tmp);
+ }
+ of_node_put(child);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pci_pwrctrl_power_on_devices);
+
+static int pci_pwrctrl_create_device(struct device_node *np,
+ struct device *parent)
+{
+ struct platform_device *pdev;
+ int ret;
+
+ for_each_available_child_of_node_scoped(np, child) {
+ ret = pci_pwrctrl_create_device(child, parent);
+ if (ret)
+ return ret;
+ }
+
+ /* Bail out if the platform device is already available for the node */
+ pdev = of_find_device_by_node(np);
+ if (pdev) {
+ platform_device_put(pdev);
+ return 0;
+ }
+
+ /*
+ * Sanity check to make sure that the node has the compatible property
+ * to allow driver binding.
+ */
+ if (!of_property_present(np, "compatible"))
+ return 0;
+
+ /*
+ * Check whether the pwrctrl device really needs to be created or not.
+ * This is decided based on at least one of the power supplies defined
+ * in the devicetree node of the device or the graph property.
+ */
+ if (!of_pci_supply_present(np) && !of_graph_is_present(np)) {
+ dev_dbg(parent, "Skipping OF node: %s\n", np->name);
+ return 0;
+ }
+
+ /* Now create the pwrctrl device */
+ pdev = of_platform_device_create(np, NULL, parent);
+ if (!pdev) {
+ dev_err(parent, "Failed to create pwrctrl device for node: %s\n", np->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * pci_pwrctrl_create_devices - Create pwrctrl devices
+ *
+ * @parent: PCI host controller device
+ *
+ * Recursively create pwrctrl devices for the devicetree hierarchy below
+ * the specified PCI host controller in a depth first manner. On error, all
+ * created devices will be destroyed.
+ *
+ * Return: 0 on success, negative error number on error.
+ */
+int pci_pwrctrl_create_devices(struct device *parent)
+{
+ int ret;
+
+ for_each_available_child_of_node_scoped(parent->of_node, child) {
+ ret = pci_pwrctrl_create_device(child, parent);
+ if (ret) {
+ pci_pwrctrl_destroy_devices(parent);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_pwrctrl_create_devices);
+
+static void pci_pwrctrl_destroy_device(struct device_node *np)
+{
+ struct platform_device *pdev;
+
+ for_each_available_child_of_node_scoped(np, child)
+ pci_pwrctrl_destroy_device(child);
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev)
+ return;
+
+ of_device_unregister(pdev);
+ platform_device_put(pdev);
+
+ of_node_clear_flag(np, OF_POPULATED);
+}
+
+/**
+ * pci_pwrctrl_destroy_devices - Destroy pwrctrl devices
+ *
+ * @parent: PCI host controller device
+ *
+ * Recursively destroy pwrctrl devices for the devicetree hierarchy below
+ * the specified PCI host controller in a depth first manner.
+ */
+void pci_pwrctrl_destroy_devices(struct device *parent)
+{
+ struct device_node *np = parent->of_node;
+
+ for_each_available_child_of_node_scoped(np, child)
+ pci_pwrctrl_destroy_device(child);
+}
+EXPORT_SYMBOL_GPL(pci_pwrctrl_destroy_devices);
+
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
MODULE_DESCRIPTION("PCI Device Power Control core driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
index 4e664e7b8dd2..0d0377283c37 100644
--- a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
+++ b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
@@ -13,12 +13,12 @@
#include <linux/slab.h>
#include <linux/types.h>
-struct pci_pwrctrl_pwrseq_data {
- struct pci_pwrctrl ctx;
+struct pwrseq_pwrctrl {
+ struct pci_pwrctrl pwrctrl;
struct pwrseq_desc *pwrseq;
};
-struct pci_pwrctrl_pwrseq_pdata {
+struct pwrseq_pwrctrl_pdata {
const char *target;
/*
* Called before doing anything else to perform device-specific
@@ -27,7 +27,7 @@ struct pci_pwrctrl_pwrseq_pdata {
int (*validate_device)(struct device *dev);
};
-static int pci_pwrctrl_pwrseq_qcm_wcn_validate_device(struct device *dev)
+static int pwrseq_pwrctrl_qcm_wcn_validate_device(struct device *dev)
{
/*
* Old device trees for some platforms already define wifi nodes for
@@ -47,22 +47,38 @@ static int pci_pwrctrl_pwrseq_qcm_wcn_validate_device(struct device *dev)
return 0;
}
-static const struct pci_pwrctrl_pwrseq_pdata pci_pwrctrl_pwrseq_qcom_wcn_pdata = {
+static const struct pwrseq_pwrctrl_pdata pwrseq_pwrctrl_qcom_wcn_pdata = {
.target = "wlan",
- .validate_device = pci_pwrctrl_pwrseq_qcm_wcn_validate_device,
+ .validate_device = pwrseq_pwrctrl_qcm_wcn_validate_device,
};
-static void devm_pci_pwrctrl_pwrseq_power_off(void *data)
+static int pwrseq_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
{
- struct pwrseq_desc *pwrseq = data;
+ struct pwrseq_pwrctrl *pwrseq = container_of(pwrctrl,
+ struct pwrseq_pwrctrl, pwrctrl);
- pwrseq_power_off(pwrseq);
+ return pwrseq_power_on(pwrseq->pwrseq);
}
-static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev)
+static int pwrseq_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl)
{
- const struct pci_pwrctrl_pwrseq_pdata *pdata;
- struct pci_pwrctrl_pwrseq_data *data;
+ struct pwrseq_pwrctrl *pwrseq = container_of(pwrctrl,
+ struct pwrseq_pwrctrl, pwrctrl);
+
+ return pwrseq_power_off(pwrseq->pwrseq);
+}
+
+static void devm_pwrseq_pwrctrl_power_off(void *data)
+{
+ struct pwrseq_pwrctrl *pwrseq = data;
+
+ pwrseq_pwrctrl_power_off(&pwrseq->pwrctrl);
+}
+
+static int pwrseq_pwrctrl_probe(struct platform_device *pdev)
+{
+ const struct pwrseq_pwrctrl_pdata *pdata;
+ struct pwrseq_pwrctrl *pwrseq;
struct device *dev = &pdev->dev;
int ret;
@@ -76,28 +92,26 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev)
return ret;
}
- data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
- if (!data)
+ pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
+ if (!pwrseq)
return -ENOMEM;
- data->pwrseq = devm_pwrseq_get(dev, pdata->target);
- if (IS_ERR(data->pwrseq))
- return dev_err_probe(dev, PTR_ERR(data->pwrseq),
+ pwrseq->pwrseq = devm_pwrseq_get(dev, pdata->target);
+ if (IS_ERR(pwrseq->pwrseq))
+ return dev_err_probe(dev, PTR_ERR(pwrseq->pwrseq),
"Failed to get the power sequencer\n");
- ret = pwrseq_power_on(data->pwrseq);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to power-on the device\n");
-
- ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_pwrseq_power_off,
- data->pwrseq);
+ ret = devm_add_action_or_reset(dev, devm_pwrseq_pwrctrl_power_off,
+ pwrseq);
if (ret)
return ret;
- pci_pwrctrl_init(&data->ctx, dev);
+ pwrseq->pwrctrl.power_on = pwrseq_pwrctrl_power_on;
+ pwrseq->pwrctrl.power_off = pwrseq_pwrctrl_power_off;
+
+ pci_pwrctrl_init(&pwrseq->pwrctrl, dev);
- ret = devm_pci_pwrctrl_device_set_ready(dev, &data->ctx);
+ ret = devm_pci_pwrctrl_device_set_ready(dev, &pwrseq->pwrctrl);
if (ret)
return dev_err_probe(dev, ret,
"Failed to register the pwrctrl wrapper\n");
@@ -105,34 +119,34 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id pci_pwrctrl_pwrseq_of_match[] = {
+static const struct of_device_id pwrseq_pwrctrl_of_match[] = {
{
/* ATH11K in QCA6390 package. */
.compatible = "pci17cb,1101",
- .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata,
+ .data = &pwrseq_pwrctrl_qcom_wcn_pdata,
},
{
/* ATH11K in WCN6855 package. */
.compatible = "pci17cb,1103",
- .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata,
+ .data = &pwrseq_pwrctrl_qcom_wcn_pdata,
},
{
/* ATH12K in WCN7850 package. */
.compatible = "pci17cb,1107",
- .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata,
+ .data = &pwrseq_pwrctrl_qcom_wcn_pdata,
},
{ }
};
-MODULE_DEVICE_TABLE(of, pci_pwrctrl_pwrseq_of_match);
+MODULE_DEVICE_TABLE(of, pwrseq_pwrctrl_of_match);
-static struct platform_driver pci_pwrctrl_pwrseq_driver = {
+static struct platform_driver pwrseq_pwrctrl_driver = {
.driver = {
.name = "pci-pwrctrl-pwrseq",
- .of_match_table = pci_pwrctrl_pwrseq_of_match,
+ .of_match_table = pwrseq_pwrctrl_of_match,
},
- .probe = pci_pwrctrl_pwrseq_probe,
+ .probe = pwrseq_pwrctrl_probe,
};
-module_platform_driver(pci_pwrctrl_pwrseq_driver);
+module_platform_driver(pwrseq_pwrctrl_driver);
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
MODULE_DESCRIPTION("Generic PCI Power Control module for power sequenced devices");
diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
index ec423432ac65..488e1ec34a7f 100644
--- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
+++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
@@ -59,7 +59,7 @@
#define TC9563_POWER_CONTROL_OVREN 0x82b2c8
#define TC9563_GPIO_MASK 0xfffffff3
-#define TC9563_GPIO_DEASSERT_BITS 0xc /* Bits to clear for GPIO deassert */
+#define TC9563_GPIO_DEASSERT_BITS 0xc /* Clear to deassert GPIO */
#define TC9563_TX_MARGIN_MIN_UA 400000
@@ -69,7 +69,7 @@
*/
#define TC9563_OSC_STAB_DELAY_US (10 * USEC_PER_MSEC)
-#define TC9563_L0S_L1_DELAY_UNIT_NS 256 /* Each unit represents 256 nanoseconds */
+#define TC9563_L0S_L1_DELAY_UNIT_NS 256 /* Each unit represents 256 ns */
struct tc9563_pwrctrl_reg_setting {
unsigned int offset;
@@ -105,13 +105,13 @@ static const char *const tc9563_supply_names[TC9563_PWRCTL_MAX_SUPPLY] = {
"vddio18",
};
-struct tc9563_pwrctrl_ctx {
+struct tc9563_pwrctrl {
+ struct pci_pwrctrl pwrctrl;
struct regulator_bulk_data supplies[TC9563_PWRCTL_MAX_SUPPLY];
struct tc9563_pwrctrl_cfg cfg[TC9563_MAX];
struct gpio_desc *reset_gpio;
struct i2c_adapter *adapter;
struct i2c_client *client;
- struct pci_pwrctrl pwrctrl;
};
/*
@@ -217,7 +217,8 @@ static int tc9563_pwrctrl_i2c_read(struct i2c_client *client,
}
static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client,
- const struct tc9563_pwrctrl_reg_setting *seq, int len)
+ const struct tc9563_pwrctrl_reg_setting *seq,
+ int len)
{
int ret, i;
@@ -230,10 +231,10 @@ static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client,
return 0;
}
-static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl_ctx *ctx,
+static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
- struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port];
+ struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port];
const struct tc9563_pwrctrl_reg_setting *seq;
int ret, len;
@@ -248,16 +249,17 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl_ctx *ctx,
len = ARRAY_SIZE(dsp2_pwroff_seq);
}
- ret = tc9563_pwrctrl_i2c_bulk_write(ctx->client, seq, len);
+ ret = tc9563_pwrctrl_i2c_bulk_write(tc9563->client, seq, len);
if (ret)
return ret;
- return tc9563_pwrctrl_i2c_bulk_write(ctx->client,
- common_pwroff_seq, ARRAY_SIZE(common_pwroff_seq));
+ return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, common_pwroff_seq,
+ ARRAY_SIZE(common_pwroff_seq));
}
-static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx,
- enum tc9563_pwrctrl_ports port, bool is_l1, u32 ns)
+static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
+ enum tc9563_pwrctrl_ports port,
+ bool is_l1, u32 ns)
{
u32 rd_val, units;
int ret;
@@ -269,30 +271,38 @@ static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx,
units = ns / TC9563_L0S_L1_DELAY_UNIT_NS;
if (port == TC9563_ETHERNET) {
- ret = tc9563_pwrctrl_i2c_read(ctx->client, TC9563_EMBEDDED_ETH_DELAY, &rd_val);
+ ret = tc9563_pwrctrl_i2c_read(tc9563->client,
+ TC9563_EMBEDDED_ETH_DELAY,
+ &rd_val);
if (ret)
return ret;
if (is_l1)
- rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L1_DELAY_MASK);
+ rd_val = u32_replace_bits(rd_val, units,
+ TC9563_ETH_L1_DELAY_MASK);
else
- rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L0S_DELAY_MASK);
+ rd_val = u32_replace_bits(rd_val, units,
+ TC9563_ETH_L0S_DELAY_MASK);
- return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_EMBEDDED_ETH_DELAY, rd_val);
+ return tc9563_pwrctrl_i2c_write(tc9563->client,
+ TC9563_EMBEDDED_ETH_DELAY,
+ rd_val);
}
- ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, BIT(port));
+ ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT,
+ BIT(port));
if (ret)
return ret;
- return tc9563_pwrctrl_i2c_write(ctx->client,
- is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY, units);
+ return tc9563_pwrctrl_i2c_write(tc9563->client,
+ is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY,
+ units);
}
-static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx,
+static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
- u32 amp = ctx->cfg[port].tx_amp;
+ u32 amp = tc9563->cfg[port].tx_amp;
int port_access;
if (amp < TC9563_TX_MARGIN_MIN_UA)
@@ -321,13 +331,14 @@ static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx,
{TC9563_TX_MARGIN, amp},
};
- return tc9563_pwrctrl_i2c_bulk_write(ctx->client, tx_amp_seq, ARRAY_SIZE(tx_amp_seq));
+ return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, tx_amp_seq,
+ ARRAY_SIZE(tx_amp_seq));
}
-static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx,
+static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
- struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port];
+ struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port];
int port_access, lane_access = 0x3;
u32 phy_rate = 0x21;
@@ -364,14 +375,14 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx,
{TC9563_PHY_RATE_CHANGE_OVERRIDE, 0x0},
};
- return tc9563_pwrctrl_i2c_bulk_write(ctx->client,
- disable_dfe_seq, ARRAY_SIZE(disable_dfe_seq));
+ return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, disable_dfe_seq,
+ ARRAY_SIZE(disable_dfe_seq));
}
-static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx,
+static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
- u8 *nfts = ctx->cfg[port].nfts;
+ u8 *nfts = tc9563->cfg[port].nfts;
struct tc9563_pwrctrl_reg_setting nfts_seq[] = {
{TC9563_NFTS_2_5_GT, nfts[0]},
{TC9563_NFTS_5_GT, nfts[1]},
@@ -381,30 +392,35 @@ static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx,
if (!nfts[0])
return 0;
- ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, BIT(port));
+ ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT,
+ BIT(port));
if (ret)
return ret;
- return tc9563_pwrctrl_i2c_bulk_write(ctx->client, nfts_seq, ARRAY_SIZE(nfts_seq));
+ return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, nfts_seq,
+ ARRAY_SIZE(nfts_seq));
}
-static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl_ctx *ctx, bool deassert)
+static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl *tc9563,
+ bool deassert)
{
int ret, val;
- ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_GPIO_CONFIG, TC9563_GPIO_MASK);
+ ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_GPIO_CONFIG,
+ TC9563_GPIO_MASK);
if (ret)
return ret;
val = deassert ? TC9563_GPIO_DEASSERT_BITS : 0;
- return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_RESET_GPIO, val);
+ return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_RESET_GPIO, val);
}
-static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, struct device_node *node,
+static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl *tc9563,
+ struct device_node *node,
enum tc9563_pwrctrl_ports port)
{
- struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port];
+ struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port];
int ret;
/* Disable port if the status of the port is disabled. */
@@ -434,128 +450,137 @@ static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, struct
return 0;
}
-static void tc9563_pwrctrl_power_off(struct tc9563_pwrctrl_ctx *ctx)
+static int tc9563_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl)
{
- gpiod_set_value(ctx->reset_gpio, 1);
+ struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl,
+ struct tc9563_pwrctrl, pwrctrl);
+
+ gpiod_set_value(tc9563->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(tc9563->supplies), tc9563->supplies);
- regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ return 0;
}
-static int tc9563_pwrctrl_bring_up(struct tc9563_pwrctrl_ctx *ctx)
+static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
{
+ struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl,
+ struct tc9563_pwrctrl, pwrctrl);
+ struct device *dev = tc9563->pwrctrl.dev;
struct tc9563_pwrctrl_cfg *cfg;
int ret, i;
- ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ ret = regulator_bulk_enable(ARRAY_SIZE(tc9563->supplies),
+ tc9563->supplies);
if (ret < 0)
- return dev_err_probe(ctx->pwrctrl.dev, ret, "cannot enable regulators\n");
+ return dev_err_probe(dev, ret, "cannot enable regulators\n");
- gpiod_set_value(ctx->reset_gpio, 0);
+ gpiod_set_value(tc9563->reset_gpio, 0);
fsleep(TC9563_OSC_STAB_DELAY_US);
- ret = tc9563_pwrctrl_assert_deassert_reset(ctx, false);
+ ret = tc9563_pwrctrl_assert_deassert_reset(tc9563, false);
if (ret)
goto power_off;
for (i = 0; i < TC9563_MAX; i++) {
- cfg = &ctx->cfg[i];
- ret = tc9563_pwrctrl_disable_port(ctx, i);
+ cfg = &tc9563->cfg[i];
+ ret = tc9563_pwrctrl_disable_port(tc9563, i);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Disabling port failed\n");
+ dev_err(dev, "Disabling port failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, false, cfg->l0s_delay);
+ ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, false, cfg->l0s_delay);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Setting L0s entry delay failed\n");
+ dev_err(dev, "Setting L0s entry delay failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, true, cfg->l1_delay);
+ ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, true, cfg->l1_delay);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Setting L1 entry delay failed\n");
+ dev_err(dev, "Setting L1 entry delay failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_set_tx_amplitude(ctx, i);
+ ret = tc9563_pwrctrl_set_tx_amplitude(tc9563, i);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Setting Tx amplitude failed\n");
+ dev_err(dev, "Setting Tx amplitude failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_set_nfts(ctx, i);
+ ret = tc9563_pwrctrl_set_nfts(tc9563, i);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Setting N_FTS failed\n");
+ dev_err(dev, "Setting N_FTS failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_disable_dfe(ctx, i);
+ ret = tc9563_pwrctrl_disable_dfe(tc9563, i);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Disabling DFE failed\n");
+ dev_err(dev, "Disabling DFE failed\n");
goto power_off;
}
}
- ret = tc9563_pwrctrl_assert_deassert_reset(ctx, true);
+ ret = tc9563_pwrctrl_assert_deassert_reset(tc9563, true);
if (!ret)
return 0;
power_off:
- tc9563_pwrctrl_power_off(ctx);
+ tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
return ret;
}
static int tc9563_pwrctrl_probe(struct platform_device *pdev)
{
- struct pci_host_bridge *bridge = to_pci_host_bridge(pdev->dev.parent);
- struct pci_bus *bus = bridge->bus;
+ struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
enum tc9563_pwrctrl_ports port;
- struct tc9563_pwrctrl_ctx *ctx;
+ struct tc9563_pwrctrl *tc9563;
struct device_node *i2c_node;
int ret, addr;
- ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
+ tc9563 = devm_kzalloc(dev, sizeof(*tc9563), GFP_KERNEL);
+ if (!tc9563)
return -ENOMEM;
- ret = of_property_read_u32_index(pdev->dev.of_node, "i2c-parent", 1, &addr);
+ ret = of_property_read_u32_index(node, "i2c-parent", 1, &addr);
if (ret)
return dev_err_probe(dev, ret, "Failed to read i2c-parent property\n");
i2c_node = of_parse_phandle(dev->of_node, "i2c-parent", 0);
- ctx->adapter = of_find_i2c_adapter_by_node(i2c_node);
+ tc9563->adapter = of_find_i2c_adapter_by_node(i2c_node);
of_node_put(i2c_node);
- if (!ctx->adapter)
+ if (!tc9563->adapter)
return dev_err_probe(dev, -EPROBE_DEFER, "Failed to find I2C adapter\n");
- ctx->client = i2c_new_dummy_device(ctx->adapter, addr);
- if (IS_ERR(ctx->client)) {
+ tc9563->client = i2c_new_dummy_device(tc9563->adapter, addr);
+ if (IS_ERR(tc9563->client)) {
dev_err(dev, "Failed to create I2C client\n");
- i2c_put_adapter(ctx->adapter);
- return PTR_ERR(ctx->client);
+ put_device(&tc9563->adapter->dev);
+ return PTR_ERR(tc9563->client);
}
for (int i = 0; i < ARRAY_SIZE(tc9563_supply_names); i++)
- ctx->supplies[i].supply = tc9563_supply_names[i];
+ tc9563->supplies[i].supply = tc9563_supply_names[i];
- ret = devm_regulator_bulk_get(dev, TC9563_PWRCTL_MAX_SUPPLY, ctx->supplies);
+ ret = devm_regulator_bulk_get(dev, TC9563_PWRCTL_MAX_SUPPLY,
+ tc9563->supplies);
if (ret) {
dev_err_probe(dev, ret, "failed to get supply regulator\n");
goto remove_i2c;
}
- ctx->reset_gpio = devm_gpiod_get(dev, "resx", GPIOD_OUT_HIGH);
- if (IS_ERR(ctx->reset_gpio)) {
- ret = dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "failed to get resx GPIO\n");
+ tc9563->reset_gpio = devm_gpiod_get(dev, "resx", GPIOD_OUT_HIGH);
+ if (IS_ERR(tc9563->reset_gpio)) {
+ ret = dev_err_probe(dev, PTR_ERR(tc9563->reset_gpio), "failed to get resx GPIO\n");
goto remove_i2c;
}
- pci_pwrctrl_init(&ctx->pwrctrl, dev);
+ pci_pwrctrl_init(&tc9563->pwrctrl, dev);
port = TC9563_USP;
- ret = tc9563_pwrctrl_parse_device_dt(ctx, pdev->dev.of_node, port);
+ ret = tc9563_pwrctrl_parse_device_dt(tc9563, node, port);
if (ret) {
dev_err(dev, "failed to parse device tree properties: %d\n", ret);
goto remove_i2c;
@@ -563,18 +588,20 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
/*
* Downstream ports are always children of the upstream port.
- * The first node represents DSP1, the second node represents DSP2, and so on.
+ * The first node represents DSP1, the second node represents DSP2,
+ * and so on.
*/
- for_each_child_of_node_scoped(pdev->dev.of_node, child) {
+ for_each_child_of_node_scoped(node, child) {
port++;
- ret = tc9563_pwrctrl_parse_device_dt(ctx, child, port);
+ ret = tc9563_pwrctrl_parse_device_dt(tc9563, child, port);
if (ret)
break;
/* Embedded ethernet device are under DSP3 */
if (port == TC9563_DSP3) {
for_each_child_of_node_scoped(child, child1) {
port++;
- ret = tc9563_pwrctrl_parse_device_dt(ctx, child1, port);
+ ret = tc9563_pwrctrl_parse_device_dt(tc9563,
+ child1, port);
if (ret)
break;
}
@@ -585,45 +612,32 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
goto remove_i2c;
}
- if (bridge->ops->assert_perst) {
- ret = bridge->ops->assert_perst(bus, true);
- if (ret)
- goto remove_i2c;
- }
+ tc9563->pwrctrl.power_on = tc9563_pwrctrl_power_on;
+ tc9563->pwrctrl.power_off = tc9563_pwrctrl_power_off;
- ret = tc9563_pwrctrl_bring_up(ctx);
- if (ret)
- goto remove_i2c;
-
- if (bridge->ops->assert_perst) {
- ret = bridge->ops->assert_perst(bus, false);
- if (ret)
- goto power_off;
- }
-
- ret = devm_pci_pwrctrl_device_set_ready(dev, &ctx->pwrctrl);
+ ret = devm_pci_pwrctrl_device_set_ready(dev, &tc9563->pwrctrl);
if (ret)
goto power_off;
- platform_set_drvdata(pdev, ctx);
-
return 0;
power_off:
- tc9563_pwrctrl_power_off(ctx);
+ tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
remove_i2c:
- i2c_unregister_device(ctx->client);
- i2c_put_adapter(ctx->adapter);
+ i2c_unregister_device(tc9563->client);
+ put_device(&tc9563->adapter->dev);
return ret;
}
static void tc9563_pwrctrl_remove(struct platform_device *pdev)
{
- struct tc9563_pwrctrl_ctx *ctx = platform_get_drvdata(pdev);
+ struct pci_pwrctrl *pwrctrl = dev_get_drvdata(&pdev->dev);
+ struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl,
+ struct tc9563_pwrctrl, pwrctrl);
- tc9563_pwrctrl_power_off(ctx);
- i2c_unregister_device(ctx->client);
- i2c_put_adapter(ctx->adapter);
+ tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
+ i2c_unregister_device(tc9563->client);
+ put_device(&tc9563->adapter->dev);
}
static const struct of_device_id tc9563_pwrctrl_of_match[] = {
diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c
index 3320494b62d8..082af81efe25 100644
--- a/drivers/pci/pwrctrl/slot.c
+++ b/drivers/pci/pwrctrl/slot.c
@@ -8,36 +8,84 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <linux/pci-pwrctrl.h>
#include <linux/platform_device.h>
+#include <linux/pwrseq/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-struct pci_pwrctrl_slot_data {
- struct pci_pwrctrl ctx;
+struct slot_pwrctrl {
+ struct pci_pwrctrl pwrctrl;
struct regulator_bulk_data *supplies;
int num_supplies;
+ struct clk *clk;
+ struct pwrseq_desc *pwrseq;
};
-static void devm_pci_pwrctrl_slot_power_off(void *data)
+static int slot_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
{
- struct pci_pwrctrl_slot_data *slot = data;
+ struct slot_pwrctrl *slot = container_of(pwrctrl,
+ struct slot_pwrctrl, pwrctrl);
+ int ret;
+
+ if (slot->pwrseq) {
+ pwrseq_power_on(slot->pwrseq);
+ return 0;
+ }
+
+ ret = regulator_bulk_enable(slot->num_supplies, slot->supplies);
+ if (ret < 0) {
+ dev_err(slot->pwrctrl.dev, "Failed to enable slot regulators\n");
+ return ret;
+ }
+
+ return clk_prepare_enable(slot->clk);
+}
+
+static int slot_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl)
+{
+ struct slot_pwrctrl *slot = container_of(pwrctrl,
+ struct slot_pwrctrl, pwrctrl);
+
+ if (slot->pwrseq) {
+ pwrseq_power_off(slot->pwrseq);
+ return 0;
+ }
regulator_bulk_disable(slot->num_supplies, slot->supplies);
+ clk_disable_unprepare(slot->clk);
+
+ return 0;
+}
+
+static void devm_slot_pwrctrl_release(void *data)
+{
+ struct slot_pwrctrl *slot = data;
+
+ slot_pwrctrl_power_off(&slot->pwrctrl);
regulator_bulk_free(slot->num_supplies, slot->supplies);
}
-static int pci_pwrctrl_slot_probe(struct platform_device *pdev)
+static int slot_pwrctrl_probe(struct platform_device *pdev)
{
- struct pci_pwrctrl_slot_data *slot;
+ struct slot_pwrctrl *slot;
struct device *dev = &pdev->dev;
- struct clk *clk;
int ret;
slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL);
if (!slot)
return -ENOMEM;
+ if (of_graph_is_present(dev_of_node(dev))) {
+ slot->pwrseq = devm_pwrseq_get(dev, "pcie");
+ if (IS_ERR(slot->pwrseq))
+ return dev_err_probe(dev, PTR_ERR(slot->pwrseq),
+ "Failed to get the power sequencer\n");
+
+ goto skip_resources;
+ }
+
ret = of_regulator_bulk_get_all(dev, dev_of_node(dev),
&slot->supplies);
if (ret < 0) {
@@ -46,49 +94,46 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev)
}
slot->num_supplies = ret;
- ret = regulator_bulk_enable(slot->num_supplies, slot->supplies);
- if (ret < 0) {
- dev_err_probe(dev, ret, "Failed to enable slot regulators\n");
- regulator_bulk_free(slot->num_supplies, slot->supplies);
- return ret;
+
+ slot->clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(slot->clk)) {
+ return dev_err_probe(dev, PTR_ERR(slot->clk),
+ "Failed to enable slot clock\n");
}
- ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_power_off,
- slot);
+skip_resources:
+ slot->pwrctrl.power_on = slot_pwrctrl_power_on;
+ slot->pwrctrl.power_off = slot_pwrctrl_power_off;
+
+ ret = devm_add_action_or_reset(dev, devm_slot_pwrctrl_release, slot);
if (ret)
return ret;
- clk = devm_clk_get_optional_enabled(dev, NULL);
- if (IS_ERR(clk)) {
- return dev_err_probe(dev, PTR_ERR(clk),
- "Failed to enable slot clock\n");
- }
-
- pci_pwrctrl_init(&slot->ctx, dev);
+ pci_pwrctrl_init(&slot->pwrctrl, dev);
- ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->ctx);
+ ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->pwrctrl);
if (ret)
return dev_err_probe(dev, ret, "Failed to register pwrctrl driver\n");
return 0;
}
-static const struct of_device_id pci_pwrctrl_slot_of_match[] = {
+static const struct of_device_id slot_pwrctrl_of_match[] = {
{
.compatible = "pciclass,0604",
},
{ }
};
-MODULE_DEVICE_TABLE(of, pci_pwrctrl_slot_of_match);
+MODULE_DEVICE_TABLE(of, slot_pwrctrl_of_match);
-static struct platform_driver pci_pwrctrl_slot_driver = {
+static struct platform_driver slot_pwrctrl_driver = {
.driver = {
.name = "pci-pwrctrl-slot",
- .of_match_table = pci_pwrctrl_slot_of_match,
+ .of_match_table = slot_pwrctrl_of_match,
},
- .probe = pci_pwrctrl_slot_probe,
+ .probe = slot_pwrctrl_probe,
};
-module_platform_driver(pci_pwrctrl_slot_driver);
+module_platform_driver(slot_pwrctrl_driver);
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
MODULE_DESCRIPTION("Generic PCI Power Control driver for PCI Slots");
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 6df24dd32754..48946cca4be7 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -1361,6 +1361,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82380FB, quirk
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA, 0x605, quirk_transparent_bridge);
/*
+ * Enabling Link Bandwidth Management Interrupts (BW notifications) can cause
+ * boot hangs on P45.
+ */
+static void quirk_p45_bw_notifications(struct pci_dev *dev)
+{
+ dev->no_bw_notif = 1;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e21, quirk_p45_bw_notifications);
+
+/*
* Common misconfiguration of the MediaGX/Geode PCI master that will reduce
* PCI bandwidth from 70MB/s to 25MB/s. See the GXM/GXLV/GX1 datasheets
* found at http://www.national.com/analog for info on what these bits do.
@@ -3750,6 +3760,14 @@ static void quirk_no_bus_reset(struct pci_dev *dev)
}
/*
+ * After asserting Secondary Bus Reset to downstream devices via a GB10
+ * Root Port, the link may not retrain correctly.
+ * https://lore.kernel.org/r/20251113084441.2124737-1-Johnny-CC.Chang@mediatek.com
+ */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, 0x22CE, quirk_no_bus_reset);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NVIDIA, 0x22D0, quirk_no_bus_reset);
+
+/*
* Some NVIDIA GPU devices do not work with bus reset, SBR needs to be
* prevented for those affected devices.
*/
@@ -3792,6 +3810,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CAVIUM, 0xa100, quirk_no_bus_reset);
*/
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TI, 0xb005, quirk_no_bus_reset);
+/*
+ * Reports from users making use of PCI device assignment with ASM1164
+ * controllers indicate an issue with bus reset where the device fails to
+ * retrain. The issue appears more common in configurations with multiple
+ * controllers. The device does indicate PM reset support (NoSoftRst-),
+ * therefore this still leaves a viable reset method.
+ * https://forum.proxmox.com/threads/problems-with-pcie-passthrough-with-two-identical-devices.149003/
+ */
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASMEDIA, 0x1164, quirk_no_bus_reset);
+
static void quirk_no_pm_reset(struct pci_dev *dev)
{
/*
@@ -4471,6 +4499,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_BROADCOM, 0x9084,
quirk_bridge_cavm_thrx2_pcie_root);
/*
+ * AST1150 doesn't use a real PCI bus and always forwards the requester ID
+ * from downstream devices.
+ */
+static void quirk_aspeed_pci_bridge_no_alias(struct pci_dev *pdev)
+{
+ pdev->dev_flags |= PCI_DEV_FLAGS_PCI_BRIDGE_NO_ALIAS;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASPEED, 0x1150, quirk_aspeed_pci_bridge_no_alias);
+
+/*
* Intersil/Techwell TW686[4589]-based video capture cards have an empty (zero)
* class code. Fix it.
*/
@@ -5124,6 +5162,10 @@ static const struct pci_dev_acs_enabled {
{ PCI_VENDOR_ID_QCOM, 0x0401, pci_quirk_qcom_rp_acs },
/* QCOM SA8775P root port */
{ PCI_VENDOR_ID_QCOM, 0x0115, pci_quirk_qcom_rp_acs },
+ /* QCOM Hamoa root port */
+ { PCI_VENDOR_ID_QCOM, 0x0111, pci_quirk_qcom_rp_acs },
+ /* QCOM Glymur root port */
+ { PCI_VENDOR_ID_QCOM, 0x0120, pci_quirk_qcom_rp_acs },
/* HXT SD4800 root ports. The ACS design is same as QCOM QDF2xxx */
{ PCI_VENDOR_ID_HXT, 0x0401, pci_quirk_qcom_rp_acs },
/* Intel PCH root ports */
@@ -5598,6 +5640,7 @@ static void quirk_no_ext_tags(struct pci_dev *pdev)
pci_walk_bus(bridge->bus, pci_configure_extended_tags, NULL);
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_3WARE, 0x1004, quirk_no_ext_tags);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_3WARE, 0x1005, quirk_no_ext_tags);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0132, quirk_no_ext_tags);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0140, quirk_no_ext_tags);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0141, quirk_no_ext_tags);
@@ -5796,7 +5839,7 @@ DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
/*
* Some IDT switches incorrectly flag an ACS Source Validation error on
- * completions for config read requests even though PCIe r4.0, sec
+ * completions for config read requests even though PCIe r7.0, sec
* 6.12.1.1, says that completions are never affected by ACS Source
* Validation. Here's the text of IDT 89H32H8G3-YC, erratum #36:
*
@@ -5809,44 +5852,20 @@ DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
*
* The workaround suggested by IDT is to issue a config write to the
* downstream device before issuing the first config read. This allows the
- * downstream device to capture its bus and device numbers (see PCIe r4.0,
- * sec 2.2.9), thus avoiding the ACS error on the completion.
+ * downstream device to capture its bus and device numbers (see PCIe r7.0,
+ * sec 2.2.9.1), thus avoiding the ACS error on the completion.
*
* However, we don't know when the device is ready to accept the config
- * write, so we do config reads until we receive a non-Config Request Retry
- * Status, then do the config write.
- *
- * To avoid hitting the erratum when doing the config reads, we disable ACS
- * SV around this process.
+ * write, and the issue affects resets of the switch as well as enumeration,
+ * so disable use of ACS SV for these devices altogether.
*/
-int pci_idt_bus_quirk(struct pci_bus *bus, int devfn, u32 *l, int timeout)
+void pci_disable_broken_acs_cap(struct pci_dev *pdev)
{
- int pos;
- u16 ctrl = 0;
- bool found;
- struct pci_dev *bridge = bus->self;
-
- pos = bridge->acs_cap;
-
- /* Disable ACS SV before initial config reads */
- if (pos) {
- pci_read_config_word(bridge, pos + PCI_ACS_CTRL, &ctrl);
- if (ctrl & PCI_ACS_SV)
- pci_write_config_word(bridge, pos + PCI_ACS_CTRL,
- ctrl & ~PCI_ACS_SV);
+ if (pdev->vendor == PCI_VENDOR_ID_IDT &&
+ (pdev->device == 0x80b5 || pdev->device == 0x8090)) {
+ pci_info(pdev, "Disabling broken ACS SV; downstream device isolation reduced\n");
+ pdev->acs_capabilities &= ~PCI_ACS_SV;
}
-
- found = pci_bus_generic_read_dev_vendor_id(bus, devfn, l, timeout);
-
- /* Write Vendor ID (read-only) so the endpoint latches its bus/dev */
- if (found)
- pci_bus_write_config_word(bus, devfn, PCI_VENDOR_ID, 0);
-
- /* Re-enable ACS_SV if it was previously enabled */
- if (ctrl & PCI_ACS_SV)
- pci_write_config_word(bridge, pos + PCI_ACS_CTRL, ctrl);
-
- return found;
}
/*
@@ -6205,6 +6224,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, 0xb404,
+ pci_fixup_pericom_acs_store_forward);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb404,
+ pci_fixup_pericom_acs_store_forward);
static void nvidia_ion_ahci_fixup(struct pci_dev *pdev)
{
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 417a9ea59117..e9d519993853 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -17,25 +17,6 @@ static void pci_free_resources(struct pci_dev *dev)
}
}
-static void pci_pwrctrl_unregister(struct device *dev)
-{
- struct device_node *np;
- struct platform_device *pdev;
-
- np = dev_of_node(dev);
- if (!np)
- return;
-
- pdev = of_find_device_by_node(np);
- if (!pdev)
- return;
-
- of_device_unregister(pdev);
- put_device(&pdev->dev);
-
- of_node_clear_flag(np, OF_POPULATED);
-}
-
static void pci_stop_dev(struct pci_dev *dev)
{
pci_pme_active(dev, false);
@@ -73,7 +54,6 @@ static void pci_destroy_dev(struct pci_dev *dev)
pci_ide_destroy(dev);
pcie_aspm_exit_link_state(dev);
pci_bridge_d3_update(dev);
- pci_pwrctrl_unregister(&dev->dev);
pci_free_resources(dev);
put_device(&dev->dev);
}
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index e6e84dc62e82..e3d3177fce54 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -86,6 +86,8 @@ int pci_for_each_dma_alias(struct pci_dev *pdev,
case PCI_EXP_TYPE_DOWNSTREAM:
continue;
case PCI_EXP_TYPE_PCI_BRIDGE:
+ if (tmp->dev_flags & PCI_DEV_FLAGS_PCI_BRIDGE_NO_ALIAS)
+ continue;
ret = fn(tmp,
PCI_DEVID(tmp->subordinate->number,
PCI_DEVFN(0, 0)), data);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index a61d38777cdc..32aa72456a44 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -14,10 +14,12 @@
* tighter packing. Prefetchable range support.
*/
+#include <linux/align.h>
#include <linux/bitops.h>
#include <linux/bug.h>
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/errno.h>
@@ -47,7 +49,7 @@ struct pci_dev_resource {
unsigned long flags;
};
-static void free_list(struct list_head *head)
+static void pci_dev_res_free_list(struct list_head *head)
{
struct pci_dev_resource *dev_res, *tmp;
@@ -58,16 +60,16 @@ static void free_list(struct list_head *head)
}
/**
- * add_to_list() - Add a new resource tracker to the list
+ * pci_dev_res_add_to_list() - Add a new resource tracker to the list
* @head: Head of the list
* @dev: Device to which the resource belongs
* @res: Resource to be tracked
* @add_size: Additional size to be optionally added to the resource
* @min_align: Minimum memory window alignment
*/
-static int add_to_list(struct list_head *head, struct pci_dev *dev,
- struct resource *res, resource_size_t add_size,
- resource_size_t min_align)
+int pci_dev_res_add_to_list(struct list_head *head, struct pci_dev *dev,
+ struct resource *res, resource_size_t add_size,
+ resource_size_t min_align)
{
struct pci_dev_resource *tmp;
@@ -88,7 +90,8 @@ static int add_to_list(struct list_head *head, struct pci_dev *dev,
return 0;
}
-static void remove_from_list(struct list_head *head, struct resource *res)
+static void pci_dev_res_remove_from_list(struct list_head *head,
+ struct resource *res)
{
struct pci_dev_resource *dev_res, *tmp;
@@ -123,25 +126,21 @@ static resource_size_t get_res_add_size(struct list_head *head,
return dev_res ? dev_res->add_size : 0;
}
-static resource_size_t get_res_add_align(struct list_head *head,
- struct resource *res)
-{
- struct pci_dev_resource *dev_res;
-
- dev_res = res_to_dev_res(head, res);
- return dev_res ? dev_res->min_align : 0;
-}
-
-static void restore_dev_resource(struct pci_dev_resource *dev_res)
+static void pci_dev_res_restore(struct pci_dev_resource *dev_res)
{
struct resource *res = dev_res->res;
+ struct pci_dev *dev = dev_res->dev;
+ int idx = pci_resource_num(dev, res);
+ const char *res_name = pci_resource_name(dev, idx);
- if (WARN_ON_ONCE(res->parent))
+ if (WARN_ON_ONCE(resource_assigned(res)))
return;
res->start = dev_res->start;
res->end = dev_res->end;
res->flags = dev_res->flags;
+
+ pci_dbg(dev, "%s %pR: resource restored\n", res_name, res);
}
/*
@@ -168,7 +167,7 @@ static struct resource *find_bus_resource_of_type(struct pci_bus *bus,
if ((r->flags & type_mask) != type)
continue;
- if (!r->parent)
+ if (!resource_assigned(r))
return r;
if (!r_assigned)
r_assigned = r;
@@ -271,7 +270,7 @@ static struct resource *pbus_select_window_for_type(struct pci_bus *bus,
struct resource *pbus_select_window(struct pci_bus *bus,
const struct resource *res)
{
- if (res->parent)
+ if (resource_assigned(res))
return res->parent;
return pbus_select_window_for_type(bus, res->flags);
@@ -302,8 +301,7 @@ static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res)
if (!res->flags)
return false;
- if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END &&
- res->flags & IORESOURCE_DISABLED)
+ if (pci_resource_is_bridge_win(idx) && res->flags & IORESOURCE_DISABLED)
return false;
return true;
@@ -311,7 +309,7 @@ static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res)
static bool pdev_resource_should_fit(struct pci_dev *dev, struct resource *res)
{
- if (res->parent)
+ if (resource_assigned(res))
return false;
if (res->flags & IORESOURCE_PCI_FIXED)
@@ -380,19 +378,24 @@ bool pci_resource_is_optional(const struct pci_dev *dev, int resno)
return true;
if (resno == PCI_ROM_RESOURCE && !(res->flags & IORESOURCE_ROM_ENABLE))
return true;
+ if (pci_resource_is_bridge_win(resno) && !resource_size(res))
+ return true;
return false;
}
-static inline void reset_resource(struct pci_dev *dev, struct resource *res)
+static void reset_resource(struct pci_dev *dev, struct resource *res)
{
int idx = pci_resource_num(dev, res);
+ const char *res_name = pci_resource_name(dev, idx);
- if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END) {
+ if (pci_resource_is_bridge_win(idx)) {
res->flags |= IORESOURCE_UNSET;
return;
}
+ pci_dbg(dev, "%s %pR: resetting resource\n", res_name, res);
+
res->start = 0;
res->end = 0;
res->flags = 0;
@@ -413,7 +416,6 @@ static void reassign_resources_sorted(struct list_head *realloc_head,
struct list_head *head)
{
struct pci_dev_resource *add_res, *tmp;
- struct pci_dev_resource *dev_res;
struct pci_dev *dev;
struct resource *res;
const char *res_name;
@@ -421,8 +423,6 @@ static void reassign_resources_sorted(struct list_head *realloc_head,
int idx;
list_for_each_entry_safe(add_res, tmp, realloc_head, list) {
- bool found_match = false;
-
res = add_res->res;
dev = add_res->dev;
idx = pci_resource_num(dev, res);
@@ -431,24 +431,18 @@ static void reassign_resources_sorted(struct list_head *realloc_head,
* Skip resource that failed the earlier assignment and is
* not optional as it would just fail again.
*/
- if (!res->parent && resource_size(res) &&
+ if (!resource_assigned(res) && resource_size(res) &&
!pci_resource_is_optional(dev, idx))
goto out;
/* Skip this resource if not found in head list */
- list_for_each_entry(dev_res, head, list) {
- if (dev_res->res == res) {
- found_match = true;
- break;
- }
- }
- if (!found_match) /* Just skip */
+ if (!res_to_dev_res(head, res))
continue;
res_name = pci_resource_name(dev, idx);
add_size = add_res->add_size;
align = add_res->min_align;
- if (!res->parent) {
+ if (!resource_assigned(res)) {
resource_set_range(res, align,
resource_size(res) + add_size);
if (pci_assign_resource(dev, idx)) {
@@ -456,7 +450,7 @@ static void reassign_resources_sorted(struct list_head *realloc_head,
"%s %pR: ignoring failure in optional allocation\n",
res_name, res);
}
- } else if (add_size > 0) {
+ } else if (add_size > 0 || !IS_ALIGNED(res->start, align)) {
res->flags |= add_res->flags &
(IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);
if (pci_reassign_resource(dev, idx, add_size, align))
@@ -505,9 +499,9 @@ static void assign_requested_resources_sorted(struct list_head *head,
if (pci_assign_resource(dev, idx)) {
if (fail_head) {
- add_to_list(fail_head, dev, res,
- 0 /* don't care */,
- 0 /* don't care */);
+ pci_dev_res_add_to_list(fail_head, dev, res,
+ 0 /* don't care */,
+ 0 /* don't care */);
}
}
}
@@ -604,11 +598,11 @@ static void __assign_resources_sorted(struct list_head *head,
LIST_HEAD(local_fail_head);
LIST_HEAD(dummy_head);
struct pci_dev_resource *save_res;
- struct pci_dev_resource *dev_res, *tmp_res, *dev_res2;
+ struct pci_dev_resource *dev_res, *tmp_res, *dev_res2, *addsize_res;
struct resource *res;
struct pci_dev *dev;
unsigned long fail_type;
- resource_size_t add_align, align;
+ resource_size_t align;
if (!realloc_head)
realloc_head = &dummy_head;
@@ -619,8 +613,9 @@ static void __assign_resources_sorted(struct list_head *head,
/* Save original start, end, flags etc at first */
list_for_each_entry(dev_res, head, list) {
- if (add_to_list(&save_head, dev_res->dev, dev_res->res, 0, 0)) {
- free_list(&save_head);
+ if (pci_dev_res_add_to_list(&save_head, dev_res->dev,
+ dev_res->res, 0, 0)) {
+ pci_dev_res_free_list(&save_head);
goto assign;
}
}
@@ -629,8 +624,11 @@ static void __assign_resources_sorted(struct list_head *head,
list_for_each_entry_safe(dev_res, tmp_res, head, list) {
res = dev_res->res;
- res->end += get_res_add_size(realloc_head, res);
+ addsize_res = res_to_dev_res(realloc_head, res);
+ if (!addsize_res)
+ continue;
+ res->end += addsize_res->add_size;
/*
* There are two kinds of additional resources in the list:
* 1. bridge resource -- IORESOURCE_STARTALIGN
@@ -640,8 +638,8 @@ static void __assign_resources_sorted(struct list_head *head,
if (!(res->flags & IORESOURCE_STARTALIGN))
continue;
- add_align = get_res_add_align(realloc_head, res);
-
+ if (addsize_res->min_align <= res->start)
+ continue;
/*
* The "head" list is sorted by alignment so resources with
* bigger alignment will be assigned first. After we
@@ -649,17 +647,15 @@ static void __assign_resources_sorted(struct list_head *head,
* need to reorder the list by alignment to make it
* consistent.
*/
- if (add_align > res->start) {
- resource_set_range(res, add_align, resource_size(res));
-
- list_for_each_entry(dev_res2, head, list) {
- align = pci_resource_alignment(dev_res2->dev,
- dev_res2->res);
- if (add_align > align) {
- list_move_tail(&dev_res->list,
- &dev_res2->list);
- break;
- }
+ resource_set_range(res, addsize_res->min_align,
+ resource_size(res));
+
+ list_for_each_entry(dev_res2, head, list) {
+ align = pci_resource_alignment(dev_res2->dev,
+ dev_res2->res);
+ if (addsize_res->min_align > align) {
+ list_move_tail(&dev_res->list, &dev_res2->list);
+ break;
}
}
@@ -672,8 +668,9 @@ assign:
if (list_empty(&local_fail_head)) {
/* Remove head list from realloc_head list */
list_for_each_entry(dev_res, head, list)
- remove_from_list(realloc_head, dev_res->res);
- free_list(&save_head);
+ pci_dev_res_remove_from_list(realloc_head,
+ dev_res->res);
+ pci_dev_res_free_list(&save_head);
goto out;
}
@@ -683,13 +680,13 @@ assign:
list_for_each_entry(save_res, &save_head, list) {
struct resource *res = save_res->res;
- if (res->parent)
+ if (resource_assigned(res))
continue;
- restore_dev_resource(save_res);
+ pci_dev_res_restore(save_res);
}
- free_list(&local_fail_head);
- free_list(&save_head);
+ pci_dev_res_free_list(&local_fail_head);
+ pci_dev_res_free_list(&save_head);
goto out;
}
@@ -699,28 +696,29 @@ assign:
list_for_each_entry_safe(dev_res, tmp_res, head, list) {
res = dev_res->res;
- if (res->parent && !pci_need_to_release(fail_type, res)) {
+ if (resource_assigned(res) &&
+ !pci_need_to_release(fail_type, res)) {
/* Remove it from realloc_head list */
- remove_from_list(realloc_head, res);
- remove_from_list(&save_head, res);
+ pci_dev_res_remove_from_list(realloc_head, res);
+ pci_dev_res_remove_from_list(&save_head, res);
list_del(&dev_res->list);
kfree(dev_res);
}
}
- free_list(&local_fail_head);
+ pci_dev_res_free_list(&local_fail_head);
/* Release assigned resource */
list_for_each_entry(dev_res, head, list) {
res = dev_res->res;
dev = dev_res->dev;
pci_release_resource(dev, pci_resource_num(dev, res));
- restore_dev_resource(dev_res);
+ pci_dev_res_restore(dev_res);
}
/* Restore start/end/flags from saved list */
list_for_each_entry(save_res, &save_head, list)
- restore_dev_resource(save_res);
- free_list(&save_head);
+ pci_dev_res_restore(save_res);
+ pci_dev_res_free_list(&save_head);
/* Satisfy the must-have resource requests */
assign_requested_resources_sorted(head, NULL, false);
@@ -735,19 +733,19 @@ out:
res = dev_res->res;
dev = dev_res->dev;
- if (res->parent)
+ if (resource_assigned(res))
continue;
if (fail_head) {
- add_to_list(fail_head, dev, res,
- 0 /* don't care */,
- 0 /* don't care */);
+ pci_dev_res_add_to_list(fail_head, dev, res,
+ 0 /* don't care */,
+ 0 /* don't care */);
}
reset_resource(dev, res);
}
- free_list(head);
+ pci_dev_res_free_list(head);
}
static void pdev_assign_resources_sorted(struct pci_dev *dev,
@@ -774,61 +772,6 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus,
__assign_resources_sorted(&head, realloc_head, fail_head);
}
-void pci_setup_cardbus(struct pci_bus *bus)
-{
- struct pci_dev *bridge = bus->self;
- struct resource *res;
- struct pci_bus_region region;
-
- pci_info(bridge, "CardBus bridge to %pR\n",
- &bus->busn_res);
-
- res = bus->resource[0];
- pcibios_resource_to_bus(bridge->bus, &region, res);
- if (res->parent && res->flags & IORESOURCE_IO) {
- /*
- * The IO resource is allocated a range twice as large as it
- * would normally need. This allows us to set both IO regs.
- */
- pci_info(bridge, " bridge window %pR\n", res);
- pci_write_config_dword(bridge, PCI_CB_IO_BASE_0,
- region.start);
- pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0,
- region.end);
- }
-
- res = bus->resource[1];
- pcibios_resource_to_bus(bridge->bus, &region, res);
- if (res->parent && res->flags & IORESOURCE_IO) {
- pci_info(bridge, " bridge window %pR\n", res);
- pci_write_config_dword(bridge, PCI_CB_IO_BASE_1,
- region.start);
- pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1,
- region.end);
- }
-
- res = bus->resource[2];
- pcibios_resource_to_bus(bridge->bus, &region, res);
- if (res->parent && res->flags & IORESOURCE_MEM) {
- pci_info(bridge, " bridge window %pR\n", res);
- pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0,
- region.start);
- pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0,
- region.end);
- }
-
- res = bus->resource[3];
- pcibios_resource_to_bus(bridge->bus, &region, res);
- if (res->parent && res->flags & IORESOURCE_MEM) {
- pci_info(bridge, " bridge window %pR\n", res);
- pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1,
- region.start);
- pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1,
- region.end);
- }
-}
-EXPORT_SYMBOL(pci_setup_cardbus);
-
/*
* Initialize bridges with base/limit values we have collected. PCI-to-PCI
* Bridge Architecture Specification rev. 1.1 (1998) requires that if there
@@ -860,7 +803,7 @@ static void pci_setup_bridge_io(struct pci_dev *bridge)
res = &bridge->resource[PCI_BRIDGE_IO_WINDOW];
res_name = pci_resource_name(bridge, PCI_BRIDGE_IO_WINDOW);
pcibios_resource_to_bus(bridge->bus, &region, res);
- if (res->parent && res->flags & IORESOURCE_IO) {
+ if (resource_assigned(res) && res->flags & IORESOURCE_IO) {
pci_read_config_word(bridge, PCI_IO_BASE, &l);
io_base_lo = (region.start >> 8) & io_mask;
io_limit_lo = (region.end >> 8) & io_mask;
@@ -892,7 +835,7 @@ static void pci_setup_bridge_mmio(struct pci_dev *bridge)
res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW];
res_name = pci_resource_name(bridge, PCI_BRIDGE_MEM_WINDOW);
pcibios_resource_to_bus(bridge->bus, &region, res);
- if (res->parent && res->flags & IORESOURCE_MEM) {
+ if (resource_assigned(res) && res->flags & IORESOURCE_MEM) {
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
pci_info(bridge, " %s %pR\n", res_name, res);
@@ -921,7 +864,7 @@ static void pci_setup_bridge_mmio_pref(struct pci_dev *bridge)
res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW];
res_name = pci_resource_name(bridge, PCI_BRIDGE_PREF_MEM_WINDOW);
pcibios_resource_to_bus(bridge->bus, &region, res);
- if (res->parent && res->flags & IORESOURCE_PREFETCH) {
+ if (resource_assigned(res) && res->flags & IORESOURCE_PREFETCH) {
l = (region.start >> 16) & 0xfff0;
l |= region.end & 0xfff00000;
if (res->flags & IORESOURCE_MEM_64) {
@@ -992,7 +935,7 @@ int pci_claim_bridge_resource(struct pci_dev *bridge, int i)
{
int ret = -EINVAL;
- if (i < PCI_BRIDGE_RESOURCES || i > PCI_BRIDGE_RESOURCE_END)
+ if (!pci_resource_is_bridge_win(i))
return 0;
if (pci_claim_resource(bridge, i) == 0)
@@ -1068,18 +1011,11 @@ static resource_size_t calculate_iosize(resource_size_t size,
static resource_size_t calculate_memsize(resource_size_t size,
resource_size_t min_size,
- resource_size_t add_size,
resource_size_t children_add_size,
- resource_size_t old_size,
resource_size_t align)
{
- if (size < min_size)
- size = min_size;
- if (old_size == 1)
- old_size = 0;
-
- size = max(size, add_size) + children_add_size;
- return ALIGN(max(size, old_size), align);
+ size = max(size, min_size) + children_add_size;
+ return ALIGN(size, align);
}
resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus,
@@ -1117,8 +1053,7 @@ static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type)
* pbus_size_io() - Size the I/O window of a given bus
*
* @bus: The bus
- * @min_size: The minimum I/O window that must be allocated
- * @add_size: Additional optional I/O window
+ * @add_size: Additional I/O window
* @realloc_head: Track the additional I/O window on this list
*
* Sizing the I/O windows of the PCI-PCI bridge is trivial, since these
@@ -1126,8 +1061,7 @@ static resource_size_t window_alignment(struct pci_bus *bus, unsigned long type)
* devices are limited to 256 bytes. We must be careful with the ISA
* aliasing though.
*/
-static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
- resource_size_t add_size,
+static void pbus_size_io(struct pci_bus *bus, resource_size_t add_size,
struct list_head *realloc_head)
{
struct pci_dev *dev;
@@ -1140,7 +1074,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
return;
/* If resource is already assigned, nothing more to do */
- if (b_res->parent)
+ if (resource_assigned(b_res))
return;
min_align = window_alignment(bus, IORESOURCE_IO);
@@ -1150,7 +1084,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
pci_dev_for_each_resource(dev, r) {
unsigned long r_size;
- if (r->parent || !(r->flags & IORESOURCE_IO))
+ if (resource_assigned(r) || !(r->flags & IORESOURCE_IO))
continue;
if (!pdev_resource_assignable(dev, r))
@@ -1172,7 +1106,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
}
}
- size0 = calculate_iosize(size, min_size, size1, 0, 0,
+ size0 = calculate_iosize(size, realloc_head ? 0 : add_size, size1, 0, 0,
resource_size(b_res), min_align);
if (size0)
@@ -1180,7 +1114,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
size1 = size0;
if (realloc_head && (add_size > 0 || children_add_size > 0)) {
- size1 = calculate_iosize(size, min_size, size1, add_size,
+ size1 = calculate_iosize(size, 0, size1, add_size,
children_add_size, resource_size(b_res),
min_align);
}
@@ -1197,8 +1131,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
b_res->flags |= IORESOURCE_STARTALIGN;
if (bus->self && size1 > size0 && realloc_head) {
b_res->flags &= ~IORESOURCE_DISABLED;
- add_to_list(realloc_head, bus->self, b_res, size1-size0,
- min_align);
+ pci_dev_res_add_to_list(realloc_head, bus->self, b_res,
+ size1 - size0, min_align);
pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n",
b_res, &bus->busn_res,
(unsigned long long) size1 - size0);
@@ -1227,65 +1161,90 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns,
return min_align;
}
-/**
- * pbus_upstream_space_available - Check no upstream resource limits allocation
- * @bus: The bus
- * @res: The resource to help select the correct bridge window
- * @size: The size required from the bridge window
- * @align: Required alignment for the resource
- *
- * Check that @size can fit inside the upstream bridge resources that are
- * already assigned. Select the upstream bridge window based on the type of
- * @res.
- *
- * Return: %true if enough space is available on all assigned upstream
+/*
+ * Calculate bridge window head alignment that leaves no gaps in between
* resources.
*/
-static bool pbus_upstream_space_available(struct pci_bus *bus,
- struct resource *res,
- resource_size_t size,
- resource_size_t align)
-{
- struct resource_constraint constraint = {
- .max = RESOURCE_SIZE_MAX,
- .align = align,
- };
- struct pci_bus *downstream = bus;
-
- while ((bus = bus->parent)) {
- if (pci_is_root_bus(bus))
- break;
+static resource_size_t calculate_head_align(resource_size_t *aligns,
+ int max_order)
+{
+ resource_size_t head_align = 1;
+ resource_size_t remainder = 0;
+ int order;
- res = pbus_select_window(bus, res);
- if (!res)
- return false;
- if (!res->parent)
- continue;
+ /* Take the largest alignment as the starting point. */
+ head_align <<= max_order + __ffs(SZ_1M);
- if (resource_size(res) >= size) {
- struct resource gap = {};
+ for (order = max_order - 1; order >= 0; order--) {
+ resource_size_t align1 = 1;
- if (find_resource_space(res, &gap, size, &constraint) == 0) {
- gap.flags = res->flags;
- pci_dbg(bus->self,
- "Assigned bridge window %pR to %pR free space at %pR\n",
- res, &bus->busn_res, &gap);
- return true;
- }
+ align1 <<= order + __ffs(SZ_1M);
+
+ /*
+ * Account smaller resources with alignment < max_order that
+ * could be used to fill head room if alignment less than
+ * max_order is used.
+ */
+ remainder += aligns[order];
+
+ /*
+ * Test if head fill is enough to satisfy the alignment of
+ * the larger resources after reducing the alignment.
+ */
+ while ((head_align > align1) && (remainder >= head_align / 2)) {
+ head_align /= 2;
+ remainder -= head_align;
}
+ }
+
+ return head_align;
+}
+
+/*
+ * pbus_size_mem_optional - Account optional resources in bridge window
+ *
+ * Account an optional resource or the optional part of the resource in bridge
+ * window size.
+ *
+ * Return: %true if the resource is entirely optional.
+ */
+static bool pbus_size_mem_optional(struct pci_dev *dev, int resno,
+ resource_size_t align,
+ struct list_head *realloc_head,
+ resource_size_t *add_align,
+ resource_size_t *children_add_size)
+{
+ struct resource *res = pci_resource_n(dev, resno);
+ bool optional = pci_resource_is_optional(dev, resno);
+ resource_size_t r_size = resource_size(res);
+ struct pci_dev_resource *dev_res;
- if (bus->self) {
- pci_info(bus->self,
- "Assigned bridge window %pR to %pR cannot fit 0x%llx required for %s bridging to %pR\n",
- res, &bus->busn_res,
- (unsigned long long)size,
- pci_name(downstream->self),
- &downstream->busn_res);
+ if (!realloc_head)
+ return false;
+
+ if (!optional) {
+ /*
+ * Only bridges have optional sizes in realloc_head at this
+ * point. As res_to_dev_res() walks the entire realloc_head
+ * list, skip calling it when known unnecessary.
+ */
+ if (!pci_resource_is_bridge_win(resno))
+ return false;
+
+ dev_res = res_to_dev_res(realloc_head, res);
+ if (dev_res) {
+ *children_add_size += dev_res->add_size;
+ *add_align = max(*add_align, dev_res->min_align);
}
return false;
}
+ /* Put SRIOV requested res to the optional list */
+ pci_dev_res_add_to_list(realloc_head, dev, res, 0, align);
+ *children_add_size += r_size;
+ *add_align = max(align, *add_align);
+
return true;
}
@@ -1293,12 +1252,11 @@ static bool pbus_upstream_space_available(struct pci_bus *bus,
* pbus_size_mem() - Size the memory window of a given bus
*
* @bus: The bus
- * @type: The type of bridge resource
- * @min_size: The minimum memory window that must be allocated
- * @add_size: Additional optional memory window
+ * @b_res: The bridge window resource
+ * @add_size: Additional memory window
* @realloc_head: Track the additional memory window on this list
*
- * Calculate the size of the bus resource for @type and minimal alignment
+ * Calculate the size of the bridge window @b_res and minimal alignment
* which guarantees that all child resources fit in this size.
*
* Set the bus resource start/end to indicate the required size if there an
@@ -1307,30 +1265,24 @@ static bool pbus_upstream_space_available(struct pci_bus *bus,
* Add optional resource requests to the @realloc_head list if it is
* supplied.
*/
-static void pbus_size_mem(struct pci_bus *bus, unsigned long type,
- resource_size_t min_size,
- resource_size_t add_size,
- struct list_head *realloc_head)
+static void pbus_size_mem(struct pci_bus *bus, struct resource *b_res,
+ resource_size_t add_size,
+ struct list_head *realloc_head)
{
struct pci_dev *dev;
resource_size_t min_align, win_align, align, size, size0, size1 = 0;
- resource_size_t aligns[28]; /* Alignments from 1MB to 128TB */
+ resource_size_t aligns[28] = {}; /* Alignments from 1MB to 128TB */
int order, max_order;
- struct resource *b_res = pbus_select_window_for_type(bus, type);
resource_size_t children_add_size = 0;
- resource_size_t children_add_align = 0;
resource_size_t add_align = 0;
- resource_size_t relaxed_align;
- resource_size_t old_size;
if (!b_res)
return;
/* If resource is already assigned, nothing more to do */
- if (b_res->parent)
+ if (resource_assigned(b_res))
return;
- memset(aligns, 0, sizeof(aligns));
max_order = 0;
size = 0;
@@ -1348,88 +1300,50 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,
if (b_res != pbus_select_window(bus, r))
continue;
- r_size = resource_size(r);
-
- /* Put SRIOV requested res to the optional list */
- if (realloc_head && pci_resource_is_optional(dev, i)) {
- add_align = max(pci_resource_alignment(dev, r), add_align);
- add_to_list(realloc_head, dev, r, 0, 0 /* Don't care */);
- children_add_size += r_size;
- continue;
- }
-
+ align = pci_resource_alignment(dev, r);
/*
* aligns[0] is for 1MB (since bridge memory
* windows are always at least 1MB aligned), so
* keep "order" from being negative for smaller
* resources.
*/
- align = pci_resource_alignment(dev, r);
- order = __ffs(align) - __ffs(SZ_1M);
- if (order < 0)
- order = 0;
+ order = max_t(int, __ffs(align) - __ffs(SZ_1M), 0);
if (order >= ARRAY_SIZE(aligns)) {
pci_warn(dev, "%s %pR: disabling; bad alignment %#llx\n",
r_name, r, (unsigned long long) align);
r->flags = 0;
continue;
}
+
+ if (pbus_size_mem_optional(dev, i, align,
+ realloc_head, &add_align,
+ &children_add_size))
+ continue;
+
+ r_size = resource_size(r);
size += max(r_size, align);
- /*
- * Exclude ranges with size > align from calculation of
- * the alignment.
- */
- if (r_size <= align)
- aligns[order] += align;
+
+ aligns[order] += align;
if (order > max_order)
max_order = order;
-
- if (realloc_head) {
- children_add_size += get_res_add_size(realloc_head, r);
- children_add_align = get_res_add_align(realloc_head, r);
- add_align = max(add_align, children_add_align);
- }
}
}
- old_size = resource_size(b_res);
win_align = window_alignment(bus, b_res->flags);
- min_align = calculate_mem_align(aligns, max_order);
+ min_align = calculate_head_align(aligns, max_order);
min_align = max(min_align, win_align);
- size0 = calculate_memsize(size, min_size, 0, 0, old_size, min_align);
+ size0 = calculate_memsize(size, realloc_head ? 0 : add_size,
+ 0, win_align);
if (size0) {
resource_set_range(b_res, min_align, size0);
b_res->flags &= ~IORESOURCE_DISABLED;
}
- if (bus->self && size0 &&
- !pbus_upstream_space_available(bus, b_res, size0, min_align)) {
- relaxed_align = 1ULL << (max_order + __ffs(SZ_1M));
- relaxed_align = max(relaxed_align, win_align);
- min_align = min(min_align, relaxed_align);
- size0 = calculate_memsize(size, min_size, 0, 0, old_size, win_align);
- resource_set_range(b_res, min_align, size0);
- pci_info(bus->self, "bridge window %pR to %pR requires relaxed alignment rules\n",
- b_res, &bus->busn_res);
- }
-
if (realloc_head && (add_size > 0 || children_add_size > 0)) {
add_align = max(min_align, add_align);
- size1 = calculate_memsize(size, min_size, add_size, children_add_size,
- old_size, add_align);
-
- if (bus->self && size1 &&
- !pbus_upstream_space_available(bus, b_res, size1, add_align)) {
- relaxed_align = 1ULL << (max_order + __ffs(SZ_1M));
- relaxed_align = max(relaxed_align, win_align);
- min_align = min(min_align, relaxed_align);
- size1 = calculate_memsize(size, min_size, add_size, children_add_size,
- old_size, win_align);
- pci_info(bus->self,
- "bridge window %pR to %pR requires relaxed alignment rules\n",
- b_res, &bus->busn_res);
- }
+ size1 = calculate_memsize(size, add_size, children_add_size,
+ win_align);
}
if (!size0 && !size1) {
@@ -1442,121 +1356,24 @@ static void pbus_size_mem(struct pci_bus *bus, unsigned long type,
resource_set_range(b_res, min_align, size0);
b_res->flags |= IORESOURCE_STARTALIGN;
- if (bus->self && size1 > size0 && realloc_head) {
+ if (bus->self && realloc_head && (size1 > size0 || add_align > min_align)) {
b_res->flags &= ~IORESOURCE_DISABLED;
- add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align);
+ add_size = size1 > size0 ? size1 - size0 : 0;
+ pci_dev_res_add_to_list(realloc_head, bus->self, b_res,
+ add_size, add_align);
pci_info(bus->self, "bridge window %pR to %pR add_size %llx add_align %llx\n",
b_res, &bus->busn_res,
- (unsigned long long) (size1 - size0),
+ (unsigned long long) add_size,
(unsigned long long) add_align);
}
}
-unsigned long pci_cardbus_resource_alignment(struct resource *res)
-{
- if (res->flags & IORESOURCE_IO)
- return pci_cardbus_io_size;
- if (res->flags & IORESOURCE_MEM)
- return pci_cardbus_mem_size;
- return 0;
-}
-
-static void pci_bus_size_cardbus(struct pci_bus *bus,
- struct list_head *realloc_head)
-{
- struct pci_dev *bridge = bus->self;
- struct resource *b_res;
- resource_size_t b_res_3_size = pci_cardbus_mem_size * 2;
- u16 ctrl;
-
- b_res = &bridge->resource[PCI_CB_BRIDGE_IO_0_WINDOW];
- if (b_res->parent)
- goto handle_b_res_1;
- /*
- * Reserve some resources for CardBus. We reserve a fixed amount
- * of bus space for CardBus bridges.
- */
- resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size);
- b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN;
- if (realloc_head) {
- b_res->end -= pci_cardbus_io_size;
- add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size,
- pci_cardbus_io_size);
- }
-
-handle_b_res_1:
- b_res = &bridge->resource[PCI_CB_BRIDGE_IO_1_WINDOW];
- if (b_res->parent)
- goto handle_b_res_2;
- resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size);
- b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN;
- if (realloc_head) {
- b_res->end -= pci_cardbus_io_size;
- add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size,
- pci_cardbus_io_size);
- }
-
-handle_b_res_2:
- /* MEM1 must not be pref MMIO */
- pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
- if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) {
- ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1;
- pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl);
- pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
- }
-
- /* Check whether prefetchable memory is supported by this bridge. */
- pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
- if (!(ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0)) {
- ctrl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0;
- pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl);
- pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
- }
-
- b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_0_WINDOW];
- if (b_res->parent)
- goto handle_b_res_3;
- /*
- * If we have prefetchable memory support, allocate two regions.
- * Otherwise, allocate one region of twice the size.
- */
- if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
- resource_set_range(b_res, pci_cardbus_mem_size,
- pci_cardbus_mem_size);
- b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH |
- IORESOURCE_STARTALIGN;
- if (realloc_head) {
- b_res->end -= pci_cardbus_mem_size;
- add_to_list(realloc_head, bridge, b_res,
- pci_cardbus_mem_size, pci_cardbus_mem_size);
- }
-
- /* Reduce that to half */
- b_res_3_size = pci_cardbus_mem_size;
- }
-
-handle_b_res_3:
- b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_1_WINDOW];
- if (b_res->parent)
- goto handle_done;
- resource_set_range(b_res, pci_cardbus_mem_size, b_res_3_size);
- b_res->flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN;
- if (realloc_head) {
- b_res->end -= b_res_3_size;
- add_to_list(realloc_head, bridge, b_res, b_res_3_size,
- pci_cardbus_mem_size);
- }
-
-handle_done:
- ;
-}
-
void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
{
struct pci_dev *dev;
resource_size_t additional_io_size = 0, additional_mmio_size = 0,
additional_mmio_pref_size = 0;
- struct resource *pref;
+ struct resource *b_res;
struct pci_host_bridge *host;
int hdr_type;
@@ -1567,7 +1384,8 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
switch (dev->hdr_type) {
case PCI_HEADER_TYPE_CARDBUS:
- pci_bus_size_cardbus(b, realloc_head);
+ if (pci_bus_size_cardbus_bridge(b, realloc_head))
+ continue;
break;
case PCI_HEADER_TYPE_BRIDGE:
@@ -1582,12 +1400,8 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
host = to_pci_host_bridge(bus->bridge);
if (!host->size_windows)
return;
- pci_bus_for_each_resource(bus, pref)
- if (pref && (pref->flags & IORESOURCE_PREFETCH))
- break;
hdr_type = -1; /* Intentionally invalid - not a PCI device. */
} else {
- pref = &bus->self->resource[PCI_BRIDGE_PREF_MEM_WINDOW];
hdr_type = bus->self->hdr_type;
}
@@ -1605,20 +1419,21 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head)
}
fallthrough;
default:
- pbus_size_io(bus, realloc_head ? 0 : additional_io_size,
- additional_io_size, realloc_head);
-
- if (pref && (pref->flags & IORESOURCE_PREFETCH)) {
- pbus_size_mem(bus,
- IORESOURCE_MEM | IORESOURCE_PREFETCH |
- (pref->flags & IORESOURCE_MEM_64),
- realloc_head ? 0 : additional_mmio_pref_size,
- additional_mmio_pref_size, realloc_head);
+ pbus_size_io(bus, additional_io_size, realloc_head);
+
+ b_res = pbus_select_window_for_type(bus, IORESOURCE_MEM |
+ IORESOURCE_PREFETCH |
+ IORESOURCE_MEM_64);
+ if (b_res && (b_res->flags & IORESOURCE_PREFETCH)) {
+ pbus_size_mem(bus, b_res, additional_mmio_pref_size,
+ realloc_head);
}
- pbus_size_mem(bus, IORESOURCE_MEM,
- realloc_head ? 0 : additional_mmio_size,
- additional_mmio_size, realloc_head);
+ b_res = pbus_select_window_for_type(bus, IORESOURCE_MEM);
+ if (b_res) {
+ pbus_size_mem(bus, b_res, additional_mmio_size,
+ realloc_head);
+ }
break;
}
}
@@ -1656,12 +1471,13 @@ static void pdev_assign_fixed_resources(struct pci_dev *dev)
pci_dev_for_each_resource(dev, r) {
struct pci_bus *b;
- if (r->parent || !(r->flags & IORESOURCE_PCI_FIXED) ||
+ if (resource_assigned(r) ||
+ !(r->flags & IORESOURCE_PCI_FIXED) ||
!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
continue;
b = dev->bus;
- while (b && !r->parent) {
+ while (b && !resource_assigned(r)) {
assign_fixed_resource_on_bus(b, r);
b = b->parent;
}
@@ -1693,7 +1509,7 @@ void __pci_bus_assign_resources(const struct pci_bus *bus,
break;
case PCI_HEADER_TYPE_CARDBUS:
- pci_setup_cardbus(b);
+ pci_setup_cardbus_bridge(b);
break;
default:
@@ -1717,7 +1533,7 @@ static void pci_claim_device_resources(struct pci_dev *dev)
for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
- if (!r->flags || r->parent)
+ if (!r->flags || resource_assigned(r))
continue;
pci_claim_resource(dev, i);
@@ -1731,7 +1547,9 @@ static void pci_claim_bridge_resources(struct pci_dev *dev)
for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
struct resource *r = &dev->resource[i];
- if (!r->flags || r->parent)
+ if (!r->flags || resource_assigned(r))
+ continue;
+ if (r->flags & IORESOURCE_DISABLED)
continue;
pci_claim_bridge_resource(dev, i);
@@ -1798,7 +1616,7 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge,
break;
case PCI_CLASS_BRIDGE_CARDBUS:
- pci_setup_cardbus(b);
+ pci_setup_cardbus_bridge(b);
break;
default:
@@ -1814,7 +1632,7 @@ static void pci_bridge_release_resources(struct pci_bus *bus,
struct pci_dev *dev = bus->self;
int idx, ret;
- if (!b_win->parent)
+ if (!resource_assigned(b_win))
return;
idx = pci_resource_num(dev, b_win);
@@ -2010,7 +1828,7 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res,
{
resource_size_t add_size, size = resource_size(res);
- if (res->parent)
+ if (resource_assigned(res))
return;
if (!new_size)
@@ -2032,7 +1850,7 @@ static void adjust_bridge_window(struct pci_dev *bridge, struct resource *res,
/* If the resource is part of the add_list, remove it now */
if (add_list)
- remove_from_list(add_list, res);
+ pci_dev_res_remove_from_list(add_list, res);
}
static void remove_dev_resource(struct resource *avail, struct pci_dev *dev,
@@ -2100,7 +1918,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus,
* window.
*/
align = pci_resource_alignment(bridge, res);
- if (!res->parent && align)
+ if (!resource_assigned(res) && align)
available[i].start = min(ALIGN(available[i].start, align),
available[i].end + 1);
@@ -2284,9 +2102,9 @@ static void pci_prepare_next_assign_round(struct list_head *fail_head,
/* Restore size and flags */
list_for_each_entry(fail_res, fail_head, list)
- restore_dev_resource(fail_res);
+ pci_dev_res_restore(fail_res);
- free_list(fail_head);
+ pci_dev_res_free_list(fail_head);
}
/*
@@ -2333,7 +2151,7 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
/* Depth last, allocate resources and update the hardware. */
__pci_bus_assign_resources(bus, add_list, &fail_head);
if (WARN_ON_ONCE(add_list && !list_empty(add_list)))
- free_list(add_list);
+ pci_dev_res_free_list(add_list);
tried_times++;
/* Any device complain? */
@@ -2348,7 +2166,7 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus)
dev_info(&bus->dev,
"Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n");
}
- free_list(&fail_head);
+ pci_dev_res_free_list(&fail_head);
break;
}
@@ -2396,7 +2214,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
__pci_bridge_assign_resources(bridge, &add_list, &fail_head);
if (WARN_ON_ONCE(!list_empty(&add_list)))
- free_list(&add_list);
+ pci_dev_res_free_list(&add_list);
tried_times++;
if (list_empty(&fail_head))
@@ -2404,7 +2222,7 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
if (tried_times >= 2) {
/* Still fail, don't need to try more */
- free_list(&fail_head);
+ pci_dev_res_free_list(&fail_head);
break;
}
@@ -2445,7 +2263,7 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *
/* Ignore BARs which are still in use */
if (!res->child) {
- ret = add_to_list(saved, bridge, res, 0, 0);
+ ret = pci_dev_res_add_to_list(saved, bridge, res, 0, 0);
if (ret)
return ret;
@@ -2467,12 +2285,12 @@ static int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *
__pci_bus_size_bridges(bridge->subordinate, &added);
__pci_bridge_assign_resources(bridge, &added, &failed);
if (WARN_ON_ONCE(!list_empty(&added)))
- free_list(&added);
+ pci_dev_res_free_list(&added);
if (!list_empty(&failed)) {
if (pci_required_resource_failed(&failed, type))
ret = -ENOSPC;
- free_list(&failed);
+ pci_dev_res_free_list(&failed);
if (ret)
return ret;
@@ -2528,7 +2346,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size
if (b_win != pbus_select_window(bus, r))
continue;
- ret = add_to_list(&saved, pdev, r, 0, 0);
+ ret = pci_dev_res_add_to_list(&saved, pdev, r, 0, 0);
if (ret)
goto restore;
pci_release_resource(pdev, i);
@@ -2546,7 +2364,7 @@ int pci_do_resource_release_and_resize(struct pci_dev *pdev, int resno, int size
out:
up_read(&pci_bus_sem);
- free_list(&saved);
+ pci_dev_res_free_list(&saved);
return ret;
restore:
@@ -2565,12 +2383,12 @@ restore:
i = pci_resource_num(dev, res);
- if (res->parent) {
+ if (resource_assigned(res)) {
release_child_resources(res);
pci_release_resource(dev, i);
}
- restore_dev_resource(dev_res);
+ pci_dev_res_restore(dev_res);
if (pci_claim_resource(dev, i))
continue;
@@ -2601,6 +2419,6 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus)
up_read(&pci_bus_sem);
__pci_bus_assign_resources(bus, &add_list, NULL);
if (WARN_ON_ONCE(!list_empty(&add_list)))
- free_list(&add_list);
+ pci_dev_res_free_list(&add_list);
}
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bus_resources);
diff --git a/drivers/pci/setup-cardbus.c b/drivers/pci/setup-cardbus.c
new file mode 100644
index 000000000000..1ebd13a1f730
--- /dev/null
+++ b/drivers/pci/setup-cardbus.c
@@ -0,0 +1,306 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cardbus bridge setup routines.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+#include <linux/sprintf.h>
+#include <linux/types.h>
+
+#include "pci.h"
+
+#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
+#define CARDBUS_RESERVE_BUSNR 3
+
+#define DEFAULT_CARDBUS_IO_SIZE SZ_256
+#define DEFAULT_CARDBUS_MEM_SIZE SZ_64M
+/* pci=cbmemsize=nnM,cbiosize=nn can override this */
+static unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE;
+static unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE;
+
+unsigned long pci_cardbus_resource_alignment(struct resource *res)
+{
+ if (res->flags & IORESOURCE_IO)
+ return pci_cardbus_io_size;
+ if (res->flags & IORESOURCE_MEM)
+ return pci_cardbus_mem_size;
+ return 0;
+}
+
+int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
+ struct list_head *realloc_head)
+{
+ struct pci_dev *bridge = bus->self;
+ struct resource *b_res;
+ resource_size_t b_res_3_size = pci_cardbus_mem_size * 2;
+ u16 ctrl;
+
+ b_res = &bridge->resource[PCI_CB_BRIDGE_IO_0_WINDOW];
+ if (resource_assigned(b_res))
+ goto handle_b_res_1;
+ /*
+ * Reserve some resources for CardBus. We reserve a fixed amount
+ * of bus space for CardBus bridges.
+ */
+ resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size);
+ b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res->end -= pci_cardbus_io_size;
+ pci_dev_res_add_to_list(realloc_head, bridge, b_res,
+ pci_cardbus_io_size,
+ pci_cardbus_io_size);
+ }
+
+handle_b_res_1:
+ b_res = &bridge->resource[PCI_CB_BRIDGE_IO_1_WINDOW];
+ if (resource_assigned(b_res))
+ goto handle_b_res_2;
+ resource_set_range(b_res, pci_cardbus_io_size, pci_cardbus_io_size);
+ b_res->flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res->end -= pci_cardbus_io_size;
+ pci_dev_res_add_to_list(realloc_head, bridge, b_res,
+ pci_cardbus_io_size,
+ pci_cardbus_io_size);
+ }
+
+handle_b_res_2:
+ /* MEM1 must not be pref MMIO */
+ pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
+ if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) {
+ ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1;
+ pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl);
+ pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
+ }
+
+ /* Check whether prefetchable memory is supported by this bridge. */
+ pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
+ if (!(ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0)) {
+ ctrl |= PCI_CB_BRIDGE_CTL_PREFETCH_MEM0;
+ pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl);
+ pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
+ }
+
+ b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_0_WINDOW];
+ if (resource_assigned(b_res))
+ goto handle_b_res_3;
+ /*
+ * If we have prefetchable memory support, allocate two regions.
+ * Otherwise, allocate one region of twice the size.
+ */
+ if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
+ resource_set_range(b_res, pci_cardbus_mem_size,
+ pci_cardbus_mem_size);
+ b_res->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH |
+ IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res->end -= pci_cardbus_mem_size;
+ pci_dev_res_add_to_list(realloc_head, bridge, b_res,
+ pci_cardbus_mem_size,
+ pci_cardbus_mem_size);
+ }
+
+ /* Reduce that to half */
+ b_res_3_size = pci_cardbus_mem_size;
+ }
+
+handle_b_res_3:
+ b_res = &bridge->resource[PCI_CB_BRIDGE_MEM_1_WINDOW];
+ if (resource_assigned(b_res))
+ goto handle_done;
+ resource_set_range(b_res, pci_cardbus_mem_size, b_res_3_size);
+ b_res->flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res->end -= b_res_3_size;
+ pci_dev_res_add_to_list(realloc_head, bridge, b_res,
+ b_res_3_size, pci_cardbus_mem_size);
+ }
+
+handle_done:
+ return 0;
+}
+
+void pci_setup_cardbus_bridge(struct pci_bus *bus)
+{
+ struct pci_dev *bridge = bus->self;
+ struct resource *res;
+ struct pci_bus_region region;
+
+ pci_info(bridge, "CardBus bridge to %pR\n",
+ &bus->busn_res);
+
+ res = bus->resource[0];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (resource_assigned(res) && res->flags & IORESOURCE_IO) {
+ /*
+ * The IO resource is allocated a range twice as large as it
+ * would normally need. This allows us to set both IO regs.
+ */
+ pci_info(bridge, " bridge window %pR\n", res);
+ pci_write_config_dword(bridge, PCI_CB_IO_BASE_0,
+ region.start);
+ pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0,
+ region.end);
+ }
+
+ res = bus->resource[1];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (resource_assigned(res) && res->flags & IORESOURCE_IO) {
+ pci_info(bridge, " bridge window %pR\n", res);
+ pci_write_config_dword(bridge, PCI_CB_IO_BASE_1,
+ region.start);
+ pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1,
+ region.end);
+ }
+
+ res = bus->resource[2];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (resource_assigned(res) && res->flags & IORESOURCE_MEM) {
+ pci_info(bridge, " bridge window %pR\n", res);
+ pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0,
+ region.start);
+ pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0,
+ region.end);
+ }
+
+ res = bus->resource[3];
+ pcibios_resource_to_bus(bridge->bus, &region, res);
+ if (resource_assigned(res) && res->flags & IORESOURCE_MEM) {
+ pci_info(bridge, " bridge window %pR\n", res);
+ pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1,
+ region.start);
+ pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1,
+ region.end);
+ }
+}
+EXPORT_SYMBOL(pci_setup_cardbus_bridge);
+
+int pci_setup_cardbus(char *str)
+{
+ if (!strncmp(str, "cbiosize=", 9)) {
+ pci_cardbus_io_size = memparse(str + 9, &str);
+ return 0;
+ } else if (!strncmp(str, "cbmemsize=", 10)) {
+ pci_cardbus_mem_size = memparse(str + 10, &str);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
+ u32 buses, int max,
+ unsigned int available_buses, int pass)
+{
+ struct pci_bus *child;
+ bool fixed_buses;
+ u8 fixed_sec, fixed_sub;
+ int next_busnr;
+ u32 i, j = 0;
+
+ /*
+ * We need to assign a number to this bus which we always do in the
+ * second pass.
+ */
+ if (!pass) {
+ /*
+ * Temporarily disable forwarding of the configuration
+ * cycles on all bridges in this bus segment to avoid
+ * possible conflicts in the second pass between two bridges
+ * programmed with overlapping bus ranges.
+ */
+ pci_write_config_dword(dev, PCI_PRIMARY_BUS,
+ buses & PCI_SEC_LATENCY_TIMER_MASK);
+ return max;
+ }
+
+ /* Clear errors */
+ pci_write_config_word(dev, PCI_STATUS, 0xffff);
+
+ /* Read bus numbers from EA Capability (if present) */
+ fixed_buses = pci_ea_fixed_busnrs(dev, &fixed_sec, &fixed_sub);
+ if (fixed_buses)
+ next_busnr = fixed_sec;
+ else
+ next_busnr = max + 1;
+
+ /*
+ * Prevent assigning a bus number that already exists. This can
+ * happen when a bridge is hot-plugged, so in this case we only
+ * re-scan this bus.
+ */
+ child = pci_find_bus(pci_domain_nr(bus), next_busnr);
+ if (!child) {
+ child = pci_add_new_bus(bus, dev, next_busnr);
+ if (!child)
+ return max;
+ pci_bus_insert_busn_res(child, next_busnr, bus->busn_res.end);
+ }
+ max++;
+ if (available_buses)
+ available_buses--;
+
+ buses = (buses & PCI_SEC_LATENCY_TIMER_MASK) |
+ FIELD_PREP(PCI_PRIMARY_BUS_MASK, child->primary) |
+ FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) |
+ FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end);
+
+ /*
+ * yenta.c forces a secondary latency timer of 176.
+ * Copy that behaviour here.
+ */
+ buses &= ~PCI_SEC_LATENCY_TIMER_MASK;
+ buses |= FIELD_PREP(PCI_SEC_LATENCY_TIMER_MASK, CARDBUS_LATENCY_TIMER);
+
+ /* We need to blast all three values with a single write */
+ pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
+
+ /*
+ * For CardBus bridges, we leave 4 bus numbers as cards with a
+ * PCI-to-PCI bridge can be inserted later.
+ */
+ for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) {
+ struct pci_bus *parent = bus;
+
+ if (pci_find_bus(pci_domain_nr(bus), max + i + 1))
+ break;
+
+ while (parent->parent) {
+ if (!pcibios_assign_all_busses() &&
+ (parent->busn_res.end > max) &&
+ (parent->busn_res.end <= max + i)) {
+ j = 1;
+ }
+ parent = parent->parent;
+ }
+ if (j) {
+ /*
+ * Often, there are two CardBus bridges -- try to
+ * leave one valid bus number for each one.
+ */
+ i /= 2;
+ break;
+ }
+ }
+ max += i;
+
+ /*
+ * Set subordinate bus number to its real value. If fixed
+ * subordinate bus number exists from EA capability then use it.
+ */
+ if (fixed_buses)
+ max = fixed_sub;
+ pci_bus_update_busn_res_end(child, max);
+ pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
+
+ scnprintf(child->name, sizeof(child->name), "PCI CardBus %04x:%02x",
+ pci_domain_nr(bus), child->number);
+
+ pbus_validate_busn(child);
+
+ return max;
+}
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index e5fcadfc58b0..bb2aef373d6f 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -359,7 +359,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
res->flags &= ~IORESOURCE_UNSET;
res->flags &= ~IORESOURCE_STARTALIGN;
- if (resno >= PCI_BRIDGE_RESOURCES && resno <= PCI_BRIDGE_RESOURCE_END)
+ if (pci_resource_is_bridge_win(resno))
res->flags &= ~IORESOURCE_DISABLED;
pci_info(dev, "%s %pR: assigned\n", res_name, res);
diff --git a/drivers/pci/trace.c b/drivers/pci/trace.c
new file mode 100644
index 000000000000..cf11abca8602
--- /dev/null
+++ b/drivers/pci/trace.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Tracepoints for PCI system
+ *
+ * Copyright (C) 2025 Alibaba Corporation
+ */
+
+#include <linux/pci.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/pci.h>
diff --git a/drivers/pcmcia/yenta_socket.c b/drivers/pcmcia/yenta_socket.c
index 923ed23570a0..34c4eaee7dfc 100644
--- a/drivers/pcmcia/yenta_socket.c
+++ b/drivers/pcmcia/yenta_socket.c
@@ -779,7 +779,7 @@ static void yenta_allocate_resources(struct yenta_socket *socket)
IORESOURCE_MEM,
PCI_CB_MEMORY_BASE_1, PCI_CB_MEMORY_LIMIT_1);
if (program)
- pci_setup_cardbus(socket->dev->subordinate);
+ pci_setup_cardbus_bridge(socket->dev->subordinate);
}
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 889dc4426271..bd3a63555594 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -21,7 +21,6 @@ static const char hcd_name[] = "ehci-pci";
/* defined here to avoid adding to pci_ids.h for single instance use */
#define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70
-#define PCI_VENDOR_ID_ASPEED 0x1a03
#define PCI_DEVICE_ID_ASPEED_EHCI 0x2603
/*-------------------------------------------------------------------------*/
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 9afa30f9346f..60ca6a49839c 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -338,7 +338,7 @@ static inline bool resource_union(const struct resource *r1, const struct resour
* Check if this resource is added to a resource tree or detached. Caller is
* responsible for not racing assignment.
*/
-static inline bool resource_assigned(struct resource *res)
+static inline bool resource_assigned(const struct resource *res)
{
return res->parent;
}
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 4286bfdbfdfa..c021c7af175f 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -223,6 +223,13 @@ struct pci_epc_bar_desc {
/**
* struct pci_epc_features - features supported by a EPC device per function
* @linkup_notifier: indicate if the EPC device can notify EPF driver on link up
+ * @dynamic_inbound_mapping: indicate if the EPC device supports updating
+ * inbound mappings for an already configured BAR
+ * (i.e. allow calling pci_epc_set_bar() again
+ * without first calling pci_epc_clear_bar())
+ * @subrange_mapping: indicate if the EPC device can map inbound subranges for a
+ * BAR. This feature depends on @dynamic_inbound_mapping
+ * feature.
* @msi_capable: indicate if the endpoint function has MSI capability
* @msix_capable: indicate if the endpoint function has MSI-X capability
* @intx_capable: indicate if the endpoint can raise INTx interrupts
@@ -231,6 +238,8 @@ struct pci_epc_bar_desc {
*/
struct pci_epc_features {
unsigned int linkup_notifier : 1;
+ unsigned int dynamic_inbound_mapping : 1;
+ unsigned int subrange_mapping : 1;
unsigned int msi_capable : 1;
unsigned int msix_capable : 1;
unsigned int intx_capable : 1;
diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
index 48f68c4dcfa5..7737a7c03260 100644
--- a/include/linux/pci-epf.h
+++ b/include/linux/pci-epf.h
@@ -111,6 +111,22 @@ struct pci_epf_driver {
#define to_pci_epf_driver(drv) container_of_const((drv), struct pci_epf_driver, driver)
/**
+ * struct pci_epf_bar_submap - BAR subrange for inbound mapping
+ * @phys_addr: target physical/DMA address for this subrange
+ * @size: the size of the subrange to be mapped
+ *
+ * When pci_epf_bar.num_submap is >0, pci_epf_bar.submap describes the
+ * complete BAR layout. This allows an EPC driver to program multiple
+ * inbound translation windows for a single BAR when supported by the
+ * controller. The array order defines the BAR layout (submap[0] at offset
+ * 0, and each immediately follows the previous one).
+ */
+struct pci_epf_bar_submap {
+ dma_addr_t phys_addr;
+ size_t size;
+};
+
+/**
* struct pci_epf_bar - represents the BAR of EPF device
* @phys_addr: physical address that should be mapped to the BAR
* @addr: virtual address corresponding to the @phys_addr
@@ -119,6 +135,9 @@ struct pci_epf_driver {
* requirement
* @barno: BAR number
* @flags: flags that are set for the BAR
+ * @num_submap: number of entries in @submap
+ * @submap: array of subrange descriptors allocated by the caller. See
+ * struct pci_epf_bar_submap for the semantics in detail.
*/
struct pci_epf_bar {
dma_addr_t phys_addr;
@@ -127,6 +146,10 @@ struct pci_epf_bar {
size_t mem_size;
enum pci_barno barno;
int flags;
+
+ /* Optional sub-range mapping */
+ unsigned int num_submap;
+ struct pci_epf_bar_submap *submap;
};
/**
diff --git a/include/linux/pci-p2pdma.h b/include/linux/pci-p2pdma.h
index 517e121d2598..873de20a2247 100644
--- a/include/linux/pci-p2pdma.h
+++ b/include/linux/pci-p2pdma.h
@@ -20,6 +20,8 @@ struct scatterlist;
* struct p2pdma_provider
*
* A p2pdma provider is a range of MMIO address space available to the CPU.
+ * @owner: Device to which this provider belongs.
+ * @bus_offset: Bus offset for p2p communication.
*/
struct p2pdma_provider {
struct device *owner;
diff --git a/include/linux/pci-pwrctrl.h b/include/linux/pci-pwrctrl.h
index 4aefc7901cd1..1192a2527521 100644
--- a/include/linux/pci-pwrctrl.h
+++ b/include/linux/pci-pwrctrl.h
@@ -31,6 +31,8 @@ struct device_link;
/**
* struct pci_pwrctrl - PCI device power control context.
* @dev: Address of the power controlling device.
+ * @power_on: Callback to power on the power controlling device.
+ * @power_off: Callback to power off the power controlling device.
*
* An object of this type must be allocated by the PCI power control device and
* passed to the pwrctrl subsystem to trigger a bus rescan and setup a device
@@ -38,6 +40,8 @@ struct device_link;
*/
struct pci_pwrctrl {
struct device *dev;
+ int (*power_on)(struct pci_pwrctrl *pwrctrl);
+ int (*power_off)(struct pci_pwrctrl *pwrctrl);
/* private: internal use only */
struct notifier_block nb;
@@ -50,5 +54,15 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl);
void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl);
int devm_pci_pwrctrl_device_set_ready(struct device *dev,
struct pci_pwrctrl *pwrctrl);
-
+#if IS_ENABLED(CONFIG_PCI_PWRCTRL)
+int pci_pwrctrl_create_devices(struct device *parent);
+void pci_pwrctrl_destroy_devices(struct device *parent);
+int pci_pwrctrl_power_on_devices(struct device *parent);
+void pci_pwrctrl_power_off_devices(struct device *parent);
+#else
+static inline int pci_pwrctrl_create_devices(struct device *parent) { return 0; }
+static void pci_pwrctrl_destroy_devices(struct device *parent) { }
+static inline int pci_pwrctrl_power_on_devices(struct device *parent) { return 0; }
+static void pci_pwrctrl_power_off_devices(struct device *parent) { }
+#endif
#endif /* __PCI_PWRCTRL_H__ */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index b30631673b5b..edf792a79193 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -248,6 +248,11 @@ enum pci_dev_flags {
PCI_DEV_FLAGS_HAS_MSI_MASKING = (__force pci_dev_flags_t) (1 << 12),
/* Device requires write to PCI_MSIX_ENTRY_DATA before any MSIX reads */
PCI_DEV_FLAGS_MSIX_TOUCH_ENTRY_DATA_FIRST = (__force pci_dev_flags_t) (1 << 13),
+ /*
+ * PCIe to PCI bridge does not create RID aliases because the bridge is
+ * integrated with the downstream devices and doesn't use real PCI.
+ */
+ PCI_DEV_FLAGS_PCI_BRIDGE_NO_ALIAS = (__force pci_dev_flags_t) (1 << 14),
};
enum pci_irq_reroute_variant {
@@ -413,6 +418,7 @@ struct pci_dev {
user sysfs */
unsigned int clear_retrain_link:1; /* Need to clear Retrain Link
bit manually */
+ unsigned int no_bw_notif:1; /* BW notifications may cause issues */
unsigned int d3hot_delay; /* D3hot->D0 transition time in ms */
unsigned int d3cold_delay; /* D3cold->D0 transition time in ms */
@@ -564,6 +570,7 @@ struct pci_dev {
struct pci_tsm *tsm; /* TSM operation state */
#endif
u16 acs_cap; /* ACS Capability offset */
+ u16 acs_capabilities; /* ACS Capabilities */
u8 supported_speeds; /* Supported Link Speeds Vector */
phys_addr_t rom; /* Physical address if not from BAR */
size_t romlen; /* Length if not from BAR */
@@ -860,7 +867,6 @@ struct pci_ops {
void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where);
int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);
int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);
- int (*assert_perst)(struct pci_bus *bus, bool assert);
};
/*
@@ -1250,7 +1256,11 @@ void pci_stop_and_remove_bus_device(struct pci_dev *dev);
void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev);
void pci_stop_root_bus(struct pci_bus *bus);
void pci_remove_root_bus(struct pci_bus *bus);
-void pci_setup_cardbus(struct pci_bus *bus);
+#ifdef CONFIG_CARDBUS
+void pci_setup_cardbus_bridge(struct pci_bus *bus);
+#else
+static inline void pci_setup_cardbus_bridge(struct pci_bus *bus) { }
+#endif
void pcibios_setup_bridge(struct pci_bus *bus, unsigned long type);
void pci_sort_breadthfirst(void);
#define dev_is_pci(d) ((d)->bus == &pci_bus_type)
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 5ed7846639bf..efd6dee093e0 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2583,6 +2583,8 @@
#define PCI_DEVICE_ID_NETRONOME_NFP3800_VF 0x3803
#define PCI_DEVICE_ID_NETRONOME_NFP6000_VF 0x6003
+#define PCI_VENDOR_ID_ASPEED 0x1a03
+
#define PCI_VENDOR_ID_QMI 0x1a32
#define PCI_VENDOR_ID_AZWAVE 0x1a3b
diff --git a/include/trace/events/pci.h b/include/trace/events/pci.h
new file mode 100644
index 000000000000..9a9122f62fd3
--- /dev/null
+++ b/include/trace/events/pci.h
@@ -0,0 +1,129 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM pci
+
+#if !defined(_TRACE_HW_EVENT_PCI_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_HW_EVENT_PCI_H
+
+#include <uapi/linux/pci_regs.h>
+#include <linux/tracepoint.h>
+
+#define PCI_HOTPLUG_EVENT \
+ EM(PCI_HOTPLUG_LINK_UP, "LINK_UP") \
+ EM(PCI_HOTPLUG_LINK_DOWN, "LINK_DOWN") \
+ EM(PCI_HOTPLUG_CARD_PRESENT, "CARD_PRESENT") \
+ EMe(PCI_HOTPLUG_CARD_NOT_PRESENT, "CARD_NOT_PRESENT")
+
+/* Enums require being exported to userspace, for user tool parsing */
+#undef EM
+#undef EMe
+#define EM(a, b) TRACE_DEFINE_ENUM(a);
+#define EMe(a, b) TRACE_DEFINE_ENUM(a);
+
+PCI_HOTPLUG_EVENT
+
+/*
+ * Now redefine the EM() and EMe() macros to map the enums to the strings
+ * that will be printed in the output.
+ */
+#undef EM
+#undef EMe
+#define EM(a, b) {a, b},
+#define EMe(a, b) {a, b}
+
+/*
+ * Note: For generic PCI hotplug events, we pass already-resolved strings
+ * (port_name, slot) instead of driver-specific structures like 'struct
+ * controller'. This is because different PCI hotplug drivers (pciehp, cpqphp,
+ * ibmphp, shpchp) define their own versions of 'struct controller' with
+ * different fields and helper functions. Using driver-specific structures would
+ * make the tracepoint interface non-generic and cause compatibility issues
+ * across different drivers.
+ */
+TRACE_EVENT(pci_hp_event,
+
+ TP_PROTO(const char *port_name,
+ const char *slot,
+ const int event),
+
+ TP_ARGS(port_name, slot, event),
+
+ TP_STRUCT__entry(
+ __string( port_name, port_name )
+ __string( slot, slot )
+ __field( int, event )
+ ),
+
+ TP_fast_assign(
+ __assign_str(port_name);
+ __assign_str(slot);
+ __entry->event = event;
+ ),
+
+ TP_printk("%s slot:%s, event:%s\n",
+ __get_str(port_name),
+ __get_str(slot),
+ __print_symbolic(__entry->event, PCI_HOTPLUG_EVENT)
+ )
+);
+
+#define PCI_EXP_LNKSTA_LINK_STATUS_MASK (PCI_EXP_LNKSTA_LBMS | \
+ PCI_EXP_LNKSTA_LABS | \
+ PCI_EXP_LNKSTA_LT | \
+ PCI_EXP_LNKSTA_DLLLA)
+
+#define LNKSTA_FLAGS \
+ { PCI_EXP_LNKSTA_LT, "LT"}, \
+ { PCI_EXP_LNKSTA_DLLLA, "DLLLA"}, \
+ { PCI_EXP_LNKSTA_LBMS, "LBMS"}, \
+ { PCI_EXP_LNKSTA_LABS, "LABS"}
+
+TRACE_EVENT(pcie_link_event,
+
+ TP_PROTO(struct pci_bus *bus,
+ unsigned int reason,
+ unsigned int width,
+ unsigned int status
+ ),
+
+ TP_ARGS(bus, reason, width, status),
+
+ TP_STRUCT__entry(
+ __string( port_name, pci_name(bus->self))
+ __field( unsigned int, type )
+ __field( unsigned int, reason )
+ __field( unsigned int, cur_bus_speed )
+ __field( unsigned int, max_bus_speed )
+ __field( unsigned int, width )
+ __field( unsigned int, flit_mode )
+ __field( unsigned int, link_status )
+ ),
+
+ TP_fast_assign(
+ __assign_str(port_name);
+ __entry->type = pci_pcie_type(bus->self);
+ __entry->reason = reason;
+ __entry->cur_bus_speed = bus->cur_bus_speed;
+ __entry->max_bus_speed = bus->max_bus_speed;
+ __entry->width = width;
+ __entry->flit_mode = bus->flit_mode;
+ __entry->link_status = status;
+ ),
+
+ TP_printk("%s type:%d, reason:%d, cur_bus_speed:%d, max_bus_speed:%d, width:%u, flit_mode:%u, status:%s\n",
+ __get_str(port_name),
+ __entry->type,
+ __entry->reason,
+ __entry->cur_bus_speed,
+ __entry->max_bus_speed,
+ __entry->width,
+ __entry->flit_mode,
+ __print_flags((unsigned long)__entry->link_status, "|",
+ LNKSTA_FLAGS)
+ )
+);
+
+#endif /* _TRACE_HW_EVENT_PCI_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/include/uapi/linux/pci.h b/include/uapi/linux/pci.h
index a769eefc5139..4f150028965d 100644
--- a/include/uapi/linux/pci.h
+++ b/include/uapi/linux/pci.h
@@ -39,4 +39,11 @@
#define PCIIOC_MMAP_IS_MEM (PCIIOC_BASE | 0x02) /* Set mmap state to MEM space. */
#define PCIIOC_WRITE_COMBINE (PCIIOC_BASE | 0x03) /* Enable/disable write-combining. */
+enum pci_hotplug_event {
+ PCI_HOTPLUG_LINK_UP,
+ PCI_HOTPLUG_LINK_DOWN,
+ PCI_HOTPLUG_CARD_PRESENT,
+ PCI_HOTPLUG_CARD_NOT_PRESENT,
+};
+
#endif /* _UAPILINUX_PCI_H */
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index 3add74ae2594..8be55ece2a21 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -132,6 +132,11 @@
#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */
#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */
#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */
+/* Masks for dword-sized processing of Bus Number and Sec Latency Timer fields */
+#define PCI_PRIMARY_BUS_MASK 0x000000ff
+#define PCI_SECONDARY_BUS_MASK 0x0000ff00
+#define PCI_SUBORDINATE_BUS_MASK 0x00ff0000
+#define PCI_SEC_LATENCY_TIMER_MASK 0xff000000
#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */
#define PCI_IO_LIMIT 0x1d
#define PCI_IO_RANGE_TYPE_MASK 0x0fUL /* I/O bridging type */
diff --git a/include/uapi/linux/pcitest.h b/include/uapi/linux/pcitest.h
index d6023a45a9d0..710f8842223f 100644
--- a/include/uapi/linux/pcitest.h
+++ b/include/uapi/linux/pcitest.h
@@ -22,6 +22,7 @@
#define PCITEST_GET_IRQTYPE _IO('P', 0x9)
#define PCITEST_BARS _IO('P', 0xa)
#define PCITEST_DOORBELL _IO('P', 0xb)
+#define PCITEST_BAR_SUBRANGE _IO('P', 0xc)
#define PCITEST_CLEAR_IRQ _IO('P', 0x10)
#define PCITEST_IRQ_TYPE_UNDEFINED -1
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 4c01e9d5ccc7..c2258b133939 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1913,6 +1913,7 @@ void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
irq_domain_free_irq_data(virq, nr_irqs);
irq_free_descs(virq, nr_irqs);
}
+EXPORT_SYMBOL_GPL(irq_domain_free_irqs);
static void irq_domain_free_one_irq(struct irq_domain *domain, unsigned int virq)
{
diff --git a/kernel/resource.c b/kernel/resource.c
index e4e9bac12e6e..c5f03ac78e44 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -82,7 +82,7 @@ static struct resource *next_resource(struct resource *p, bool skip_children,
#ifdef CONFIG_PROC_FS
-enum { MAX_IORES_LEVEL = 5 };
+enum { MAX_IORES_LEVEL = 8 };
static void *r_start(struct seq_file *m, loff_t *pos)
__acquires(resource_lock)
diff --git a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c
index 23aac6f97061..eecb776c33af 100644
--- a/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c
+++ b/tools/testing/selftests/pci_endpoint/pci_endpoint_test.c
@@ -70,6 +70,23 @@ TEST_F(pci_ep_bar, BAR_TEST)
EXPECT_FALSE(ret) TH_LOG("Test failed for BAR%d", variant->barno);
}
+TEST_F(pci_ep_bar, BAR_SUBRANGE_TEST)
+{
+ int ret;
+
+ pci_ep_ioctl(PCITEST_SET_IRQTYPE, PCITEST_IRQ_TYPE_AUTO);
+ ASSERT_EQ(0, ret) TH_LOG("Can't set AUTO IRQ type");
+
+ pci_ep_ioctl(PCITEST_BAR_SUBRANGE, variant->barno);
+ if (ret == -ENODATA)
+ SKIP(return, "BAR is disabled");
+ if (ret == -EBUSY)
+ SKIP(return, "BAR is test register space");
+ if (ret == -EOPNOTSUPP)
+ SKIP(return, "Subrange map is not supported");
+ EXPECT_FALSE(ret) TH_LOG("Test failed for BAR%d", variant->barno);
+}
+
FIXTURE(pci_ep_basic)
{
int fd;