Merge 3cc30140db ("Merge tag 'pci-v5.19-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci") into android-mainline
Steps on the way to 5.19-rc1 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I99872c1974ace8515cd56e274f7aff16f32cbfaa
This commit is contained in:
commit
f7eff2dfa2
43 changed files with 1649 additions and 751 deletions
|
|
@ -273,12 +273,12 @@ Set the DMA mask size
|
|||
While all drivers should explicitly indicate the DMA capability
|
||||
(e.g. 32 or 64 bit) of the PCI bus master, devices with more than
|
||||
32-bit bus master capability for streaming data need the driver
|
||||
to "register" this capability by calling pci_set_dma_mask() with
|
||||
to "register" this capability by calling dma_set_mask() with
|
||||
appropriate parameters. In general this allows more efficient DMA
|
||||
on systems where System RAM exists above 4G _physical_ address.
|
||||
|
||||
Drivers for all PCI-X and PCIe compliant devices must call
|
||||
set_dma_mask() as they are 64-bit DMA devices.
|
||||
dma_set_mask() as they are 64-bit DMA devices.
|
||||
|
||||
Similarly, drivers must also "register" this capability if the device
|
||||
can directly address "coherent memory" in System RAM above 4G physical
|
||||
|
|
|
|||
|
|
@ -4107,6 +4107,15 @@
|
|||
please report a bug.
|
||||
nocrs [X86] Ignore PCI host bridge windows from ACPI.
|
||||
If you need to use this, please report a bug.
|
||||
use_e820 [X86] Use E820 reservations to exclude parts of
|
||||
PCI host bridge windows. This is a workaround
|
||||
for BIOS defects in host bridge _CRS methods.
|
||||
If you need to use this, please report a bug to
|
||||
<linux-pci@vger.kernel.org>.
|
||||
no_e820 [X86] Ignore E820 reservations for PCI host
|
||||
bridge windows. This is the default on modern
|
||||
hardware. If you need to use this, please report
|
||||
a bug to <linux-pci@vger.kernel.org>.
|
||||
routeirq Do IRQ routing for all PCI devices.
|
||||
This is normally done in pci_enable_device(),
|
||||
so this option is a temporary workaround
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ Required properties:
|
|||
"fsl,ls1012a-pcie"
|
||||
"fsl,ls1028a-pcie"
|
||||
EP mode:
|
||||
"fsl,ls1028a-pcie-ep", "fsl,ls-pcie-ep"
|
||||
"fsl,ls1046a-pcie-ep", "fsl,ls-pcie-ep"
|
||||
"fsl,ls1088a-pcie-ep", "fsl,ls-pcie-ep"
|
||||
"fsl,ls2088a-pcie-ep", "fsl,ls-pcie-ep"
|
||||
|
|
@ -30,39 +31,49 @@ Required properties:
|
|||
- reg: base addresses and lengths of the PCIe controller register blocks.
|
||||
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
||||
entry for each entry in the interrupt-names property.
|
||||
- interrupt-names: Must include the following entries:
|
||||
"intr": The interrupt that is asserted for controller interrupts
|
||||
- interrupt-names: It could include the following entries:
|
||||
"aer": Used for interrupt line which reports AER events when
|
||||
non MSI/MSI-X/INTx mode is used
|
||||
"pme": Used for interrupt line which reports PME events when
|
||||
non MSI/MSI-X/INTx mode is used
|
||||
"intr": Used for SoCs(like ls2080a, lx2160a, ls2080a, ls2088a, ls1088a)
|
||||
which has a single interrupt line for miscellaneous controller
|
||||
events(could include AER and PME events).
|
||||
- fsl,pcie-scfg: Must include two entries.
|
||||
The first entry must be a link to the SCFG device node
|
||||
The second entry must be '0' or '1' based on physical PCIe controller index.
|
||||
The second entry is the physical PCIe controller index starting from '0'.
|
||||
This is used to get SCFG PEXN registers
|
||||
- dma-coherent: Indicates that the hardware IP block can ensure the coherency
|
||||
of the data transferred from/to the IP block. This can avoid the software
|
||||
cache flush/invalid actions, and improve the performance significantly.
|
||||
|
||||
Optional properties:
|
||||
- big-endian: If the PEX_LUT and PF register block is in big-endian, specify
|
||||
this property.
|
||||
|
||||
Example:
|
||||
|
||||
pcie@3400000 {
|
||||
compatible = "fsl,ls1021a-pcie";
|
||||
reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */
|
||||
0x40 0x00000000 0x0 0x00002000>; /* configuration space */
|
||||
reg-names = "regs", "config";
|
||||
interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>; /* controller interrupt */
|
||||
interrupt-names = "intr";
|
||||
fsl,pcie-scfg = <&scfg 0>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
dma-coherent;
|
||||
num-lanes = <4>;
|
||||
bus-range = <0x0 0xff>;
|
||||
ranges = <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x00010000 /* downstream I/O */
|
||||
0xc2000000 0x0 0x20000000 0x40 0x20000000 0x0 0x20000000 /* prefetchable memory */
|
||||
0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0000 0 0 2 &gic GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0000 0 0 3 &gic GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0000 0 0 4 &gic GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
pcie@3400000 {
|
||||
compatible = "fsl,ls1088a-pcie";
|
||||
reg = <0x00 0x03400000 0x0 0x00100000>, /* controller registers */
|
||||
<0x20 0x00000000 0x0 0x00002000>; /* configuration space */
|
||||
reg-names = "regs", "config";
|
||||
interrupts = <0 108 IRQ_TYPE_LEVEL_HIGH>; /* aer interrupt */
|
||||
interrupt-names = "aer";
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
device_type = "pci";
|
||||
dma-coherent;
|
||||
num-viewport = <256>;
|
||||
bus-range = <0x0 0xff>;
|
||||
ranges = <0x81000000 0x0 0x00000000 0x20 0x00010000 0x0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0x0 0x40000000 0x20 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
|
||||
msi-parent = <&its>;
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 7>;
|
||||
interrupt-map = <0000 0 0 1 &gic 0 0 0 109 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0000 0 0 2 &gic 0 0 0 110 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0000 0 0 3 &gic 0 0 0 111 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<0000 0 0 4 &gic 0 0 0 112 IRQ_TYPE_LEVEL_HIGH>;
|
||||
iommu-map = <0 &smmu 0 1>; /* Fixed-up by bootloader */
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,397 +0,0 @@
|
|||
* Qualcomm PCI express root complex
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: Value should contain
|
||||
- "qcom,pcie-ipq8064" for ipq8064
|
||||
- "qcom,pcie-ipq8064-v2" for ipq8064 rev 2 or ipq8065
|
||||
- "qcom,pcie-apq8064" for apq8064
|
||||
- "qcom,pcie-apq8084" for apq8084
|
||||
- "qcom,pcie-msm8996" for msm8996 or apq8096
|
||||
- "qcom,pcie-ipq4019" for ipq4019
|
||||
- "qcom,pcie-ipq8074" for ipq8074
|
||||
- "qcom,pcie-qcs404" for qcs404
|
||||
- "qcom,pcie-sc8180x" for sc8180x
|
||||
- "qcom,pcie-sdm845" for sdm845
|
||||
- "qcom,pcie-sm8250" for sm8250
|
||||
- "qcom,pcie-sm8450-pcie0" for PCIe0 on sm8450
|
||||
- "qcom,pcie-sm8450-pcie1" for PCIe1 on sm8450
|
||||
- "qcom,pcie-ipq6018" for ipq6018
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: Register ranges as listed in the reg-names property
|
||||
|
||||
- reg-names:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: Must include the following entries
|
||||
- "parf" Qualcomm specific registers
|
||||
- "dbi" DesignWare PCIe registers
|
||||
- "elbi" External local bus interface registers
|
||||
- "config" PCIe configuration space
|
||||
- "atu" ATU address space (optional)
|
||||
|
||||
- device_type:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: Should be "pci". As specified in snps,dw-pcie.yaml
|
||||
|
||||
- #address-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: Should be 3. As specified in snps,dw-pcie.yaml
|
||||
|
||||
- #size-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: Should be 2. As specified in snps,dw-pcie.yaml
|
||||
|
||||
- ranges:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: As specified in snps,dw-pcie.yaml
|
||||
|
||||
- interrupts:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: MSI interrupt
|
||||
|
||||
- interrupt-names:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain "msi"
|
||||
|
||||
- #interrupt-cells:
|
||||
Usage: required
|
||||
Value type: <u32>
|
||||
Definition: Should be 1. As specified in snps,dw-pcie.yaml
|
||||
|
||||
- interrupt-map-mask:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: As specified in snps,dw-pcie.yaml
|
||||
|
||||
- interrupt-map:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: As specified in snps,dw-pcie.yaml
|
||||
|
||||
- clocks:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: List of phandle and clock specifier pairs as listed
|
||||
in clock-names property
|
||||
|
||||
- clock-names:
|
||||
Usage: required
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "iface" Configuration AHB clock
|
||||
|
||||
- clock-names:
|
||||
Usage: required for ipq/apq8064
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "core" Clocks the pcie hw block
|
||||
- "phy" Clocks the pcie PHY block
|
||||
- "aux" Clocks the pcie AUX block
|
||||
- "ref" Clocks the pcie ref block
|
||||
- clock-names:
|
||||
Usage: required for apq8084/ipq4019
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "aux" Auxiliary (AUX) clock
|
||||
- "bus_master" Master AXI clock
|
||||
- "bus_slave" Slave AXI clock
|
||||
|
||||
- clock-names:
|
||||
Usage: required for msm8996/apq8096
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "pipe" Pipe Clock driving internal logic
|
||||
- "aux" Auxiliary (AUX) clock
|
||||
- "cfg" Configuration clock
|
||||
- "bus_master" Master AXI clock
|
||||
- "bus_slave" Slave AXI clock
|
||||
|
||||
- clock-names:
|
||||
Usage: required for ipq8074
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "iface" PCIe to SysNOC BIU clock
|
||||
- "axi_m" AXI Master clock
|
||||
- "axi_s" AXI Slave clock
|
||||
- "ahb" AHB clock
|
||||
- "aux" Auxiliary clock
|
||||
|
||||
- clock-names:
|
||||
Usage: required for ipq6018
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "iface" PCIe to SysNOC BIU clock
|
||||
- "axi_m" AXI Master clock
|
||||
- "axi_s" AXI Slave clock
|
||||
- "axi_bridge" AXI bridge clock
|
||||
- "rchng"
|
||||
|
||||
- clock-names:
|
||||
Usage: required for qcs404
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "iface" AHB clock
|
||||
- "aux" Auxiliary clock
|
||||
- "master_bus" AXI Master clock
|
||||
- "slave_bus" AXI Slave clock
|
||||
|
||||
- clock-names:
|
||||
Usage: required for sdm845
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "aux" Auxiliary clock
|
||||
- "cfg" Configuration clock
|
||||
- "bus_master" Master AXI clock
|
||||
- "bus_slave" Slave AXI clock
|
||||
- "slave_q2a" Slave Q2A clock
|
||||
- "tbu" PCIe TBU clock
|
||||
- "pipe" PIPE clock
|
||||
|
||||
- clock-names:
|
||||
Usage: required for sc8180x and sm8250
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "aux" Auxiliary clock
|
||||
- "cfg" Configuration clock
|
||||
- "bus_master" Master AXI clock
|
||||
- "bus_slave" Slave AXI clock
|
||||
- "slave_q2a" Slave Q2A clock
|
||||
- "tbu" PCIe TBU clock
|
||||
- "ddrss_sf_tbu" PCIe SF TBU clock
|
||||
- "pipe" PIPE clock
|
||||
|
||||
- clock-names:
|
||||
Usage: required for sm8450-pcie0 and sm8450-pcie1
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "aux" Auxiliary clock
|
||||
- "cfg" Configuration clock
|
||||
- "bus_master" Master AXI clock
|
||||
- "bus_slave" Slave AXI clock
|
||||
- "slave_q2a" Slave Q2A clock
|
||||
- "tbu" PCIe TBU clock
|
||||
- "ddrss_sf_tbu" PCIe SF TBU clock
|
||||
- "pipe" PIPE clock
|
||||
- "pipe_mux" PIPE MUX
|
||||
- "phy_pipe" PIPE output clock
|
||||
- "ref" REFERENCE clock
|
||||
- "aggre0" Aggre NoC PCIe0 AXI clock, only for sm8450-pcie0
|
||||
- "aggre1" Aggre NoC PCIe1 AXI clock
|
||||
|
||||
- resets:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: List of phandle and reset specifier pairs as listed
|
||||
in reset-names property
|
||||
|
||||
- reset-names:
|
||||
Usage: required for ipq/apq8064
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "axi" AXI reset
|
||||
- "ahb" AHB reset
|
||||
- "por" POR reset
|
||||
- "pci" PCI reset
|
||||
- "phy" PHY reset
|
||||
|
||||
- reset-names:
|
||||
Usage: required for apq8084
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "core" Core reset
|
||||
|
||||
- reset-names:
|
||||
Usage: required for ipq/apq8064
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "axi_m" AXI master reset
|
||||
- "axi_s" AXI slave reset
|
||||
- "pipe" PIPE reset
|
||||
- "axi_m_vmid" VMID reset
|
||||
- "axi_s_xpu" XPU reset
|
||||
- "parf" PARF reset
|
||||
- "phy" PHY reset
|
||||
- "axi_m_sticky" AXI sticky reset
|
||||
- "pipe_sticky" PIPE sticky reset
|
||||
- "pwr" PWR reset
|
||||
- "ahb" AHB reset
|
||||
- "phy_ahb" PHY AHB reset
|
||||
- "ext" EXT reset
|
||||
|
||||
- reset-names:
|
||||
Usage: required for ipq8074
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "pipe" PIPE reset
|
||||
- "sleep" Sleep reset
|
||||
- "sticky" Core Sticky reset
|
||||
- "axi_m" AXI Master reset
|
||||
- "axi_s" AXI Slave reset
|
||||
- "ahb" AHB Reset
|
||||
- "axi_m_sticky" AXI Master Sticky reset
|
||||
|
||||
- reset-names:
|
||||
Usage: required for ipq6018
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "pipe" PIPE reset
|
||||
- "sleep" Sleep reset
|
||||
- "sticky" Core Sticky reset
|
||||
- "axi_m" AXI Master reset
|
||||
- "axi_s" AXI Slave reset
|
||||
- "ahb" AHB Reset
|
||||
- "axi_m_sticky" AXI Master Sticky reset
|
||||
- "axi_s_sticky" AXI Slave Sticky reset
|
||||
|
||||
- reset-names:
|
||||
Usage: required for qcs404
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "axi_m" AXI Master reset
|
||||
- "axi_s" AXI Slave reset
|
||||
- "axi_m_sticky" AXI Master Sticky reset
|
||||
- "pipe_sticky" PIPE sticky reset
|
||||
- "pwr" PWR reset
|
||||
- "ahb" AHB reset
|
||||
|
||||
- reset-names:
|
||||
Usage: required for sc8180x, sdm845, sm8250 and sm8450
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain the following entries
|
||||
- "pci" PCIe core reset
|
||||
|
||||
- power-domains:
|
||||
Usage: required for apq8084 and msm8996/apq8096
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: A phandle and power domain specifier pair to the
|
||||
power domain which is responsible for collapsing
|
||||
and restoring power to the peripheral
|
||||
|
||||
- vdda-supply:
|
||||
Usage: required
|
||||
Value type: <phandle>
|
||||
Definition: A phandle to the core analog power supply
|
||||
|
||||
- vdda_phy-supply:
|
||||
Usage: required for ipq/apq8064
|
||||
Value type: <phandle>
|
||||
Definition: A phandle to the analog power supply for PHY
|
||||
|
||||
- vdda_refclk-supply:
|
||||
Usage: required for ipq/apq8064
|
||||
Value type: <phandle>
|
||||
Definition: A phandle to the analog power supply for IC which generates
|
||||
reference clock
|
||||
- vddpe-3v3-supply:
|
||||
Usage: optional
|
||||
Value type: <phandle>
|
||||
Definition: A phandle to the PCIe endpoint power supply
|
||||
|
||||
- phys:
|
||||
Usage: required for apq8084 and qcs404
|
||||
Value type: <phandle>
|
||||
Definition: List of phandle(s) as listed in phy-names property
|
||||
|
||||
- phy-names:
|
||||
Usage: required for apq8084 and qcs404
|
||||
Value type: <stringlist>
|
||||
Definition: Should contain "pciephy"
|
||||
|
||||
- <name>-gpios:
|
||||
Usage: optional
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: List of phandle and GPIO specifier pairs. Should contain
|
||||
- "perst-gpios" PCIe endpoint reset signal line
|
||||
- "wake-gpios" PCIe endpoint wake signal line
|
||||
|
||||
* Example for ipq/apq8064
|
||||
pcie@1b500000 {
|
||||
compatible = "qcom,pcie-apq8064", "qcom,pcie-ipq8064", "snps,dw-pcie";
|
||||
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 /* I/O */
|
||||
0x82000000 0 0 0x08000000 0 0x07e00000>; /* memory */
|
||||
interrupts = <GIC_SPI 238 IRQ_TYPE_NONE>;
|
||||
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>, /* int_a */
|
||||
<0 0 0 2 &intc 0 37 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
|
||||
<0 0 0 3 &intc 0 38 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
|
||||
<0 0 0 4 &intc 0 39 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
|
||||
clocks = <&gcc PCIE_A_CLK>,
|
||||
<&gcc PCIE_H_CLK>,
|
||||
<&gcc PCIE_PHY_CLK>,
|
||||
<&gcc PCIE_AUX_CLK>,
|
||||
<&gcc PCIE_ALT_REF_CLK>;
|
||||
clock-names = "core", "iface", "phy", "aux", "ref";
|
||||
resets = <&gcc PCIE_ACLK_RESET>,
|
||||
<&gcc PCIE_HCLK_RESET>,
|
||||
<&gcc PCIE_POR_RESET>,
|
||||
<&gcc PCIE_PCI_RESET>,
|
||||
<&gcc PCIE_PHY_RESET>,
|
||||
<&gcc PCIE_EXT_RESET>;
|
||||
reset-names = "axi", "ahb", "por", "pci", "phy", "ext";
|
||||
pinctrl-0 = <&pcie_pins_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
|
||||
* Example for apq8084
|
||||
pcie0@fc520000 {
|
||||
compatible = "qcom,pcie-apq8084", "snps,dw-pcie";
|
||||
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 /* I/O */
|
||||
0x82000000 0 0x00300000 0xff300000 0 0x00d00000>; /* memory */
|
||||
interrupts = <GIC_SPI 243 IRQ_TYPE_NONE>;
|
||||
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>, /* int_a */
|
||||
<0 0 0 2 &intc 0 245 IRQ_TYPE_LEVEL_HIGH>, /* int_b */
|
||||
<0 0 0 3 &intc 0 247 IRQ_TYPE_LEVEL_HIGH>, /* int_c */
|
||||
<0 0 0 4 &intc 0 248 IRQ_TYPE_LEVEL_HIGH>; /* int_d */
|
||||
clocks = <&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_AUX_CLK>;
|
||||
clock-names = "iface", "master_bus", "slave_bus", "aux";
|
||||
resets = <&gcc GCC_PCIE_0_BCR>;
|
||||
reset-names = "core";
|
||||
power-domains = <&gcc PCIE0_GDSC>;
|
||||
vdda-supply = <&pma8084_l3>;
|
||||
phys = <&pciephy0>;
|
||||
phy-names = "pciephy";
|
||||
perst-gpio = <&tlmm 70 GPIO_ACTIVE_LOW>;
|
||||
pinctrl-0 = <&pcie0_pins_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
714
Documentation/devicetree/bindings/pci/qcom,pcie.yaml
Normal file
714
Documentation/devicetree/bindings/pci/qcom,pcie.yaml
Normal file
|
|
@ -0,0 +1,714 @@
|
|||
# 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>
|
||||
- Stanimir Varbanov <svarbanov@mm-sol.com>
|
||||
|
||||
description: |
|
||||
Qualcomm PCIe root complex controller is bansed on the Synopsys DesignWare
|
||||
PCIe IP.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,pcie-ipq8064
|
||||
- qcom,pcie-ipq8064-v2
|
||||
- qcom,pcie-apq8064
|
||||
- qcom,pcie-apq8084
|
||||
- qcom,pcie-msm8996
|
||||
- qcom,pcie-ipq4019
|
||||
- qcom,pcie-ipq8074
|
||||
- qcom,pcie-qcs404
|
||||
- qcom,pcie-sc7280
|
||||
- qcom,pcie-sc8180x
|
||||
- qcom,pcie-sdm845
|
||||
- qcom,pcie-sm8150
|
||||
- qcom,pcie-sm8250
|
||||
- qcom,pcie-sm8450-pcie0
|
||||
- qcom,pcie-sm8450-pcie1
|
||||
- qcom,pcie-ipq6018
|
||||
|
||||
reg:
|
||||
minItems: 4
|
||||
maxItems: 5
|
||||
|
||||
reg-names:
|
||||
minItems: 4
|
||||
maxItems: 5
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: msi
|
||||
|
||||
# Common definitions for clocks, clock-names and reset.
|
||||
# Platform constraints are described later.
|
||||
clocks:
|
||||
minItems: 3
|
||||
maxItems: 12
|
||||
|
||||
clock-names:
|
||||
minItems: 3
|
||||
maxItems: 12
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
maxItems: 12
|
||||
|
||||
resets-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
|
||||
|
||||
wake-gpios:
|
||||
description: GPIO controlled connection to WAKE# signal
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- "#interrupt-cells"
|
||||
- interrupt-map-mask
|
||||
- interrupt-map
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/pci/pci-bus.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-ipq6018
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 5
|
||||
maxItems: 5
|
||||
reg-names:
|
||||
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
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,pcie-apq8084
|
||||
- qcom,pcie-msm8996
|
||||
- qcom,pcie-sdm845
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 4
|
||||
maxItems: 4
|
||||
reg-names:
|
||||
items:
|
||||
- const: parf # Qualcomm specific registers
|
||||
- const: dbi # DesignWare PCIe registers
|
||||
- const: elbi # External local bus interface registers
|
||||
- const: config # PCIe configuration space
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,pcie-sc7280
|
||||
- qcom,pcie-sc8180x
|
||||
- qcom,pcie-sm8250
|
||||
- qcom,pcie-sm8450-pcie0
|
||||
- qcom,pcie-sm8450-pcie1
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 5
|
||||
maxItems: 5
|
||||
reg-names:
|
||||
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
|
||||
|
||||
- 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-msm8996
|
||||
then:
|
||||
oneOf:
|
||||
- properties:
|
||||
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
|
||||
- properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: pipe # Pipe Clock driving internal logic
|
||||
- const: bus_master # Master AXI clock
|
||||
- const: bus_slave # Slave AXI clock
|
||||
- const: cfg # Configuration clock
|
||||
- const: aux # Auxiliary (AUX) clock
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 5
|
||||
maxItems: 5
|
||||
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
|
||||
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-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-sc7280
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 11
|
||||
maxItems: 11
|
||||
clock-names:
|
||||
items:
|
||||
- const: pipe # PIPE clock
|
||||
- const: pipe_mux # PIPE MUX
|
||||
- const: phy_pipe # PIPE output clock
|
||||
- const: ref # REFERENCE 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
|
||||
- const: ddrss_sf_tbu # PCIe SF TBU clock
|
||||
resets:
|
||||
maxItems: 1
|
||||
reset-names:
|
||||
items:
|
||||
- const: pci # PCIe core 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-sc8180x
|
||||
- qcom,pcie-sm8150
|
||||
- qcom,pcie-sm8250
|
||||
then:
|
||||
oneOf:
|
||||
# Unfortunately the "optional" ref clock is used in the middle of the list
|
||||
- properties:
|
||||
clocks:
|
||||
minItems: 9
|
||||
maxItems: 9
|
||||
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
|
||||
- const: ddrss_sf_tbu # PCIe SF TBU clock
|
||||
- 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: tbu # PCIe TBU clock
|
||||
- const: ddrss_sf_tbu # PCIe SF TBU clock
|
||||
properties:
|
||||
resets:
|
||||
maxItems: 1
|
||||
reset-names:
|
||||
items:
|
||||
- const: pci # PCIe core reset
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,pcie-sm8450-pcie0
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 12
|
||||
maxItems: 12
|
||||
clock-names:
|
||||
items:
|
||||
- const: pipe # PIPE clock
|
||||
- const: pipe_mux # PIPE MUX
|
||||
- const: phy_pipe # PIPE output clock
|
||||
- const: ref # REFERENCE 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: ddrss_sf_tbu # PCIe SF TBU clock
|
||||
- const: aggre0 # Aggre NoC PCIe0 AXI clock
|
||||
- const: aggre1 # Aggre NoC PCIe1 AXI clock
|
||||
resets:
|
||||
maxItems: 1
|
||||
reset-names:
|
||||
items:
|
||||
- const: pci # PCIe core reset
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,pcie-sm8450-pcie1
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 11
|
||||
maxItems: 11
|
||||
clock-names:
|
||||
items:
|
||||
- const: pipe # PIPE clock
|
||||
- const: pipe_mux # PIPE MUX
|
||||
- const: phy_pipe # PIPE output clock
|
||||
- const: ref # REFERENCE 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: ddrss_sf_tbu # PCIe SF TBU clock
|
||||
- const: aggre1 # Aggre NoC PCIe1 AXI 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-ipq8064
|
||||
- qcom,pcie-ipq8064v2
|
||||
- qcom,pcie-ipq8074
|
||||
- qcom,pcie-qcs404
|
||||
then:
|
||||
required:
|
||||
- power-domains
|
||||
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatibles:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,pcie-msm8996
|
||||
then:
|
||||
required:
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
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";
|
||||
};
|
||||
...
|
||||
|
|
@ -19,20 +19,10 @@ description: |+
|
|||
allOf:
|
||||
- $ref: /schemas/pci/pci-bus.yaml#
|
||||
|
||||
# We need a select here so we don't match all nodes with 'snps,dw-pcie'
|
||||
select:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: rockchip,rk3568-pcie
|
||||
required:
|
||||
- compatible
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- const: rockchip,rk3568-pcie
|
||||
- const: snps,dw-pcie
|
||||
|
||||
reg:
|
||||
items:
|
||||
|
|
@ -110,7 +100,7 @@ examples:
|
|||
#size-cells = <2>;
|
||||
|
||||
pcie3x2: pcie@fe280000 {
|
||||
compatible = "rockchip,rk3568-pcie", "snps,dw-pcie";
|
||||
compatible = "rockchip,rk3568-pcie";
|
||||
reg = <0x3 0xc0800000 0x0 0x390000>,
|
||||
<0x0 0xfe280000 0x0 0x10000>,
|
||||
<0x3 0x80000000 0x0 0x100000>;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
#include <asm/e820/types.h>
|
||||
|
||||
struct device;
|
||||
struct resource;
|
||||
|
||||
extern struct e820_table *e820_table;
|
||||
extern struct e820_table *e820_table_kexec;
|
||||
extern struct e820_table *e820_table_firmware;
|
||||
|
|
@ -43,6 +46,8 @@ extern void e820__register_nosave_regions(unsigned long limit_pfn);
|
|||
|
||||
extern int e820__get_entry_type(u64 start, u64 end);
|
||||
|
||||
extern void remove_e820_regions(struct device *dev, struct resource *avail);
|
||||
|
||||
/*
|
||||
* Returns true iff the specified range [start,end) is completely contained inside
|
||||
* the ISA region.
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ do { \
|
|||
#define PCI_ROOT_NO_CRS 0x100000
|
||||
#define PCI_NOASSIGN_BARS 0x200000
|
||||
#define PCI_BIG_ROOT_WINDOW 0x400000
|
||||
#define PCI_USE_E820 0x800000
|
||||
#define PCI_NO_E820 0x1000000
|
||||
|
||||
extern unsigned int pci_probe;
|
||||
extern unsigned long pirq_table_addr;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <asm/e820/api.h>
|
||||
|
||||
|
|
@ -23,16 +24,27 @@ static void resource_clip(struct resource *res, resource_size_t start,
|
|||
res->start = end + 1;
|
||||
}
|
||||
|
||||
static void remove_e820_regions(struct resource *avail)
|
||||
void remove_e820_regions(struct device *dev, struct resource *avail)
|
||||
{
|
||||
int i;
|
||||
struct e820_entry *entry;
|
||||
u64 e820_start, e820_end;
|
||||
struct resource orig = *avail;
|
||||
|
||||
if (!(avail->flags & IORESOURCE_MEM))
|
||||
return;
|
||||
|
||||
for (i = 0; i < e820_table->nr_entries; i++) {
|
||||
entry = &e820_table->entries[i];
|
||||
e820_start = entry->addr;
|
||||
e820_end = entry->addr + entry->size - 1;
|
||||
|
||||
resource_clip(avail, entry->addr,
|
||||
entry->addr + entry->size - 1);
|
||||
resource_clip(avail, e820_start, e820_end);
|
||||
if (orig.start != avail->start || orig.end != avail->end) {
|
||||
dev_info(dev, "clipped %pR to %pR for e820 entry [mem %#010Lx-%#010Lx]\n",
|
||||
&orig, avail, e820_start, e820_end);
|
||||
orig = *avail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -43,9 +55,6 @@ void arch_remove_reservations(struct resource *avail)
|
|||
* the low 1MB unconditionally, as this area is needed for some ISA
|
||||
* cards requiring a memory range, e.g. the i82365 PCMCIA controller.
|
||||
*/
|
||||
if (avail->flags & IORESOURCE_MEM) {
|
||||
if (avail->flags & IORESOURCE_MEM)
|
||||
resource_clip(avail, BIOS_ROM_BASE, BIOS_ROM_END);
|
||||
|
||||
remove_e820_regions(avail);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/pci-acpi.h>
|
||||
#include <asm/numa.h>
|
||||
#include <asm/pci_x86.h>
|
||||
#include <asm/e820/api.h>
|
||||
|
||||
struct pci_root_info {
|
||||
struct acpi_pci_root_info common;
|
||||
|
|
@ -19,6 +20,7 @@ struct pci_root_info {
|
|||
#endif
|
||||
};
|
||||
|
||||
static bool pci_use_e820 = true;
|
||||
static bool pci_use_crs = true;
|
||||
static bool pci_ignore_seg;
|
||||
|
||||
|
|
@ -41,6 +43,14 @@ static int __init set_ignore_seg(const struct dmi_system_id *id)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init set_no_e820(const struct dmi_system_id *id)
|
||||
{
|
||||
printk(KERN_INFO "PCI: %s detected: not clipping E820 regions from _CRS\n",
|
||||
id->ident);
|
||||
pci_use_e820 = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id pci_crs_quirks[] __initconst = {
|
||||
/* http://bugzilla.kernel.org/show_bug.cgi?id=14183 */
|
||||
{
|
||||
|
|
@ -135,6 +145,51 @@ static const struct dmi_system_id pci_crs_quirks[] __initconst = {
|
|||
DMI_MATCH(DMI_PRODUCT_NAME, "HP xw9300 Workstation"),
|
||||
},
|
||||
},
|
||||
|
||||
/*
|
||||
* Many Lenovo models with "IIL" in their DMI_PRODUCT_VERSION have
|
||||
* an E820 reserved region that covers the entire 32-bit host
|
||||
* bridge memory window from _CRS. Using the E820 region to clip
|
||||
* _CRS means no space is available for hot-added or uninitialized
|
||||
* PCI devices. This typically breaks I2C controllers for touchpads
|
||||
* and hot-added Thunderbolt devices. See the commit log for
|
||||
* models known to require this quirk and related bug reports.
|
||||
*/
|
||||
{
|
||||
.callback = set_no_e820,
|
||||
.ident = "Lenovo *IIL* product version",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "IIL"),
|
||||
},
|
||||
},
|
||||
|
||||
/*
|
||||
* The Acer Spin 5 (SP513-54N) has the same E820 reservation covering
|
||||
* the entire _CRS 32-bit window issue as the Lenovo *IIL* models.
|
||||
* See https://bugs.launchpad.net/bugs/1884232
|
||||
*/
|
||||
{
|
||||
.callback = set_no_e820,
|
||||
.ident = "Acer Spin 5 (SP513-54N)",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Spin SP513-54N"),
|
||||
},
|
||||
},
|
||||
|
||||
/*
|
||||
* Clevo X170KM-G barebones have the same E820 reservation covering
|
||||
* the entire _CRS 32-bit window issue as the Lenovo *IIL* models.
|
||||
* See https://bugzilla.kernel.org/show_bug.cgi?id=214259
|
||||
*/
|
||||
{
|
||||
.callback = set_no_e820,
|
||||
.ident = "Clevo X170KM-G Barebone",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_NAME, "X170KM-G"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
@ -145,6 +200,27 @@ void __init pci_acpi_crs_quirks(void)
|
|||
if (year >= 0 && year < 2008 && iomem_resource.end <= 0xffffffff)
|
||||
pci_use_crs = false;
|
||||
|
||||
/*
|
||||
* Some firmware includes unusable space (host bridge registers,
|
||||
* hidden PCI device BARs, etc) in PCI host bridge _CRS. This is a
|
||||
* firmware defect, and 4dc2287c1805 ("x86: avoid E820 regions when
|
||||
* allocating address space") has clipped out the unusable space in
|
||||
* the past.
|
||||
*
|
||||
* But other firmware supplies E820 reserved regions that cover
|
||||
* entire _CRS windows, so clipping throws away the entire window,
|
||||
* leaving none for hot-added or uninitialized devices. These E820
|
||||
* entries are probably *not* a firmware defect, so disable the
|
||||
* clipping by default for post-2022 machines.
|
||||
*
|
||||
* We already have quirks to disable clipping for pre-2023
|
||||
* machines, and we'll likely need quirks to *enable* clipping for
|
||||
* post-2022 machines that incorrectly include unusable space in
|
||||
* _CRS.
|
||||
*/
|
||||
if (year >= 2023)
|
||||
pci_use_e820 = false;
|
||||
|
||||
dmi_check_system(pci_crs_quirks);
|
||||
|
||||
/*
|
||||
|
|
@ -160,6 +236,17 @@ void __init pci_acpi_crs_quirks(void)
|
|||
"if necessary, use \"pci=%s\" and report a bug\n",
|
||||
pci_use_crs ? "Using" : "Ignoring",
|
||||
pci_use_crs ? "nocrs" : "use_crs");
|
||||
|
||||
/* "pci=use_e820"/"pci=no_e820" on the kernel cmdline takes precedence */
|
||||
if (pci_probe & PCI_NO_E820)
|
||||
pci_use_e820 = false;
|
||||
else if (pci_probe & PCI_USE_E820)
|
||||
pci_use_e820 = true;
|
||||
|
||||
printk(KERN_INFO "PCI: %s E820 reservations for host bridge windows\n",
|
||||
pci_use_e820 ? "Using" : "Ignoring");
|
||||
if (pci_probe & (PCI_NO_E820 | PCI_USE_E820))
|
||||
printk(KERN_INFO "PCI: Please notify linux-pci@vger.kernel.org so future kernels can this automatically\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI_MMCONFIG
|
||||
|
|
@ -299,6 +386,12 @@ static int pci_acpi_root_prepare_resources(struct acpi_pci_root_info *ci)
|
|||
int status;
|
||||
|
||||
status = acpi_pci_probe_root_resources(ci);
|
||||
|
||||
if (pci_use_e820) {
|
||||
resource_list_for_each_entry(entry, &ci->resources)
|
||||
remove_e820_regions(&device->dev, entry->res);
|
||||
}
|
||||
|
||||
if (pci_use_crs) {
|
||||
resource_list_for_each_entry_safe(entry, tmp, &ci->resources)
|
||||
if (resource_is_pcicfg_ioport(entry->res))
|
||||
|
|
|
|||
|
|
@ -595,6 +595,14 @@ char *__init pcibios_setup(char *str)
|
|||
} else if (!strcmp(str, "nocrs")) {
|
||||
pci_probe |= PCI_ROOT_NO_CRS;
|
||||
return NULL;
|
||||
} else if (!strcmp(str, "use_e820")) {
|
||||
pci_probe |= PCI_USE_E820;
|
||||
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
|
||||
return NULL;
|
||||
} else if (!strcmp(str, "no_e820")) {
|
||||
pci_probe |= PCI_NO_E820;
|
||||
add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
|
||||
return NULL;
|
||||
#ifdef CONFIG_PHYS_ADDR_T_64BIT
|
||||
} else if (!strcmp(str, "big_root_window")) {
|
||||
pci_probe |= PCI_BIG_ROOT_WINDOW;
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ struct j721e_pcie_data {
|
|||
enum j721e_pcie_mode mode;
|
||||
unsigned int quirk_retrain_flag:1;
|
||||
unsigned int quirk_detect_quiet_flag:1;
|
||||
unsigned int quirk_disable_flr:1;
|
||||
u32 linkdown_irq_regfield;
|
||||
unsigned int byte_access_allowed:1;
|
||||
};
|
||||
|
|
@ -307,6 +308,7 @@ static const struct j721e_pcie_data j7200_pcie_rc_data = {
|
|||
static const struct j721e_pcie_data j7200_pcie_ep_data = {
|
||||
.mode = PCI_MODE_EP,
|
||||
.quirk_detect_quiet_flag = true,
|
||||
.quirk_disable_flr = true,
|
||||
};
|
||||
|
||||
static const struct j721e_pcie_data am64_pcie_rc_data = {
|
||||
|
|
@ -405,6 +407,7 @@ static int j721e_pcie_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
ep->quirk_detect_quiet_flag = data->quirk_detect_quiet_flag;
|
||||
ep->quirk_disable_flr = data->quirk_disable_flr;
|
||||
|
||||
cdns_pcie = &ep->pcie;
|
||||
cdns_pcie->dev = dev;
|
||||
|
|
|
|||
|
|
@ -187,8 +187,7 @@ static int cdns_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn,
|
|||
struct cdns_pcie *pcie = &ep->pcie;
|
||||
u32 r;
|
||||
|
||||
r = find_first_zero_bit(&ep->ob_region_map,
|
||||
sizeof(ep->ob_region_map) * BITS_PER_LONG);
|
||||
r = find_first_zero_bit(&ep->ob_region_map, BITS_PER_LONG);
|
||||
if (r >= ep->max_regions - 1) {
|
||||
dev_err(&epc->dev, "no free outbound region\n");
|
||||
return -EINVAL;
|
||||
|
|
@ -565,7 +564,8 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
|
|||
struct cdns_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
struct cdns_pcie *pcie = &ep->pcie;
|
||||
struct device *dev = pcie->dev;
|
||||
int ret;
|
||||
int max_epfs = sizeof(epc->function_num_map) * 8;
|
||||
int ret, value, epf;
|
||||
|
||||
/*
|
||||
* BIT(0) is hardwired to 1, hence function 0 is always enabled
|
||||
|
|
@ -573,6 +573,21 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
|
|||
*/
|
||||
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, epc->function_num_map);
|
||||
|
||||
if (ep->quirk_disable_flr) {
|
||||
for (epf = 0; epf < max_epfs; epf++) {
|
||||
if (!(epc->function_num_map & BIT(epf)))
|
||||
continue;
|
||||
|
||||
value = cdns_pcie_ep_fn_readl(pcie, epf,
|
||||
CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET +
|
||||
PCI_EXP_DEVCAP);
|
||||
value &= ~PCI_EXP_DEVCAP_FLR;
|
||||
cdns_pcie_ep_fn_writel(pcie, epf,
|
||||
CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET +
|
||||
PCI_EXP_DEVCAP, value);
|
||||
}
|
||||
}
|
||||
|
||||
ret = cdns_pcie_start_link(pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to start link\n");
|
||||
|
|
|
|||
|
|
@ -123,6 +123,14 @@ static int cdns_pcie_retrain(struct cdns_pcie *pcie)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void cdns_pcie_host_enable_ptm_response(struct cdns_pcie *pcie)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = cdns_pcie_readl(pcie, CDNS_PCIE_LM_PTM_CTRL);
|
||||
cdns_pcie_writel(pcie, CDNS_PCIE_LM_PTM_CTRL, val | CDNS_PCIE_LM_TPM_CTRL_PTMRSEN);
|
||||
}
|
||||
|
||||
static int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc)
|
||||
{
|
||||
struct cdns_pcie *pcie = &rc->pcie;
|
||||
|
|
@ -501,6 +509,8 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
|
|||
if (rc->quirk_detect_quiet_flag)
|
||||
cdns_pcie_detect_quiet_min_delay_set(&rc->pcie);
|
||||
|
||||
cdns_pcie_host_enable_ptm_response(pcie);
|
||||
|
||||
ret = cdns_pcie_start_link(pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to start link\n");
|
||||
|
|
|
|||
|
|
@ -116,6 +116,10 @@
|
|||
#define LM_RC_BAR_CFG_APERTURE(bar, aperture) \
|
||||
(((aperture) - 2) << ((bar) * 8))
|
||||
|
||||
/* PTM Control Register */
|
||||
#define CDNS_PCIE_LM_PTM_CTRL (CDNS_PCIE_LM_BASE + 0x0da8)
|
||||
#define CDNS_PCIE_LM_TPM_CTRL_PTMRSEN BIT(17)
|
||||
|
||||
/*
|
||||
* Endpoint Function Registers (PCI configuration space for endpoint functions)
|
||||
*/
|
||||
|
|
@ -123,6 +127,7 @@
|
|||
|
||||
#define CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET 0x90
|
||||
#define CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET 0xb0
|
||||
#define CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET 0xc0
|
||||
#define CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET 0x200
|
||||
|
||||
/*
|
||||
|
|
@ -357,6 +362,7 @@ struct cdns_pcie_epf {
|
|||
* minimize time between read and write
|
||||
* @epf: Structure to hold info about endpoint function
|
||||
* @quirk_detect_quiet_flag: LTSSM Detect Quiet min delay set as quirk
|
||||
* @quirk_disable_flr: Disable FLR (Function Level Reset) quirk flag
|
||||
*/
|
||||
struct cdns_pcie_ep {
|
||||
struct cdns_pcie pcie;
|
||||
|
|
@ -372,6 +378,7 @@ struct cdns_pcie_ep {
|
|||
spinlock_t lock;
|
||||
struct cdns_pcie_epf *epf;
|
||||
unsigned int quirk_detect_quiet_flag:1;
|
||||
unsigned int quirk_disable_flr:1;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -408,6 +408,11 @@ static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
|
|||
dev_err(dev, "failed to disable vpcie regulator: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
/* Some boards don't have PCIe reset GPIO. */
|
||||
if (gpio_is_valid(imx6_pcie->reset_gpio))
|
||||
gpio_set_value_cansleep(imx6_pcie->reset_gpio,
|
||||
imx6_pcie->gpio_active_high);
|
||||
}
|
||||
|
||||
static unsigned int imx6_pcie_grp_offset(const struct imx6_pcie *imx6_pcie)
|
||||
|
|
@ -540,15 +545,6 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
|
|||
/* allow the clocks to stabilize */
|
||||
usleep_range(200, 500);
|
||||
|
||||
/* Some boards don't have PCIe reset GPIO. */
|
||||
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
|
||||
gpio_set_value_cansleep(imx6_pcie->reset_gpio,
|
||||
imx6_pcie->gpio_active_high);
|
||||
msleep(100);
|
||||
gpio_set_value_cansleep(imx6_pcie->reset_gpio,
|
||||
!imx6_pcie->gpio_active_high);
|
||||
}
|
||||
|
||||
switch (imx6_pcie->drvdata->variant) {
|
||||
case IMX8MQ:
|
||||
reset_control_deassert(imx6_pcie->pciephy_reset);
|
||||
|
|
@ -595,6 +591,15 @@ static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
|
|||
break;
|
||||
}
|
||||
|
||||
/* Some boards don't have PCIe reset GPIO. */
|
||||
if (gpio_is_valid(imx6_pcie->reset_gpio)) {
|
||||
msleep(100);
|
||||
gpio_set_value_cansleep(imx6_pcie->reset_gpio,
|
||||
!imx6_pcie->gpio_active_high);
|
||||
/* Wait for 100ms after PERST# deassertion (PCIe r5.0, 6.6.1) */
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err_ref_clk:
|
||||
|
|
|
|||
|
|
@ -393,7 +393,8 @@ int dw_pcie_host_init(struct pcie_port *pp)
|
|||
sizeof(pp->msi_msg),
|
||||
DMA_FROM_DEVICE,
|
||||
DMA_ATTR_SKIP_CPU_SYNC);
|
||||
if (dma_mapping_error(pci->dev, pp->msi_data)) {
|
||||
ret = dma_mapping_error(pci->dev, pp->msi_data);
|
||||
if (ret) {
|
||||
dev_err(pci->dev, "Failed to map MSI data\n");
|
||||
pp->msi_data = 0;
|
||||
goto err_free_msi;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,12 @@
|
|||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
|
@ -26,6 +29,7 @@
|
|||
*/
|
||||
#define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val))
|
||||
#define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val)
|
||||
#define HIWORD_DISABLE_BIT(val) HIWORD_UPDATE(val, ~val)
|
||||
|
||||
#define to_rockchip_pcie(x) dev_get_drvdata((x)->dev)
|
||||
|
||||
|
|
@ -36,10 +40,12 @@
|
|||
#define PCIE_LINKUP (PCIE_SMLH_LINKUP | PCIE_RDLH_LINKUP)
|
||||
#define PCIE_L0S_ENTRY 0x11
|
||||
#define PCIE_CLIENT_GENERAL_CONTROL 0x0
|
||||
#define PCIE_CLIENT_INTR_STATUS_LEGACY 0x8
|
||||
#define PCIE_CLIENT_INTR_MASK_LEGACY 0x1c
|
||||
#define PCIE_CLIENT_GENERAL_DEBUG 0x104
|
||||
#define PCIE_CLIENT_HOT_RESET_CTRL 0x180
|
||||
#define PCIE_CLIENT_HOT_RESET_CTRL 0x180
|
||||
#define PCIE_CLIENT_LTSSM_STATUS 0x300
|
||||
#define PCIE_LTSSM_ENABLE_ENHANCE BIT(4)
|
||||
#define PCIE_LTSSM_ENABLE_ENHANCE BIT(4)
|
||||
#define PCIE_LTSSM_STATUS_MASK GENMASK(5, 0)
|
||||
|
||||
struct rockchip_pcie {
|
||||
|
|
@ -51,6 +57,7 @@ struct rockchip_pcie {
|
|||
struct reset_control *rst;
|
||||
struct gpio_desc *rst_gpio;
|
||||
struct regulator *vpcie3v3;
|
||||
struct irq_domain *irq_domain;
|
||||
};
|
||||
|
||||
static int rockchip_pcie_readl_apb(struct rockchip_pcie *rockchip,
|
||||
|
|
@ -65,6 +72,78 @@ static void rockchip_pcie_writel_apb(struct rockchip_pcie *rockchip,
|
|||
writel_relaxed(val, rockchip->apb_base + reg);
|
||||
}
|
||||
|
||||
static void rockchip_pcie_legacy_int_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
struct rockchip_pcie *rockchip = irq_desc_get_handler_data(desc);
|
||||
unsigned long reg, hwirq;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_LEGACY);
|
||||
|
||||
for_each_set_bit(hwirq, ®, 4)
|
||||
generic_handle_domain_irq(rockchip->irq_domain, hwirq);
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void rockchip_intx_mask(struct irq_data *data)
|
||||
{
|
||||
rockchip_pcie_writel_apb(irq_data_get_irq_chip_data(data),
|
||||
HIWORD_UPDATE_BIT(BIT(data->hwirq)),
|
||||
PCIE_CLIENT_INTR_MASK_LEGACY);
|
||||
};
|
||||
|
||||
static void rockchip_intx_unmask(struct irq_data *data)
|
||||
{
|
||||
rockchip_pcie_writel_apb(irq_data_get_irq_chip_data(data),
|
||||
HIWORD_DISABLE_BIT(BIT(data->hwirq)),
|
||||
PCIE_CLIENT_INTR_MASK_LEGACY);
|
||||
};
|
||||
|
||||
static struct irq_chip rockchip_intx_irq_chip = {
|
||||
.name = "INTx",
|
||||
.irq_mask = rockchip_intx_mask,
|
||||
.irq_unmask = rockchip_intx_unmask,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND,
|
||||
};
|
||||
|
||||
static int rockchip_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
|
||||
irq_hw_number_t hwirq)
|
||||
{
|
||||
irq_set_chip_and_handler(irq, &rockchip_intx_irq_chip, handle_level_irq);
|
||||
irq_set_chip_data(irq, domain->host_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct irq_domain_ops intx_domain_ops = {
|
||||
.map = rockchip_pcie_intx_map,
|
||||
};
|
||||
|
||||
static int rockchip_pcie_init_irq_domain(struct rockchip_pcie *rockchip)
|
||||
{
|
||||
struct device *dev = rockchip->pci.dev;
|
||||
struct device_node *intc;
|
||||
|
||||
intc = of_get_child_by_name(dev->of_node, "legacy-interrupt-controller");
|
||||
if (!intc) {
|
||||
dev_err(dev, "missing child interrupt-controller node\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rockchip->irq_domain = irq_domain_add_linear(intc, PCI_NUM_INTX,
|
||||
&intx_domain_ops, rockchip);
|
||||
of_node_put(intc);
|
||||
if (!rockchip->irq_domain) {
|
||||
dev_err(dev, "failed to get a INTx IRQ domain\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rockchip_pcie_enable_ltssm(struct rockchip_pcie *rockchip)
|
||||
{
|
||||
rockchip_pcie_writel_apb(rockchip, PCIE_CLIENT_ENABLE_LTSSM,
|
||||
|
|
@ -111,7 +190,20 @@ static int rockchip_pcie_host_init(struct pcie_port *pp)
|
|||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct rockchip_pcie *rockchip = to_rockchip_pcie(pci);
|
||||
struct device *dev = rockchip->pci.dev;
|
||||
u32 val = HIWORD_UPDATE_BIT(PCIE_LTSSM_ENABLE_ENHANCE);
|
||||
int irq, ret;
|
||||
|
||||
irq = of_irq_get_byname(dev->of_node, "legacy");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = rockchip_pcie_init_irq_domain(rockchip);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "failed to init irq domain\n");
|
||||
|
||||
irq_set_chained_handler_and_data(irq, rockchip_pcie_legacy_int_handler,
|
||||
rockchip);
|
||||
|
||||
/* LTSSM enable control mode */
|
||||
rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
|
||||
|
|
@ -152,6 +244,11 @@ static int rockchip_pcie_resource_get(struct platform_device *pdev,
|
|||
if (IS_ERR(rockchip->rst_gpio))
|
||||
return PTR_ERR(rockchip->rst_gpio);
|
||||
|
||||
rockchip->rst = devm_reset_control_array_get_exclusive(&pdev->dev);
|
||||
if (IS_ERR(rockchip->rst))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(rockchip->rst),
|
||||
"failed to get reset lines\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -182,18 +279,6 @@ static void rockchip_pcie_phy_deinit(struct rockchip_pcie *rockchip)
|
|||
phy_power_off(rockchip->phy);
|
||||
}
|
||||
|
||||
static int rockchip_pcie_reset_control_release(struct rockchip_pcie *rockchip)
|
||||
{
|
||||
struct device *dev = rockchip->pci.dev;
|
||||
|
||||
rockchip->rst = devm_reset_control_array_get_exclusive(dev);
|
||||
if (IS_ERR(rockchip->rst))
|
||||
return dev_err_probe(dev, PTR_ERR(rockchip->rst),
|
||||
"failed to get reset lines\n");
|
||||
|
||||
return reset_control_deassert(rockchip->rst);
|
||||
}
|
||||
|
||||
static const struct dw_pcie_ops dw_pcie_ops = {
|
||||
.link_up = rockchip_pcie_link_up,
|
||||
.start_link = rockchip_pcie_start_link,
|
||||
|
|
@ -222,6 +307,10 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_assert(rockchip->rst);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* DON'T MOVE ME: must be enable before PHY init */
|
||||
rockchip->vpcie3v3 = devm_regulator_get_optional(dev, "vpcie3v3");
|
||||
if (IS_ERR(rockchip->vpcie3v3)) {
|
||||
|
|
@ -241,7 +330,7 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto disable_regulator;
|
||||
|
||||
ret = rockchip_pcie_reset_control_release(rockchip);
|
||||
ret = reset_control_deassert(rockchip->rst);
|
||||
if (ret)
|
||||
goto deinit_phy;
|
||||
|
||||
|
|
|
|||
|
|
@ -223,11 +223,8 @@ static void qcom_pcie_dw_stop_link(struct dw_pcie *pci)
|
|||
disable_irq(pcie_ep->perst_irq);
|
||||
}
|
||||
|
||||
static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
|
||||
static int qcom_pcie_enable_resources(struct qcom_pcie_ep *pcie_ep)
|
||||
{
|
||||
struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci);
|
||||
struct device *dev = pci->dev;
|
||||
u32 val, offset;
|
||||
int ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(ARRAY_SIZE(qcom_pcie_ep_clks),
|
||||
|
|
@ -247,6 +244,38 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
|
|||
if (ret)
|
||||
goto err_phy_exit;
|
||||
|
||||
return 0;
|
||||
|
||||
err_phy_exit:
|
||||
phy_exit(pcie_ep->phy);
|
||||
err_disable_clk:
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(qcom_pcie_ep_clks),
|
||||
qcom_pcie_ep_clks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void qcom_pcie_disable_resources(struct qcom_pcie_ep *pcie_ep)
|
||||
{
|
||||
phy_power_off(pcie_ep->phy);
|
||||
phy_exit(pcie_ep->phy);
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(qcom_pcie_ep_clks),
|
||||
qcom_pcie_ep_clks);
|
||||
}
|
||||
|
||||
static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
|
||||
{
|
||||
struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci);
|
||||
struct device *dev = pci->dev;
|
||||
u32 val, offset;
|
||||
int ret;
|
||||
|
||||
ret = qcom_pcie_enable_resources(pcie_ep);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable resources: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Assert WAKE# to RC to indicate device is ready */
|
||||
gpiod_set_value_cansleep(pcie_ep->wake, 1);
|
||||
usleep_range(WAKE_DELAY_US, WAKE_DELAY_US + 500);
|
||||
|
|
@ -335,7 +364,7 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
|
|||
ret = dw_pcie_ep_init_complete(&pcie_ep->pci.ep);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to complete initialization: %d\n", ret);
|
||||
goto err_phy_power_off;
|
||||
goto err_disable_resources;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -355,13 +384,8 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci)
|
|||
|
||||
return 0;
|
||||
|
||||
err_phy_power_off:
|
||||
phy_power_off(pcie_ep->phy);
|
||||
err_phy_exit:
|
||||
phy_exit(pcie_ep->phy);
|
||||
err_disable_clk:
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(qcom_pcie_ep_clks),
|
||||
qcom_pcie_ep_clks);
|
||||
err_disable_resources:
|
||||
qcom_pcie_disable_resources(pcie_ep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -376,10 +400,7 @@ static void qcom_pcie_perst_assert(struct dw_pcie *pci)
|
|||
return;
|
||||
}
|
||||
|
||||
phy_power_off(pcie_ep->phy);
|
||||
phy_exit(pcie_ep->phy);
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(qcom_pcie_ep_clks),
|
||||
qcom_pcie_ep_clks);
|
||||
qcom_pcie_disable_resources(pcie_ep);
|
||||
pcie_ep->link_status = QCOM_PCIE_EP_LINK_DISABLED;
|
||||
}
|
||||
|
||||
|
|
@ -643,43 +664,26 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_bulk_prepare_enable(ARRAY_SIZE(qcom_pcie_ep_clks),
|
||||
qcom_pcie_ep_clks);
|
||||
if (ret)
|
||||
ret = qcom_pcie_enable_resources(pcie_ep);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable resources: %d\n", ret);
|
||||
return ret;
|
||||
|
||||
ret = qcom_pcie_ep_core_reset(pcie_ep);
|
||||
if (ret)
|
||||
goto err_disable_clk;
|
||||
|
||||
ret = phy_init(pcie_ep->phy);
|
||||
if (ret)
|
||||
goto err_disable_clk;
|
||||
|
||||
/* PHY needs to be powered on for dw_pcie_ep_init() */
|
||||
ret = phy_power_on(pcie_ep->phy);
|
||||
if (ret)
|
||||
goto err_phy_exit;
|
||||
}
|
||||
|
||||
ret = dw_pcie_ep_init(&pcie_ep->pci.ep);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize endpoint: %d\n", ret);
|
||||
goto err_phy_power_off;
|
||||
goto err_disable_resources;
|
||||
}
|
||||
|
||||
ret = qcom_pcie_ep_enable_irq_resources(pdev, pcie_ep);
|
||||
if (ret)
|
||||
goto err_phy_power_off;
|
||||
goto err_disable_resources;
|
||||
|
||||
return 0;
|
||||
|
||||
err_phy_power_off:
|
||||
phy_power_off(pcie_ep->phy);
|
||||
err_phy_exit:
|
||||
phy_exit(pcie_ep->phy);
|
||||
err_disable_clk:
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(qcom_pcie_ep_clks),
|
||||
qcom_pcie_ep_clks);
|
||||
err_disable_resources:
|
||||
qcom_pcie_disable_resources(pcie_ep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -691,10 +695,7 @@ static int qcom_pcie_ep_remove(struct platform_device *pdev)
|
|||
if (pcie_ep->link_status == QCOM_PCIE_EP_LINK_DISABLED)
|
||||
return 0;
|
||||
|
||||
phy_power_off(pcie_ep->phy);
|
||||
phy_exit(pcie_ep->phy);
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(qcom_pcie_ep_clks),
|
||||
qcom_pcie_ep_clks);
|
||||
qcom_pcie_disable_resources(pcie_ep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1238,12 +1238,6 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
|
|||
goto err_disable_clocks;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(res->pipe_clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot prepare/enable pipe clock\n");
|
||||
goto err_disable_clocks;
|
||||
}
|
||||
|
||||
/* Wait for reset to complete, required on SM8450 */
|
||||
usleep_range(1000, 1500);
|
||||
|
||||
|
|
@ -1523,6 +1517,13 @@ static const struct qcom_pcie_cfg sdm845_cfg = {
|
|||
.has_tbu_clk = true,
|
||||
};
|
||||
|
||||
static const struct qcom_pcie_cfg sm8150_cfg = {
|
||||
/* sm8150 has qcom IP rev 1.5.0. However 1.5.0 ops are same as
|
||||
* 1.9.0, so reuse the same.
|
||||
*/
|
||||
.ops = &ops_1_9_0,
|
||||
};
|
||||
|
||||
static const struct qcom_pcie_cfg sm8250_cfg = {
|
||||
.ops = &ops_1_9_0,
|
||||
.has_tbu_clk = true,
|
||||
|
|
@ -1627,22 +1628,21 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
|||
pp->ops = &qcom_pcie_dw_ops;
|
||||
|
||||
ret = phy_init(pcie->phy);
|
||||
if (ret) {
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (ret)
|
||||
goto err_pm_runtime_put;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, pcie);
|
||||
|
||||
ret = dw_pcie_host_init(pp);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot initialize host\n");
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
goto err_pm_runtime_put;
|
||||
goto err_phy_exit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_phy_exit:
|
||||
phy_exit(pcie->phy);
|
||||
err_pm_runtime_put:
|
||||
pm_runtime_put(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
|
@ -1660,6 +1660,7 @@ static const struct of_device_id qcom_pcie_match[] = {
|
|||
{ .compatible = "qcom,pcie-ipq4019", .data = &ipq4019_cfg },
|
||||
{ .compatible = "qcom,pcie-qcs404", .data = &ipq4019_cfg },
|
||||
{ .compatible = "qcom,pcie-sdm845", .data = &sdm845_cfg },
|
||||
{ .compatible = "qcom,pcie-sm8150", .data = &sm8150_cfg },
|
||||
{ .compatible = "qcom,pcie-sm8250", .data = &sm8250_cfg },
|
||||
{ .compatible = "qcom,pcie-sc8180x", .data = &sc8180x_cfg },
|
||||
{ .compatible = "qcom,pcie-sm8450-pcie0", .data = &sm8450_pcie0_cfg },
|
||||
|
|
|
|||
|
|
@ -186,8 +186,6 @@
|
|||
#define N_FTS_VAL 52
|
||||
#define FTS_VAL 52
|
||||
|
||||
#define PORT_LOGIC_MSI_CTRL_INT_0_EN 0x828
|
||||
|
||||
#define GEN3_EQ_CONTROL_OFF 0x8a8
|
||||
#define GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_SHIFT 8
|
||||
#define GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC_MASK GENMASK(23, 8)
|
||||
|
|
@ -2189,9 +2187,6 @@ static int tegra194_pcie_suspend_noirq(struct device *dev)
|
|||
if (!pcie->link_state)
|
||||
return 0;
|
||||
|
||||
/* Save MSI interrupt vector */
|
||||
pcie->msi_ctrl_int = dw_pcie_readl_dbi(&pcie->pci,
|
||||
PORT_LOGIC_MSI_CTRL_INT_0_EN);
|
||||
tegra_pcie_downstream_dev_to_D0(pcie);
|
||||
tegra194_pcie_pme_turnoff(pcie);
|
||||
tegra_pcie_unconfig_controller(pcie);
|
||||
|
|
@ -2223,10 +2218,6 @@ static int tegra194_pcie_resume_noirq(struct device *dev)
|
|||
if (ret < 0)
|
||||
goto fail_host_init;
|
||||
|
||||
/* Restore MSI interrupt vector */
|
||||
dw_pcie_writel_dbi(&pcie->pci, PORT_LOGIC_MSI_CTRL_INT_0_EN,
|
||||
pcie->msi_ctrl_int);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_host_init:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
|
|
@ -66,6 +67,12 @@
|
|||
#define PCIE_STAT_BUS 0xff00
|
||||
#define PCIE_STAT_DEV 0x1f0000
|
||||
#define PCIE_STAT_LINK_DOWN BIT(0)
|
||||
#define PCIE_SSPL_OFF 0x1a0c
|
||||
#define PCIE_SSPL_VALUE_SHIFT 0
|
||||
#define PCIE_SSPL_VALUE_MASK GENMASK(7, 0)
|
||||
#define PCIE_SSPL_SCALE_SHIFT 8
|
||||
#define PCIE_SSPL_SCALE_MASK GENMASK(9, 8)
|
||||
#define PCIE_SSPL_ENABLE BIT(16)
|
||||
#define PCIE_RC_RTSTA 0x1a14
|
||||
#define PCIE_DEBUG_CTRL 0x1a60
|
||||
#define PCIE_DEBUG_SOFT_RESET BIT(20)
|
||||
|
|
@ -111,6 +118,8 @@ struct mvebu_pcie_port {
|
|||
struct mvebu_pcie_window iowin;
|
||||
u32 saved_pcie_stat;
|
||||
struct resource regs;
|
||||
u8 slot_power_limit_value;
|
||||
u8 slot_power_limit_scale;
|
||||
struct irq_domain *intx_irq_domain;
|
||||
raw_spinlock_t irq_lock;
|
||||
int intx_irq;
|
||||
|
|
@ -239,7 +248,7 @@ static void mvebu_pcie_setup_wins(struct mvebu_pcie_port *port)
|
|||
|
||||
static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
|
||||
{
|
||||
u32 ctrl, lnkcap, cmd, dev_rev, unmask;
|
||||
u32 ctrl, lnkcap, cmd, dev_rev, unmask, sspl;
|
||||
|
||||
/* Setup PCIe controller to Root Complex mode. */
|
||||
ctrl = mvebu_readl(port, PCIE_CTRL_OFF);
|
||||
|
|
@ -292,6 +301,20 @@ static void mvebu_pcie_setup_hw(struct mvebu_pcie_port *port)
|
|||
/* Point PCIe unit MBUS decode windows to DRAM space. */
|
||||
mvebu_pcie_setup_wins(port);
|
||||
|
||||
/*
|
||||
* Program Root Port to automatically send Set_Slot_Power_Limit
|
||||
* PCIe Message when changing status from Dl_Down to Dl_Up and valid
|
||||
* slot power limit was specified.
|
||||
*/
|
||||
sspl = mvebu_readl(port, PCIE_SSPL_OFF);
|
||||
sspl &= ~(PCIE_SSPL_VALUE_MASK | PCIE_SSPL_SCALE_MASK | PCIE_SSPL_ENABLE);
|
||||
if (port->slot_power_limit_value) {
|
||||
sspl |= port->slot_power_limit_value << PCIE_SSPL_VALUE_SHIFT;
|
||||
sspl |= port->slot_power_limit_scale << PCIE_SSPL_SCALE_SHIFT;
|
||||
sspl |= PCIE_SSPL_ENABLE;
|
||||
}
|
||||
mvebu_writel(port, sspl, PCIE_SSPL_OFF);
|
||||
|
||||
/* Mask all interrupt sources. */
|
||||
mvebu_writel(port, ~PCIE_INT_ALL_MASK, PCIE_INT_UNMASK_OFF);
|
||||
|
||||
|
|
@ -628,9 +651,24 @@ mvebu_pci_bridge_emul_pcie_conf_read(struct pci_bridge_emul *bridge,
|
|||
(PCI_EXP_LNKSTA_DLLLA << 16) : 0);
|
||||
break;
|
||||
|
||||
case PCI_EXP_SLTCTL:
|
||||
*value = PCI_EXP_SLTSTA_PDS << 16;
|
||||
case PCI_EXP_SLTCTL: {
|
||||
u16 slotctl = le16_to_cpu(bridge->pcie_conf.slotctl);
|
||||
u16 slotsta = le16_to_cpu(bridge->pcie_conf.slotsta);
|
||||
u32 val = 0;
|
||||
/*
|
||||
* When slot power limit was not specified in DT then
|
||||
* ASPL_DISABLE bit is stored only in emulated config space.
|
||||
* Otherwise reflect status of PCIE_SSPL_ENABLE bit in HW.
|
||||
*/
|
||||
if (!port->slot_power_limit_value)
|
||||
val |= slotctl & PCI_EXP_SLTCTL_ASPL_DISABLE;
|
||||
else if (!(mvebu_readl(port, PCIE_SSPL_OFF) & PCIE_SSPL_ENABLE))
|
||||
val |= PCI_EXP_SLTCTL_ASPL_DISABLE;
|
||||
/* This callback is 32-bit and in high bits is slot status. */
|
||||
val |= slotsta << 16;
|
||||
*value = val;
|
||||
break;
|
||||
}
|
||||
|
||||
case PCI_EXP_RTSTA:
|
||||
*value = mvebu_readl(port, PCIE_RC_RTSTA);
|
||||
|
|
@ -774,6 +812,22 @@ mvebu_pci_bridge_emul_pcie_conf_write(struct pci_bridge_emul *bridge,
|
|||
mvebu_writel(port, new, PCIE_CAP_PCIEXP + PCI_EXP_LNKCTL);
|
||||
break;
|
||||
|
||||
case PCI_EXP_SLTCTL:
|
||||
/*
|
||||
* Allow to change PCIE_SSPL_ENABLE bit only when slot power
|
||||
* limit was specified in DT and configured into HW.
|
||||
*/
|
||||
if ((mask & PCI_EXP_SLTCTL_ASPL_DISABLE) &&
|
||||
port->slot_power_limit_value) {
|
||||
u32 sspl = mvebu_readl(port, PCIE_SSPL_OFF);
|
||||
if (new & PCI_EXP_SLTCTL_ASPL_DISABLE)
|
||||
sspl &= ~PCIE_SSPL_ENABLE;
|
||||
else
|
||||
sspl |= PCIE_SSPL_ENABLE;
|
||||
mvebu_writel(port, sspl, PCIE_SSPL_OFF);
|
||||
}
|
||||
break;
|
||||
|
||||
case PCI_EXP_RTSTA:
|
||||
/*
|
||||
* PME Status bit in Root Status Register (PCIE_RC_RTSTA)
|
||||
|
|
@ -868,8 +922,26 @@ static int mvebu_pci_bridge_emul_init(struct mvebu_pcie_port *port)
|
|||
/*
|
||||
* Older mvebu hardware provides PCIe Capability structure only in
|
||||
* version 1. New hardware provides it in version 2.
|
||||
* Enable slot support which is emulated.
|
||||
*/
|
||||
bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver);
|
||||
bridge->pcie_conf.cap = cpu_to_le16(pcie_cap_ver | PCI_EXP_FLAGS_SLOT);
|
||||
|
||||
/*
|
||||
* Set Presence Detect State bit permanently as there is no support for
|
||||
* unplugging PCIe card from the slot. Assume that PCIe card is always
|
||||
* connected in slot.
|
||||
*
|
||||
* Set physical slot number to port+1 as mvebu ports are indexed from
|
||||
* zero and zero value is reserved for ports within the same silicon
|
||||
* as Root Port which is not mvebu case.
|
||||
*
|
||||
* Also set correct slot power limit.
|
||||
*/
|
||||
bridge->pcie_conf.slotcap = cpu_to_le32(
|
||||
FIELD_PREP(PCI_EXP_SLTCAP_SPLV, port->slot_power_limit_value) |
|
||||
FIELD_PREP(PCI_EXP_SLTCAP_SPLS, port->slot_power_limit_scale) |
|
||||
FIELD_PREP(PCI_EXP_SLTCAP_PSN, port->port+1));
|
||||
bridge->pcie_conf.slotsta = cpu_to_le16(PCI_EXP_SLTSTA_PDS);
|
||||
|
||||
bridge->subsystem_vendor_id = ssdev_id & 0xffff;
|
||||
bridge->subsystem_id = ssdev_id >> 16;
|
||||
|
|
@ -1191,6 +1263,7 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
|
|||
{
|
||||
struct device *dev = &pcie->pdev->dev;
|
||||
enum of_gpio_flags flags;
|
||||
u32 slot_power_limit;
|
||||
int reset_gpio, ret;
|
||||
u32 num_lanes;
|
||||
|
||||
|
|
@ -1291,6 +1364,15 @@ static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
|
|||
port->reset_gpio = gpio_to_desc(reset_gpio);
|
||||
}
|
||||
|
||||
slot_power_limit = of_pci_get_slot_power_limit(child,
|
||||
&port->slot_power_limit_value,
|
||||
&port->slot_power_limit_scale);
|
||||
if (slot_power_limit)
|
||||
dev_info(dev, "%s: Slot power limit %u.%uW\n",
|
||||
port->name,
|
||||
slot_power_limit / 1000,
|
||||
(slot_power_limit / 100) % 10);
|
||||
|
||||
port->clk = of_clk_get_by_name(child, NULL);
|
||||
if (IS_ERR(port->clk)) {
|
||||
dev_err(dev, "%s: cannot get clock\n", port->name);
|
||||
|
|
@ -1588,7 +1670,7 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct mvebu_pcie *pcie = platform_get_drvdata(pdev);
|
||||
struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
|
||||
u32 cmd;
|
||||
u32 cmd, sspl;
|
||||
int i;
|
||||
|
||||
/* Remove PCI bus with all devices. */
|
||||
|
|
@ -1625,6 +1707,11 @@ static int mvebu_pcie_remove(struct platform_device *pdev)
|
|||
/* Free config space for emulated root bridge. */
|
||||
pci_bridge_emul_cleanup(&port->bridge);
|
||||
|
||||
/* Disable sending Set_Slot_Power_Limit PCIe Message. */
|
||||
sspl = mvebu_readl(port, PCIE_SSPL_OFF);
|
||||
sspl &= ~(PCIE_SSPL_VALUE_MASK | PCIE_SSPL_SCALE_MASK | PCIE_SSPL_ENABLE);
|
||||
mvebu_writel(port, sspl, PCIE_SSPL_OFF);
|
||||
|
||||
/* Disable and clear BARs and windows. */
|
||||
mvebu_pcie_disable_wins(port);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,10 +31,9 @@ static u32 pci_slot_ignore;
|
|||
|
||||
static int __init versatile_pci_slot_ignore(char *str)
|
||||
{
|
||||
int retval;
|
||||
int slot;
|
||||
|
||||
while ((retval = get_option(&str, &slot))) {
|
||||
while (get_option(&str, &slot)) {
|
||||
if ((slot < 0) || (slot > 31))
|
||||
pr_err("Illegal slot value: %d\n", slot);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -838,6 +838,14 @@ static int mtk_pcie_setup(struct mtk_gen3_pcie *pcie)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* The controller may have been left out of reset by the bootloader
|
||||
* so make sure that we get a clean start by asserting resets here.
|
||||
*/
|
||||
reset_control_assert(pcie->phy_reset);
|
||||
reset_control_assert(pcie->mac_reset);
|
||||
usleep_range(10, 20);
|
||||
|
||||
/* Don't touch the hardware registers before power up */
|
||||
err = mtk_pcie_power_up(pcie);
|
||||
if (err)
|
||||
|
|
|
|||
|
|
@ -1008,6 +1008,7 @@ static int mtk_pcie_subsys_powerup(struct mtk_pcie *pcie)
|
|||
"mediatek,generic-pciecfg");
|
||||
if (cfg_node) {
|
||||
pcie->cfg = syscon_node_to_regmap(cfg_node);
|
||||
of_node_put(cfg_node);
|
||||
if (IS_ERR(pcie->cfg))
|
||||
return PTR_ERR(pcie->cfg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -406,6 +406,7 @@ static void mc_pcie_enable_msi(struct mc_pcie *port, void __iomem *base)
|
|||
static void mc_handle_msi(struct irq_desc *desc)
|
||||
{
|
||||
struct mc_pcie *port = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
struct device *dev = port->dev;
|
||||
struct mc_msi *msi = &port->msi;
|
||||
void __iomem *bridge_base_addr =
|
||||
|
|
@ -414,8 +415,11 @@ static void mc_handle_msi(struct irq_desc *desc)
|
|||
u32 bit;
|
||||
int ret;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
|
||||
if (status & PM_MSI_INT_MSI_MASK) {
|
||||
writel_relaxed(status & PM_MSI_INT_MSI_MASK, bridge_base_addr + ISTATUS_LOCAL);
|
||||
status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
|
||||
for_each_set_bit(bit, &status, msi->num_vectors) {
|
||||
ret = generic_handle_domain_irq(msi->dev_domain, bit);
|
||||
|
|
@ -424,6 +428,8 @@ static void mc_handle_msi(struct irq_desc *desc)
|
|||
bit);
|
||||
}
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void mc_msi_bottom_irq_ack(struct irq_data *data)
|
||||
|
|
@ -432,13 +438,8 @@ static void mc_msi_bottom_irq_ack(struct irq_data *data)
|
|||
void __iomem *bridge_base_addr =
|
||||
port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
|
||||
u32 bitpos = data->hwirq;
|
||||
unsigned long status;
|
||||
|
||||
writel_relaxed(BIT(bitpos), bridge_base_addr + ISTATUS_MSI);
|
||||
status = readl_relaxed(bridge_base_addr + ISTATUS_MSI);
|
||||
if (!status)
|
||||
writel_relaxed(BIT(PM_MSI_INT_MSI_SHIFT),
|
||||
bridge_base_addr + ISTATUS_LOCAL);
|
||||
}
|
||||
|
||||
static void mc_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
|
|
@ -563,6 +564,7 @@ static int mc_allocate_msi_domains(struct mc_pcie *port)
|
|||
static void mc_handle_intx(struct irq_desc *desc)
|
||||
{
|
||||
struct mc_pcie *port = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
struct device *dev = port->dev;
|
||||
void __iomem *bridge_base_addr =
|
||||
port->axi_base_addr + MC_PCIE_BRIDGE_ADDR;
|
||||
|
|
@ -570,6 +572,8 @@ static void mc_handle_intx(struct irq_desc *desc)
|
|||
u32 bit;
|
||||
int ret;
|
||||
|
||||
chained_irq_enter(chip, desc);
|
||||
|
||||
status = readl_relaxed(bridge_base_addr + ISTATUS_LOCAL);
|
||||
if (status & PM_MSI_INT_INTX_MASK) {
|
||||
status &= PM_MSI_INT_INTX_MASK;
|
||||
|
|
@ -581,6 +585,8 @@ static void mc_handle_intx(struct irq_desc *desc)
|
|||
bit);
|
||||
}
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
}
|
||||
|
||||
static void mc_ack_intx_irq(struct irq_data *data)
|
||||
|
|
@ -1115,7 +1121,7 @@ static const struct of_device_id mc_pcie_of_match[] = {
|
|||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, mc_pcie_of_match)
|
||||
MODULE_DEVICE_TABLE(of, mc_pcie_of_match);
|
||||
|
||||
static struct platform_driver mc_pcie_driver = {
|
||||
.probe = pci_host_common_probe,
|
||||
|
|
|
|||
|
|
@ -264,8 +264,7 @@ static int rockchip_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn,
|
|||
struct rockchip_pcie *pcie = &ep->rockchip;
|
||||
u32 r;
|
||||
|
||||
r = find_first_zero_bit(&ep->ob_region_map,
|
||||
sizeof(ep->ob_region_map) * BITS_PER_LONG);
|
||||
r = find_first_zero_bit(&ep->ob_region_map, BITS_PER_LONG);
|
||||
/*
|
||||
* Region 0 is reserved for configuration space and shouldn't
|
||||
* be used elsewhere per TRM, so leave it out.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -813,8 +812,7 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
|
|||
* acceptable because the guest is usually CPU-limited and MSI
|
||||
* remapping doesn't become a performance bottleneck.
|
||||
*/
|
||||
if (iommu_capable(vmd->dev->dev.bus, IOMMU_CAP_INTR_REMAP) ||
|
||||
!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) ||
|
||||
if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) ||
|
||||
offset[0] || offset[1]) {
|
||||
ret = vmd_alloc_irqs(vmd);
|
||||
if (ret)
|
||||
|
|
@ -853,6 +851,9 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)
|
|||
vmd_attach_resources(vmd);
|
||||
if (vmd->irq_domain)
|
||||
dev_set_msi_domain(&vmd->bus->dev, vmd->irq_domain);
|
||||
else
|
||||
dev_set_msi_domain(&vmd->bus->dev,
|
||||
dev_get_msi_domain(&vmd->dev->dev));
|
||||
|
||||
vmd_acpi_begin();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/of_fdt.h>
|
||||
|
||||
#include <asm/opal.h>
|
||||
#include <asm/pnv-pci.h>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci_hotplug.h>
|
||||
#include <linux/smp.h>
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
#include <asm/eeh.h> /* for eeh_add_device() */
|
||||
#include <asm/rtas.h> /* rtas_call */
|
||||
#include <asm/pci-bridge.h> /* for pci_controller */
|
||||
#include <asm/prom.h>
|
||||
#include "../pci.h" /* for pci_add_new_bus */
|
||||
/* and pci_do_scan_bus */
|
||||
#include "rpaphp.h"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
* Send feedback to <lxie@us.ibm.com>
|
||||
*
|
||||
*/
|
||||
#include <linux/of.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
|||
|
|
@ -633,3 +633,73 @@ int of_pci_get_max_link_speed(struct device_node *node)
|
|||
return max_link_speed;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_pci_get_max_link_speed);
|
||||
|
||||
/**
|
||||
* of_pci_get_slot_power_limit - Parses the "slot-power-limit-milliwatt"
|
||||
* property.
|
||||
*
|
||||
* @node: device tree node with the slot power limit information
|
||||
* @slot_power_limit_value: pointer where the value should be stored in PCIe
|
||||
* Slot Capabilities Register format
|
||||
* @slot_power_limit_scale: pointer where the scale should be stored in PCIe
|
||||
* Slot Capabilities Register format
|
||||
*
|
||||
* Returns the slot power limit in milliwatts and if @slot_power_limit_value
|
||||
* and @slot_power_limit_scale pointers are non-NULL, fills in the value and
|
||||
* scale in format used by PCIe Slot Capabilities Register.
|
||||
*
|
||||
* If the property is not found or is invalid, returns 0.
|
||||
*/
|
||||
u32 of_pci_get_slot_power_limit(struct device_node *node,
|
||||
u8 *slot_power_limit_value,
|
||||
u8 *slot_power_limit_scale)
|
||||
{
|
||||
u32 slot_power_limit_mw;
|
||||
u8 value, scale;
|
||||
|
||||
if (of_property_read_u32(node, "slot-power-limit-milliwatt",
|
||||
&slot_power_limit_mw))
|
||||
slot_power_limit_mw = 0;
|
||||
|
||||
/* Calculate Slot Power Limit Value and Slot Power Limit Scale */
|
||||
if (slot_power_limit_mw == 0) {
|
||||
value = 0x00;
|
||||
scale = 0;
|
||||
} else if (slot_power_limit_mw <= 255) {
|
||||
value = slot_power_limit_mw;
|
||||
scale = 3;
|
||||
} else if (slot_power_limit_mw <= 255*10) {
|
||||
value = slot_power_limit_mw / 10;
|
||||
scale = 2;
|
||||
slot_power_limit_mw = slot_power_limit_mw / 10 * 10;
|
||||
} else if (slot_power_limit_mw <= 255*100) {
|
||||
value = slot_power_limit_mw / 100;
|
||||
scale = 1;
|
||||
slot_power_limit_mw = slot_power_limit_mw / 100 * 100;
|
||||
} else if (slot_power_limit_mw <= 239*1000) {
|
||||
value = slot_power_limit_mw / 1000;
|
||||
scale = 0;
|
||||
slot_power_limit_mw = slot_power_limit_mw / 1000 * 1000;
|
||||
} else if (slot_power_limit_mw < 250*1000) {
|
||||
value = 0xEF;
|
||||
scale = 0;
|
||||
slot_power_limit_mw = 239*1000;
|
||||
} else if (slot_power_limit_mw <= 600*1000) {
|
||||
value = 0xF0 + (slot_power_limit_mw / 1000 - 250) / 25;
|
||||
scale = 0;
|
||||
slot_power_limit_mw = slot_power_limit_mw / (1000*25) * (1000*25);
|
||||
} else {
|
||||
value = 0xFE;
|
||||
scale = 0;
|
||||
slot_power_limit_mw = 600*1000;
|
||||
}
|
||||
|
||||
if (slot_power_limit_value)
|
||||
*slot_power_limit_value = value;
|
||||
|
||||
if (slot_power_limit_scale)
|
||||
*slot_power_limit_scale = scale;
|
||||
|
||||
return slot_power_limit_mw;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_pci_get_slot_power_limit);
|
||||
|
|
|
|||
|
|
@ -326,15 +326,16 @@ static const struct pci_p2pdma_whitelist_entry {
|
|||
};
|
||||
|
||||
/*
|
||||
* This lookup function tries to find the PCI device corresponding to a given
|
||||
* host bridge.
|
||||
* If the first device on host's root bus is either devfn 00.0 or a PCIe
|
||||
* Root Port, return it. Otherwise return NULL.
|
||||
*
|
||||
* It assumes the host bridge device is the first PCI device in the
|
||||
* bus->devices list and that the devfn is 00.0. These assumptions should hold
|
||||
* for all the devices in the whitelist above.
|
||||
* We often use a devfn 00.0 "host bridge" in the pci_p2pdma_whitelist[]
|
||||
* (though there is no PCI/PCIe requirement for such a device). On some
|
||||
* platforms, e.g., Intel Skylake, there is no such host bridge device, and
|
||||
* pci_p2pdma_whitelist[] may contain a Root Port at any devfn.
|
||||
*
|
||||
* This function is equivalent to pci_get_slot(host->bus, 0), however it does
|
||||
* not take the pci_bus_sem lock seeing __host_bridge_whitelist() must not
|
||||
* This function is similar to pci_get_slot(host->bus, 0), but it does
|
||||
* not take the pci_bus_sem lock since __host_bridge_whitelist() must not
|
||||
* sleep.
|
||||
*
|
||||
* For this to be safe, the caller should hold a reference to a device on the
|
||||
|
|
@ -350,10 +351,14 @@ static struct pci_dev *pci_host_bridge_dev(struct pci_host_bridge *host)
|
|||
|
||||
if (!root)
|
||||
return NULL;
|
||||
if (root->devfn != PCI_DEVFN(0, 0))
|
||||
return NULL;
|
||||
|
||||
return root;
|
||||
if (root->devfn == PCI_DEVFN(0, 0))
|
||||
return root;
|
||||
|
||||
if (pci_pcie_type(root) == PCI_EXP_TYPE_ROOT_PORT)
|
||||
return root;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool __host_bridge_whitelist(struct pci_host_bridge *host,
|
||||
|
|
|
|||
|
|
@ -974,9 +974,11 @@ bool acpi_pci_power_manageable(struct pci_dev *dev)
|
|||
|
||||
bool acpi_pci_bridge_d3(struct pci_dev *dev)
|
||||
{
|
||||
const union acpi_object *obj;
|
||||
struct acpi_device *adev;
|
||||
struct pci_dev *rpdev;
|
||||
struct acpi_device *adev;
|
||||
acpi_status status;
|
||||
unsigned long long state;
|
||||
const union acpi_object *obj;
|
||||
|
||||
if (acpi_pci_disabled || !dev->is_hotplug_bridge)
|
||||
return false;
|
||||
|
|
@ -985,12 +987,6 @@ bool acpi_pci_bridge_d3(struct pci_dev *dev)
|
|||
if (acpi_pci_power_manageable(dev))
|
||||
return true;
|
||||
|
||||
/*
|
||||
* The ACPI firmware will provide the device-specific properties through
|
||||
* _DSD configuration object. Look for the 'HotPlugSupportInD3' property
|
||||
* for the root port and if it is set we know the hierarchy behind it
|
||||
* supports D3 just fine.
|
||||
*/
|
||||
rpdev = pcie_find_root_port(dev);
|
||||
if (!rpdev)
|
||||
return false;
|
||||
|
|
@ -999,11 +995,34 @@ bool acpi_pci_bridge_d3(struct pci_dev *dev)
|
|||
if (!adev)
|
||||
return false;
|
||||
|
||||
if (acpi_dev_get_property(adev, "HotPlugSupportInD3",
|
||||
ACPI_TYPE_INTEGER, &obj) < 0)
|
||||
/*
|
||||
* If the Root Port cannot signal wakeup signals at all, i.e., it
|
||||
* doesn't supply a wakeup GPE via _PRW, it cannot signal hotplug
|
||||
* events from low-power states including D3hot and D3cold.
|
||||
*/
|
||||
if (!adev->wakeup.flags.valid)
|
||||
return false;
|
||||
|
||||
return obj->integer.value == 1;
|
||||
/*
|
||||
* If the Root Port cannot wake itself from D3hot or D3cold, we
|
||||
* can't use D3.
|
||||
*/
|
||||
status = acpi_evaluate_integer(adev->handle, "_S0W", NULL, &state);
|
||||
if (ACPI_SUCCESS(status) && state < ACPI_STATE_D3_HOT)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* The "HotPlugSupportInD3" property in a Root Port _DSD indicates
|
||||
* the Port can signal hotplug events while in D3. We assume any
|
||||
* bridges *below* that Root Port can also signal hotplug events
|
||||
* while in D3.
|
||||
*/
|
||||
if (!acpi_dev_get_property(adev, "HotPlugSupportInD3",
|
||||
ACPI_TYPE_INTEGER, &obj) &&
|
||||
obj->integer.value == 1)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||
|
|
|
|||
|
|
@ -522,9 +522,9 @@ static void pci_device_shutdown(struct device *dev)
|
|||
pci_clear_master(pci_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
/* Auxiliary functions used for system resume and run-time resume. */
|
||||
/* Auxiliary functions used for system resume */
|
||||
|
||||
/**
|
||||
* pci_restore_standard_config - restore standard config registers of PCI device
|
||||
|
|
@ -544,6 +544,11 @@ static int pci_restore_standard_config(struct pci_dev *pci_dev)
|
|||
pci_pme_restore(pci_dev);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/* Auxiliary functions used for system resume and run-time resume */
|
||||
|
||||
static void pci_pm_default_resume(struct pci_dev *pci_dev)
|
||||
{
|
||||
|
|
@ -551,18 +556,34 @@ static void pci_pm_default_resume(struct pci_dev *pci_dev)
|
|||
pci_enable_wake(pci_dev, PCI_D0, false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static void pci_pm_default_resume_early(struct pci_dev *pci_dev)
|
||||
static void pci_pm_power_up_and_verify_state(struct pci_dev *pci_dev)
|
||||
{
|
||||
pci_power_up(pci_dev);
|
||||
pci_update_current_state(pci_dev, PCI_D0);
|
||||
}
|
||||
|
||||
static void pci_pm_default_resume_early(struct pci_dev *pci_dev)
|
||||
{
|
||||
pci_pm_power_up_and_verify_state(pci_dev);
|
||||
pci_restore_state(pci_dev);
|
||||
pci_pme_restore(pci_dev);
|
||||
}
|
||||
|
||||
static void pci_pm_bridge_power_up_actions(struct pci_dev *pci_dev)
|
||||
{
|
||||
pci_bridge_wait_for_secondary_bus(pci_dev);
|
||||
/*
|
||||
* When powering on a bridge from D3cold, the whole hierarchy may be
|
||||
* powered on into D0uninitialized state, resume them to give them a
|
||||
* chance to suspend again
|
||||
*/
|
||||
pci_resume_bus(pci_dev->subordinate);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
/*
|
||||
* Default "suspend" method for devices that have no driver provided suspend,
|
||||
* or not even a driver at all (second part).
|
||||
|
|
@ -934,7 +955,7 @@ static int pci_pm_resume_noirq(struct device *dev)
|
|||
pcie_pme_root_status_cleanup(pci_dev);
|
||||
|
||||
if (!skip_bus_pm && prev_state == PCI_D3cold)
|
||||
pci_bridge_wait_for_secondary_bus(pci_dev);
|
||||
pci_pm_bridge_power_up_actions(pci_dev);
|
||||
|
||||
if (pci_has_legacy_pm_support(pci_dev))
|
||||
return 0;
|
||||
|
|
@ -1068,7 +1089,7 @@ static int pci_pm_thaw_noirq(struct device *dev)
|
|||
* in case the driver's "freeze" callbacks put it into a low-power
|
||||
* state.
|
||||
*/
|
||||
pci_set_power_state(pci_dev, PCI_D0);
|
||||
pci_pm_power_up_and_verify_state(pci_dev);
|
||||
pci_restore_state(pci_dev);
|
||||
|
||||
if (pci_has_legacy_pm_support(pci_dev))
|
||||
|
|
@ -1312,7 +1333,7 @@ static int pci_pm_runtime_resume(struct device *dev)
|
|||
* to a driver because although we left it in D0, it may have gone to
|
||||
* D3cold when the bridge above it runtime suspended.
|
||||
*/
|
||||
pci_restore_standard_config(pci_dev);
|
||||
pci_pm_default_resume_early(pci_dev);
|
||||
|
||||
if (!pci_dev->driver)
|
||||
return 0;
|
||||
|
|
@ -1321,13 +1342,11 @@ static int pci_pm_runtime_resume(struct device *dev)
|
|||
pci_pm_default_resume(pci_dev);
|
||||
|
||||
if (prev_state == PCI_D3cold)
|
||||
pci_bridge_wait_for_secondary_bus(pci_dev);
|
||||
pci_pm_bridge_power_up_actions(pci_dev);
|
||||
|
||||
if (pm && pm->runtime_resume)
|
||||
error = pm->runtime_resume(dev);
|
||||
|
||||
pci_dev->runtime_d3cold = false;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1067,126 +1067,6 @@ static inline bool platform_pci_bridge_d3(struct pci_dev *dev)
|
|||
return acpi_pci_bridge_d3(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_raw_set_power_state - Use PCI PM registers to set the power state of
|
||||
* given PCI device
|
||||
* @dev: PCI device to handle.
|
||||
* @state: PCI power state (D0, D1, D2, D3hot) to put the device into.
|
||||
*
|
||||
* RETURN VALUE:
|
||||
* -EINVAL if the requested state is invalid.
|
||||
* -EIO if device does not support PCI PM or its PM capabilities register has a
|
||||
* wrong version, or device doesn't support the requested state.
|
||||
* 0 if device already is in the requested state.
|
||||
* 0 if device's power state has been successfully changed.
|
||||
*/
|
||||
static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
|
||||
{
|
||||
u16 pmcsr;
|
||||
bool need_restore = false;
|
||||
|
||||
/* Check if we're already there */
|
||||
if (dev->current_state == state)
|
||||
return 0;
|
||||
|
||||
if (!dev->pm_cap)
|
||||
return -EIO;
|
||||
|
||||
if (state < PCI_D0 || state > PCI_D3hot)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Validate transition: We can enter D0 from any state, but if
|
||||
* we're already in a low-power state, we can only go deeper. E.g.,
|
||||
* we can go from D1 to D3, but we can't go directly from D3 to D1;
|
||||
* we'd have to go from D3 to D0, then to D1.
|
||||
*/
|
||||
if (state != PCI_D0 && dev->current_state <= PCI_D3cold
|
||||
&& dev->current_state > state) {
|
||||
pci_err(dev, "invalid power transition (from %s to %s)\n",
|
||||
pci_power_name(dev->current_state),
|
||||
pci_power_name(state));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check if this device supports the desired state */
|
||||
if ((state == PCI_D1 && !dev->d1_support)
|
||||
|| (state == PCI_D2 && !dev->d2_support))
|
||||
return -EIO;
|
||||
|
||||
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
||||
if (PCI_POSSIBLE_ERROR(pmcsr)) {
|
||||
pci_err(dev, "can't change power state from %s to %s (config space inaccessible)\n",
|
||||
pci_power_name(dev->current_state),
|
||||
pci_power_name(state));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're (effectively) in D3, force entire word to 0.
|
||||
* This doesn't affect PME_Status, disables PME_En, and
|
||||
* sets PowerState to 0.
|
||||
*/
|
||||
switch (dev->current_state) {
|
||||
case PCI_D0:
|
||||
case PCI_D1:
|
||||
case PCI_D2:
|
||||
pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
|
||||
pmcsr |= state;
|
||||
break;
|
||||
case PCI_D3hot:
|
||||
case PCI_D3cold:
|
||||
case PCI_UNKNOWN: /* Boot-up */
|
||||
if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot
|
||||
&& !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))
|
||||
need_restore = true;
|
||||
fallthrough; /* force to D0 */
|
||||
default:
|
||||
pmcsr = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enter specified state */
|
||||
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
|
||||
|
||||
/*
|
||||
* Mandatory power management transition delays; see PCI PM 1.1
|
||||
* 5.6.1 table 18
|
||||
*/
|
||||
if (state == PCI_D3hot || dev->current_state == PCI_D3hot)
|
||||
pci_dev_d3_sleep(dev);
|
||||
else if (state == PCI_D2 || dev->current_state == PCI_D2)
|
||||
udelay(PCI_PM_D2_DELAY);
|
||||
|
||||
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
||||
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
|
||||
if (dev->current_state != state)
|
||||
pci_info_ratelimited(dev, "refused to change power state from %s to %s\n",
|
||||
pci_power_name(dev->current_state),
|
||||
pci_power_name(state));
|
||||
|
||||
/*
|
||||
* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
|
||||
* INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
|
||||
* from D3hot to D0 _may_ perform an internal reset, thereby
|
||||
* going to "D0 Uninitialized" rather than "D0 Initialized".
|
||||
* For example, at least some versions of the 3c905B and the
|
||||
* 3c556B exhibit this behaviour.
|
||||
*
|
||||
* At least some laptop BIOSen (e.g. the Thinkpad T21) leave
|
||||
* devices in a D3hot state at boot. Consequently, we need to
|
||||
* restore at least the BARs so that the device will be
|
||||
* accessible to its driver.
|
||||
*/
|
||||
if (need_restore)
|
||||
pci_restore_bars(dev);
|
||||
|
||||
if (dev->bus->self)
|
||||
pcie_aspm_pm_state_change(dev->bus->self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_update_current_state - Read power state of given device and cache it
|
||||
* @dev: PCI device to handle.
|
||||
|
|
@ -1201,14 +1081,17 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|||
*/
|
||||
void pci_update_current_state(struct pci_dev *dev, pci_power_t state)
|
||||
{
|
||||
if (platform_pci_get_power_state(dev) == PCI_D3cold ||
|
||||
!pci_device_is_present(dev)) {
|
||||
if (platform_pci_get_power_state(dev) == PCI_D3cold) {
|
||||
dev->current_state = PCI_D3cold;
|
||||
} else if (dev->pm_cap) {
|
||||
u16 pmcsr;
|
||||
|
||||
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
||||
dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK);
|
||||
if (PCI_POSSIBLE_ERROR(pmcsr)) {
|
||||
dev->current_state = PCI_D3cold;
|
||||
return;
|
||||
}
|
||||
dev->current_state = pmcsr & PCI_PM_CTRL_STATE_MASK;
|
||||
} else {
|
||||
dev->current_state = state;
|
||||
}
|
||||
|
|
@ -1306,26 +1189,114 @@ static int pci_dev_wait(struct pci_dev *dev, char *reset_type, int timeout)
|
|||
/**
|
||||
* pci_power_up - Put the given device into D0
|
||||
* @dev: PCI device to power up
|
||||
*
|
||||
* On success, return 0 or 1, depending on whether or not it is necessary to
|
||||
* restore the device's BARs subsequently (1 is returned in that case).
|
||||
*/
|
||||
int pci_power_up(struct pci_dev *dev)
|
||||
{
|
||||
pci_platform_power_transition(dev, PCI_D0);
|
||||
bool need_restore;
|
||||
pci_power_t state;
|
||||
u16 pmcsr;
|
||||
|
||||
/*
|
||||
* Mandatory power management transition delays are handled in
|
||||
* pci_pm_resume_noirq() and pci_pm_runtime_resume() of the
|
||||
* corresponding bridge.
|
||||
*/
|
||||
if (dev->runtime_d3cold) {
|
||||
/*
|
||||
* When powering on a bridge from D3cold, the whole hierarchy
|
||||
* may be powered on into D0uninitialized state, resume them to
|
||||
* give them a chance to suspend again
|
||||
*/
|
||||
pci_resume_bus(dev->subordinate);
|
||||
platform_pci_set_power_state(dev, PCI_D0);
|
||||
|
||||
if (!dev->pm_cap) {
|
||||
state = platform_pci_get_power_state(dev);
|
||||
if (state == PCI_UNKNOWN)
|
||||
dev->current_state = PCI_D0;
|
||||
else
|
||||
dev->current_state = state;
|
||||
|
||||
if (state == PCI_D0)
|
||||
return 0;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return pci_raw_set_power_state(dev, PCI_D0);
|
||||
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 D0, device inaccessible\n",
|
||||
pci_power_name(dev->current_state));
|
||||
dev->current_state = PCI_D3cold;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
state = pmcsr & PCI_PM_CTRL_STATE_MASK;
|
||||
|
||||
need_restore = (state == PCI_D3hot || dev->current_state >= PCI_D3hot) &&
|
||||
!(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET);
|
||||
|
||||
if (state == PCI_D0)
|
||||
goto end;
|
||||
|
||||
/*
|
||||
* Force the entire word to 0. This doesn't affect PME_Status, disables
|
||||
* PME_En, and sets PowerState to 0.
|
||||
*/
|
||||
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, 0);
|
||||
|
||||
/* Mandatory transition delays; see PCI PM 1.2. */
|
||||
if (state == PCI_D3hot)
|
||||
pci_dev_d3_sleep(dev);
|
||||
else if (state == PCI_D2)
|
||||
udelay(PCI_PM_D2_DELAY);
|
||||
|
||||
end:
|
||||
dev->current_state = PCI_D0;
|
||||
if (need_restore)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_set_full_power_state - Put a PCI device into D0 and update its state
|
||||
* @dev: PCI device to power up
|
||||
*
|
||||
* Call pci_power_up() to put @dev into D0, read from its PCI_PM_CTRL register
|
||||
* to confirm the state change, restore its BARs if they might be lost and
|
||||
* reconfigure ASPM in acordance with the new power state.
|
||||
*
|
||||
* If pci_restore_state() is going to be called right after a power state change
|
||||
* to D0, it is more efficient to use pci_power_up() directly instead of this
|
||||
* function.
|
||||
*/
|
||||
static int pci_set_full_power_state(struct pci_dev *dev)
|
||||
{
|
||||
u16 pmcsr;
|
||||
int ret;
|
||||
|
||||
ret = pci_power_up(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
||||
dev->current_state = pmcsr & PCI_PM_CTRL_STATE_MASK;
|
||||
if (dev->current_state != PCI_D0) {
|
||||
pci_info_ratelimited(dev, "Refused to change power state from %s to D0\n",
|
||||
pci_power_name(dev->current_state));
|
||||
} else if (ret > 0) {
|
||||
/*
|
||||
* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT
|
||||
* INTERFACE SPECIFICATION, REV. 1.2", a device transitioning
|
||||
* from D3hot to D0 _may_ perform an internal reset, thereby
|
||||
* going to "D0 Uninitialized" rather than "D0 Initialized".
|
||||
* For example, at least some versions of the 3c905B and the
|
||||
* 3c556B exhibit this behaviour.
|
||||
*
|
||||
* At least some laptop BIOSen (e.g. the Thinkpad T21) leave
|
||||
* devices in a D3hot state at boot. Consequently, we need to
|
||||
* restore at least the BARs so that the device will be
|
||||
* accessible to its driver.
|
||||
*/
|
||||
pci_restore_bars(dev);
|
||||
}
|
||||
|
||||
if (dev->bus->self)
|
||||
pcie_aspm_pm_state_change(dev->bus->self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1352,6 +1323,79 @@ void pci_bus_set_current_state(struct pci_bus *bus, pci_power_t state)
|
|||
pci_walk_bus(bus, __pci_dev_set_current_state, &state);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_set_low_power_state - Put a PCI device into a low-power state.
|
||||
* @dev: PCI device to handle.
|
||||
* @state: PCI power state (D1, D2, D3hot) to put the device into.
|
||||
*
|
||||
* Use the device's PCI_PM_CTRL register to put it into a low-power state.
|
||||
*
|
||||
* RETURN VALUE:
|
||||
* -EINVAL if the requested state is invalid.
|
||||
* -EIO if device does not support PCI PM or its PM capabilities register has a
|
||||
* wrong version, or device doesn't support the requested state.
|
||||
* 0 if device already is in the requested state.
|
||||
* 0 if device's power state has been successfully changed.
|
||||
*/
|
||||
static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state)
|
||||
{
|
||||
u16 pmcsr;
|
||||
|
||||
if (!dev->pm_cap)
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Validate transition: We can enter D0 from any state, but if
|
||||
* we're already in a low-power state, we can only go deeper. E.g.,
|
||||
* we can go from D1 to D3, but we can't go directly from D3 to D1;
|
||||
* we'd have to go from D3 to D0, then to D1.
|
||||
*/
|
||||
if (dev->current_state <= PCI_D3cold && dev->current_state > state) {
|
||||
pci_dbg(dev, "Invalid power transition (from %s to %s)\n",
|
||||
pci_power_name(dev->current_state),
|
||||
pci_power_name(state));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check if this device supports the desired state */
|
||||
if ((state == PCI_D1 && !dev->d1_support)
|
||||
|| (state == PCI_D2 && !dev->d2_support))
|
||||
return -EIO;
|
||||
|
||||
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",
|
||||
pci_power_name(dev->current_state),
|
||||
pci_power_name(state));
|
||||
dev->current_state = PCI_D3cold;
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pmcsr &= ~PCI_PM_CTRL_STATE_MASK;
|
||||
pmcsr |= state;
|
||||
|
||||
/* Enter specified state */
|
||||
pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
|
||||
|
||||
/* Mandatory power management transition delays; see PCI PM 1.2. */
|
||||
if (state == PCI_D3hot)
|
||||
pci_dev_d3_sleep(dev);
|
||||
else if (state == PCI_D2)
|
||||
udelay(PCI_PM_D2_DELAY);
|
||||
|
||||
pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr);
|
||||
dev->current_state = pmcsr & PCI_PM_CTRL_STATE_MASK;
|
||||
if (dev->current_state != state)
|
||||
pci_info_ratelimited(dev, "Refused to change power state from %s to %s\n",
|
||||
pci_power_name(dev->current_state),
|
||||
pci_power_name(state));
|
||||
|
||||
if (dev->bus->self)
|
||||
pcie_aspm_pm_state_change(dev->bus->self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_set_power_state - Set the power state of a PCI device
|
||||
* @dev: PCI device to handle.
|
||||
|
|
@ -1393,7 +1437,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|||
return 0;
|
||||
|
||||
if (state == PCI_D0)
|
||||
return pci_power_up(dev);
|
||||
return pci_set_full_power_state(dev);
|
||||
|
||||
/*
|
||||
* This device is quirked not to be put into D3, so don't put it in
|
||||
|
|
@ -1402,19 +1446,25 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state)
|
|||
if (state >= PCI_D3hot && (dev->dev_flags & PCI_DEV_FLAGS_NO_D3))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* To put device in D3cold, we put device into D3hot in native
|
||||
* way, then put device into D3cold with platform ops
|
||||
*/
|
||||
error = pci_raw_set_power_state(dev, state > PCI_D3hot ?
|
||||
PCI_D3hot : state);
|
||||
if (state == PCI_D3cold) {
|
||||
/*
|
||||
* To put the device in D3cold, put it into D3hot in the native
|
||||
* way, then put it into D3cold using platform ops.
|
||||
*/
|
||||
error = pci_set_low_power_state(dev, PCI_D3hot);
|
||||
|
||||
if (pci_platform_power_transition(dev, state))
|
||||
return error;
|
||||
if (pci_platform_power_transition(dev, PCI_D3cold))
|
||||
return error;
|
||||
|
||||
/* Powering off a bridge may power off the whole hierarchy */
|
||||
if (state == PCI_D3cold)
|
||||
pci_bus_set_current_state(dev->subordinate, PCI_D3cold);
|
||||
/* Powering off a bridge may power off the whole hierarchy */
|
||||
if (dev->current_state == PCI_D3cold)
|
||||
pci_bus_set_current_state(dev->subordinate, PCI_D3cold);
|
||||
} else {
|
||||
error = pci_set_low_power_state(dev, state);
|
||||
|
||||
if (pci_platform_power_transition(dev, state))
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2718,8 +2768,6 @@ int pci_finish_runtime_suspend(struct pci_dev *dev)
|
|||
if (target_state == PCI_POWER_ERROR)
|
||||
return -EIO;
|
||||
|
||||
dev->runtime_d3cold = target_state == PCI_D3cold;
|
||||
|
||||
/*
|
||||
* There are systems (for example, Intel mobile chips since Coffee
|
||||
* Lake) where the power drawn while suspended can be significantly
|
||||
|
|
@ -2737,7 +2785,6 @@ int pci_finish_runtime_suspend(struct pci_dev *dev)
|
|||
if (error) {
|
||||
pci_enable_wake(dev, target_state, false);
|
||||
pci_restore_ptm_state(dev);
|
||||
dev->runtime_d3cold = false;
|
||||
}
|
||||
|
||||
return error;
|
||||
|
|
@ -5113,19 +5160,19 @@ static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
|
|||
|
||||
void pci_dev_lock(struct pci_dev *dev)
|
||||
{
|
||||
pci_cfg_access_lock(dev);
|
||||
/* block PM suspend, driver probe, etc. */
|
||||
device_lock(&dev->dev);
|
||||
pci_cfg_access_lock(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_dev_lock);
|
||||
|
||||
/* Return 1 on successful lock, 0 on contention */
|
||||
int pci_dev_trylock(struct pci_dev *dev)
|
||||
{
|
||||
if (pci_cfg_access_trylock(dev)) {
|
||||
if (device_trylock(&dev->dev))
|
||||
if (device_trylock(&dev->dev)) {
|
||||
if (pci_cfg_access_trylock(dev))
|
||||
return 1;
|
||||
pci_cfg_access_unlock(dev);
|
||||
device_unlock(&dev->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -5134,8 +5181,8 @@ EXPORT_SYMBOL_GPL(pci_dev_trylock);
|
|||
|
||||
void pci_dev_unlock(struct pci_dev *dev)
|
||||
{
|
||||
device_unlock(&dev->dev);
|
||||
pci_cfg_access_unlock(dev);
|
||||
device_unlock(&dev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_dev_unlock);
|
||||
|
||||
|
|
|
|||
|
|
@ -627,6 +627,9 @@ struct device_node;
|
|||
int of_pci_parse_bus_range(struct device_node *node, struct resource *res);
|
||||
int of_get_pci_domain_nr(struct device_node *node);
|
||||
int of_pci_get_max_link_speed(struct device_node *node);
|
||||
u32 of_pci_get_slot_power_limit(struct device_node *node,
|
||||
u8 *slot_power_limit_value,
|
||||
u8 *slot_power_limit_scale);
|
||||
void pci_set_of_node(struct pci_dev *dev);
|
||||
void pci_release_of_node(struct pci_dev *dev);
|
||||
void pci_set_bus_of_node(struct pci_bus *bus);
|
||||
|
|
@ -653,6 +656,18 @@ of_pci_get_max_link_speed(struct device_node *node)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline u32
|
||||
of_pci_get_slot_power_limit(struct device_node *node,
|
||||
u8 *slot_power_limit_value,
|
||||
u8 *slot_power_limit_scale)
|
||||
{
|
||||
if (slot_power_limit_value)
|
||||
*slot_power_limit_value = 0;
|
||||
if (slot_power_limit_scale)
|
||||
*slot_power_limit_scale = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void pci_set_of_node(struct pci_dev *dev) { }
|
||||
static inline void pci_release_of_node(struct pci_dev *dev) { }
|
||||
static inline void pci_set_bus_of_node(struct pci_bus *bus) { }
|
||||
|
|
|
|||
|
|
@ -101,6 +101,11 @@ struct aer_stats {
|
|||
#define ERR_COR_ID(d) (d & 0xffff)
|
||||
#define ERR_UNCOR_ID(d) (d >> 16)
|
||||
|
||||
#define AER_ERR_STATUS_MASK (PCI_ERR_ROOT_UNCOR_RCV | \
|
||||
PCI_ERR_ROOT_COR_RCV | \
|
||||
PCI_ERR_ROOT_MULTI_COR_RCV | \
|
||||
PCI_ERR_ROOT_MULTI_UNCOR_RCV)
|
||||
|
||||
static int pcie_aer_disable;
|
||||
static pci_ers_result_t aer_root_reset(struct pci_dev *dev);
|
||||
|
||||
|
|
@ -1196,7 +1201,7 @@ static irqreturn_t aer_irq(int irq, void *context)
|
|||
struct aer_err_source e_src = {};
|
||||
|
||||
pci_read_config_dword(rp, aer + PCI_ERR_ROOT_STATUS, &e_src.status);
|
||||
if (!(e_src.status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV)))
|
||||
if (!(e_src.status & AER_ERR_STATUS_MASK))
|
||||
return IRQ_NONE;
|
||||
|
||||
pci_read_config_dword(rp, aer + PCI_ERR_ROOT_ERR_SRC, &e_src.id);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
* file, where their drivers can use them.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
|
|
@ -5895,3 +5896,49 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1533, rom_bar_overlap_defect);
|
|||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1536, rom_bar_overlap_defect);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1537, rom_bar_overlap_defect);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1538, rom_bar_overlap_defect);
|
||||
|
||||
#ifdef CONFIG_PCIEASPM
|
||||
/*
|
||||
* Several Intel DG2 graphics devices advertise that they can only tolerate
|
||||
* 1us latency when transitioning from L1 to L0, which may prevent ASPM L1
|
||||
* from being enabled. But in fact these devices can tolerate unlimited
|
||||
* latency. Override their Device Capabilities value to allow ASPM L1 to
|
||||
* be enabled.
|
||||
*/
|
||||
static void aspm_l1_acceptable_latency(struct pci_dev *dev)
|
||||
{
|
||||
u32 l1_lat = FIELD_GET(PCI_EXP_DEVCAP_L1, dev->devcap);
|
||||
|
||||
if (l1_lat < 7) {
|
||||
dev->devcap |= FIELD_PREP(PCI_EXP_DEVCAP_L1, 7);
|
||||
pci_info(dev, "ASPM: overriding L1 acceptable latency from %#x to 0x7\n",
|
||||
l1_lat);
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f80, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f81, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f82, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f83, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f84, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f85, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f86, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f87, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4f88, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5690, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5691, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5692, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5693, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5694, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5695, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56a0, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56a1, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56a2, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56a3, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56a4, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56a5, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56a6, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56b0, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56b1, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56c0, aspm_l1_acceptable_latency);
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x56c1, aspm_l1_acceptable_latency);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -379,10 +379,6 @@ struct pci_dev {
|
|||
unsigned int mmio_always_on:1; /* Disallow turning off io/mem
|
||||
decoding during BAR sizing */
|
||||
unsigned int wakeup_prepared:1;
|
||||
unsigned int runtime_d3cold:1; /* Whether go through runtime
|
||||
D3cold, not set for devices
|
||||
powered on/off by the
|
||||
corresponding bridge */
|
||||
unsigned int skip_bus_pm:1; /* Internal: Skip bus-level PM */
|
||||
unsigned int ignore_hotplug:1; /* Ignore hotplug events */
|
||||
unsigned int hotplug_user_indicators:1; /* SlotCtl indicators
|
||||
|
|
|
|||
|
|
@ -616,6 +616,7 @@
|
|||
#define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */
|
||||
#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */
|
||||
#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */
|
||||
#define PCI_EXP_SLTCTL_ASPL_DISABLE 0x2000 /* Auto Slot Power Limit Disable */
|
||||
#define PCI_EXP_SLTCTL_IBPD_DISABLE 0x4000 /* In-band PD disable */
|
||||
#define PCI_EXP_SLTSTA 0x1a /* Slot Status */
|
||||
#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue