mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
pci-v7.4-changes
-----BEGIN PGP SIGNATURE-----
iQJIBAABCgAyFiEEgMe7l+5h9hnxdsnuWYigwDrT+vwFAmrZFXsUHGJoZWxnYWFz
QGdvb2dsZS5jb20ACgkQWYigwDrT+vxlSxAAwXOwRpzBXQjGzvpA7usELOuBqUn6
kToTlY5VHQZ01Iax3IwbENP8DP3Nh4Zmq3GVOwLREdZ6ouAw/jXGfdnAnFgl6EV2
5m6yW0RXElZgxIbPJZIbGve7UwrpgBIvvsPl6KVZRY/6lzobEU8XLIHWFKi1EF9l
nicYADjI1gocFaq8GyAqAoh5WpHSpP2rNBY2fQmZSm+ihGBUzvy6inL0f3j19A1G
L6av4MwpAQQI137oN1WG2tR3Donc1r7bIfsGiYkMg6TjV/yeqNeafCJ3jbZnTUAS
UHVfnCxImkKmQXYElVXsn93VwigRMbtH3KXaAVjctp6VoUxNCTJRtjvvwrpu1tlp
92eazDj9BU2SgcHxH5D4TYmWgslp43l8EbnbBgt64lLthsdhv2evOkQbGbMtZwzL
FnSYG7Y/wfPxM2u3ODFfjXoKBdmbJqKzwmjs1CQFJVrp0TMLUHUpTI2XDxCOYh+J
HpNSnAevSjUUFaXf+IPvgOnZZanN6K2/ZUns48GIYQruebViTv7RCfPFZKjLI1Ny
1w5VwUZ9KEEidlB3vAmeAbACWoRGumoeS9AElzuBLRcOWKTiofmupTwO7GoTwqGa
69SpDgU9t99UK2Q9++NLO+rW1ZwCtdi0ImmJ0t9rZtGPMDR11LZwdIRmMxEjdj3r
fg+9TQDdxiMF+r0=
=1Pqr
-----END PGP SIGNATURE-----
Merge tag 'pci-v7.4-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci
Pull PCI updates from Bjorn Helgaas:
"Enumeration:
- Set supported_speeds in set_pcie_port_type() so sparc64 and
powerpc, which call it via of_create_pci_dev() instead of the usual
pci_setup_device(), correctly set the supported PCIe link speeds
(Stian Halseth)
- When pcie_set_mps() changes MPS, update the value in the saved
state so a subsequent restore doesn't restore an invalid value
(Niklas Cassel)
- Skip Target Speed quirk on clamped ports with no link (Maciej W.
Rozycki)
Driver binding:
- Assume control of portdrv-related features only when portdrv is
enabled, since we can't use them without portdrv (Kuppuswamy
Sathyanarayanan)
- Centralize feature ownership checking so code only needs to check
'host->native_X' and doesn't need to also check 'pcie_ports_native'
(Kuppuswamy Sathyanarayanan)
Error handling:
- Fix leaks when ACPI APEI/GHES error events overflow the kfifo or
mention a device that no longer exists (Priyank Rathod)
- Fix aer_process_err_devices() leak when a device is recorded as an
error source purely by being mentioned in the Root Port Error
Source and the device is inaccessible or logged only masked errors
(Priyank Rathod)
ASPM:
- Disable ASPM when drivers request that, even if the OS doesn't own
ASPM; otherwise drivers do it on their own, which is worse (Ilpo
Järvinen)
Power management:
- Align hibernate poweroff flow with suspend flow for bridges so they
can be powered down at hibernate as they are for suspend (Mario
Limonciello)
- Restore BARs after powering up during enumeration to fix a
regression on devices that don't preserve state in D3hot (Keng-Yu
Lin)
- Avoid D3 on PLX PEX 8532/8533/8548 downstream ports, where putting
them in D3hot takes down the whole switch (Imre Kaloz)
Power control:
- Add GPIO driver for Toshiba TC9563 embedded GPIO controller (Alex
Elder)
- Add DT documentation and pwrctrl driver support for TC9563 embedded
GPIO controller (Lorenzo Bianconi)
- Switch the tc9563 per-port reset to use the GPIO descriptor API
instead of direct regmap accesses (Lorenzo Bianconi)
- Only destroy devices created by pwrctrl core, so that when removing
a PCIe controller driver that uses pwrctrl, we don't remove devices
populated by another mechanism (Shawn Lin)
- Power off child devices on power-on failure (Lorenzo Bianconi)
PCIe native device hotplug:
- Make the 'pciehp_poll_mode' parameter read-only to prevent changes
via sysfs, which can cause errors when unbinding (Runyu Xiao)
Virtualization:
- Read SR-IOV VF Revision ID and Subsystem ID from VF instead of
assuming they're the same as VF 0; they may differ (Logan Odell)
- Add ACS quirk for Pericom PI7C9X2G304 switches (Hans de Goede)
Peer-to-peer DMA:
- Add Zhaoxin KX-6000, KH-40000, KX-6000G, KX-7000, and KH-50000 host
bridges to allowlist (LeoLiu-oc)
- Add Alibaba T-HEAD Yitian 710 host bridge to allowlist (Feng Tang)
- Add Intel Haswell client host bridge to allowlist (Scott Lee)
- Intel Xeon E3/E5/E7 0x6f00 host bridge to allowlist (kai zhang)
- Fix handling of errors in pci_p2pdma_add_resource() to avoid (a)
unmapping a BAR if registering another fails and (b) use after free
of p2p_pgmap (Leon Romanovsky)
- Wait for RCU readers before freeing state to avoid use-after-free
of pdev->p2pdma (Leon Romanovsky)
- Restrict pci_p2pmem_find_many() to providers backed by a pool (Leon
Romanovsky)
- Safely terminate ACS redirect lists to avoid out-of-bounds access
to the buffer (Leon Romanovsky)
- Silence 'Host bridge not in P2PDMA whitelist' warnings unless
caller, e.g., pci_p2pmem_find_many(), requests 'verbose' (Leon
Romanovsky)
Sysfs:
- Return -ENODEV or -EIO sysfs 'label' or 'acpi_index' attributes
when Device Name _DSM fails, not -1, which is interpreted as -EPERM
(Krzysztof Wilczyński)
- Return valid sysfs 'acpi_index' attribute even if the Device Name
_DSM returned an invalid 'label' (Krzysztof Wilczyński)
- Expose sysfs 'acpi_index' and 'label' based on whether the Device
Name _DSM returns valid data, not merely whether the _DSM is
implemented (Krzysztof Wilczyński)
- Avoid out-of-bounds reads of the label returned by the Device Name
_DSM, which is in UTF-16 characters, not bytes (Krzysztof
Wilczyński)
- Reject unaligned resource I/O port accesses, which can oops on
arm64 (Deepanshu Kartikey)
Endpoint framework:
- Pass the correct physical and virtual endpoint function numbers
when setting translation of inbound memory windows (Koichiro Den)
- Make embedded doorbell IRQ non-shared, since currently only the
first EPF can allocate doorbells (Koichiro Den)
- MSI doorbells currently target the first EPF attached to an EPC;
let other EPFs try the embedded doorbell instead of failing when
they can't use the MSI doorbell (Koichiro Den)
- Serialize scan of vNTB virtual PCI bus with other PCI topology
changes (Koichiro Den)
- Manage vNTB lifetimes to avoid leaking virtual devices and buses
and allow removal (Koichiro Den)
- Track vNTB link state from both sides so both see the same
effective link state (Koichiro Den)
- Configure vNTB MSI-X when supported to avoid IOMMU faults and
failure to deliver doorbell interrupts on RC (Koichiro Den)
APM X-Gene PCIe controller driver:
- Use devm for clocks so they're automatically disabled, unprepared,
and released if errors happen during probe (Yuho Choi)
ASPEED PCIe controller driver:
- Fix clk and PHY leaks in aspeed_pcie_port_init() error paths (Liu
Zhenlong)
Broadcom iProc PCIe controller driver:
- Expose devm_pci_alloc_host_bridge_release() so drivers can share a
devm release function even if they don't need the device tree
parsing done by devm_pci_alloc_host_bridge() (Bjorn Helgaas)
- Request only the memory window from BCMA Enumeration ROM and warn
if it doesn't match the one in DT, which fixes an iproc-bcma probe
failure (Semih Baskan)
Broadcom STB PCIe controller driver:
- Replace bool properties for each SoC configuration with a u32
bitvector and a BFLAG() helper (Jim Quinlan)
- Use a similar BQUIRK() helper to test various SoC quirks (Jim
Quinlan)
Cadence PCIe controller driver:
- Remove device links to PHY, which are redundant because the PHY
core itself adds them (Vladimir Oltean)
- Return error (not success) from cdns_plat_pcie_probe() if
pm_runtime_get_sync() or cdns_pcie_ep_setup() failed (Vladimir
Oltean)
- Correct the driver data pointer usage; previously .probe() stored
'struct cdns_plat_pcie *', but .shutdown() used the pointer as
'struct cdns_pcie *', resulting in accessing garbage (Linmao Li)
ESWIN PCIe controller driver:
- Publish the platform driver MODULE_DEVICE_TABLE so the driver can
be autoloaded when built as a module (Pengpeng Hou)
Freescale i.MX6 PCIe controller driver:
- Fix resource and pwrctrl device leaks .probe() (Zhijian Han)
- Improve i.MX95 Gen3 link stability by changing MPLLB_BANDWIDTH from
default 120 to 140 (Richard Zhu)
- Use dev_err_probe() in host init error path to silence
-EPROBE_DEFER messages (Alexander Stein)
- Avoid dereferencing NULL clock names when 'clocks' includes more
phandles than 'clock-names' has names (Rudi Heitbaum)
- Add suspend_poweroff flag so platforms like i.MX that power off RC
controllers during suspend can enter L2 so endpoints can get
PME_Turn_Off notifications before power loss (Richard Zhu)
Freescale Layerscape PCIe controller driver:
- Enable PROBE_PREFER_ASYNCHRONOUS to reduce probe time of the four
controllers on LX2160A (Vincent Jardin)
Intel VMD host bridge driver:
- Flush DMA writes before handling MSI on Meteor Lake: MSI may arrive
before write has completed, resulting in an nvme 30 second stall
before a QID timeout and possibly other similar problems (Rickey
Bartlett)
- Flush DMA writes before handling MSI on Arrow Lake (Vladimir
Nedoshivin)
Marvell Aardvark PCIe controller driver:
- Disable PHY on probe failures (Myeonghun Pak)
Marvell MVEBU PCIe controller driver:
- Refcount child OF nodes during .probe() to make a concurrent OF
overlay removal safe (Krzysztof Kozlowski)
MediaTek PCIe controller driver:
- Find INTx DT property by 'interrupt-controller' name instead of
assuming it's the first child node (Zhengping Zhang)
MediaTek PCIe Gen3 controller driver:
- Fix a 64-bit type truncation in mtk_pcie_set_trans_table() to avoid
issues with windows above or larger than 4GB (Sreeraj S Kurup)
NVIDIA Tegra264 PCIe controller driver:
- Fix the Link Capabilities offset so we read the correct register
(Bjorn Helgaas)
Qualcomm PCIe controller driver:
- Only collect reset GPIOs from PCI bridge DT nodes to avoid stealing
resets used by downstream devices like USB controllers (Christopher
Obbard)
- Block accesses to downstream devices on link down to reduce a storm
of Completion Timeout AER interrupts (Qiang Yu)
- Skip system suspend/resume for firmware-managed PCIe, where
firmware manages clocks, regulators, resets, etc (Mrinmay Sarkar)
- Honor IOMMU provider's #iommu-cells in qcom_pcie_config_sid_1_9_0()
(Manivannan Sadhasivam)
- Deinitialize endpoint on remove (Guangshuo Li)
- Treat Root Port error recovery as successful even if link doesn't
come up after reset because the device was hot-unplugged (Konrad
Dybcio)
- Skip unnecessary iMSI-RX initialization if DT 'msi-map' is present,
indicating an external MSI controller (Ayiluri Naga Rashmi)
Renesas R-Car PCIe controller driver:
- Limit Max_Read_Request_Size to 256 to avoid hardware erratum (Marek
Vasut)
- Support PERST# via reset subsystem as well as the existing GPIO
support to prepare for R-Car Gen5 SoCs (Marek Vasut)
- Add DT and driver support for the R-Car Gen 5 X5H (R8A78000) PCIe
Gen 4 core (Marek Vasut)
- Add Application/Local register reset DT description and driver
support for both host and endpoint mode (Marek Vasut)
- Add PM ops so controller works after suspend/resume (Marek Vasut)
Renesas RZ/G3S PCIe controller driver:
- Disable refclk on probe failure (Lad Prabhakar)
- Propagate platform_get_irq_byname() errors so probe deferral works
correctly (Lad Prabhakar)
- Tear down INTx handlers if subsequent initialization fails (Lad
Prabhakar)
- Add RZ/G3L (R9A08G046) DT and driver support (Biju Das)
Rockchip PCIe controller driver:
- Limit host and endpoint controllers to 2.5 GT/s since 5.0 GT/s is
unsafe per Rockchip (Geraldo Nascimento)
Synopsys DesignWare PCIe controller driver:
- Set DMA_BYPASS bit in outbound IATU CTRL2 register to avoid data
corruption if the iATU translates DMA engine transactions (Sumit
Kumar)
- Use FIELD_PREP() to remove need for *_SHIFT definitions (Hans
Zhang)
TI DRA7xx PCIe controller driver:
- Remove device links to PHY, which are redundant because the PHY
core itself adds them (Vladimir Oltean)
- Fix optional clock leak when .probe() fails by using
devm_clk_get_optional_enabled() (Felix Gu)
TI Keystone PCIe controller driver:
- Remove device links to PHY, which are redundant because the PHY
core itself adds them (Vladimir Oltean)
UltraRISC PCIe controller driver:
- Allow DWC drivers to skip outbound iATU setup if they don't need
translation between CPU and PCI addresses (Xincheng Zhang)
- Skip MEM outbound iATU setup on DP1000 (Xincheng Zhang)
Xilinx Versal CPM PCIe controller driver:
- Add DT and driver support for optional PERST# for use in warm reset
scenarios (Sai Krishna Musham)
Xilinx XDMA PCIe controller driver:
- Fix device node reference leak in xilinx_pl_dma_pcie_init_irq_domain()
(Wentao Liang)"
* tag 'pci-v7.4-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci: (139 commits)
PCI/pwrctrl: Power off child devices on power-on failure
PCI/pwrctrl: Only destroy devices created by pwrctrl core
PCI/pwrctrl: generic: Fix leaking array from of_regulator_bulk_get_all()
PCI/pwrctrl: tc9563: Switch per-port reset to GPIO descriptor API
PCI/pwrctrl: tc9563: Add GPIO auxiliary device support
dt-bindings: PCI: toshiba,tc9563: Document embedded GPIO controller
gpio: tc9563: Add support for the embedded GPIO controller
PCI: qcom: Skip DWC MSI host initialization when DT 'msi-map' is present
PCI: qcom: Ignore -ENODEV after Root Port reset
PCI: qcom-ep: Deinitialize endpoint on remove
PCI/sysfs: Reject unaligned resource I/O port accesses
PCI: rcar-gen4: Add missing PM ops
PCI: endpoint: pci-epf-vntb: Honor MSI-X selection
PCI: endpoint: pci-epf-vntb: Use a local epf pointer in interrupt setup
PCI: dwc: imx6: Use regmap_assign_bits() for conditional set/clear
PCI: dwc: Describe DWC PCIe fields with full masks and FIELD_PREP()
PCI: dwc: Rename pp->irq_domain to pp->msi_irq_domain
PCI: dwc: Rename dw_pcie_allocate_domains() to dw_pcie_allocate_msi_domains()
PCI: dwc: Correct PCIE_DW_PLAT_EP reference in Kconfig help
PCI/ASPM: Disable ASPM when driver requests it
...
This commit is contained in:
commit
17bb0e2f6c
|
|
@ -244,10 +244,16 @@ Contact: Narendra K <narendra_k@dell.com>, linux-bugs@dell.com
|
|||
Description:
|
||||
Reading this attribute will provide the firmware
|
||||
given name (SMBIOS type 41 string or ACPI _DSM string) of
|
||||
the PCI device. The attribute will be created only
|
||||
if the firmware has given a name to the PCI device.
|
||||
ACPI _DSM string name will be given priority if the
|
||||
system firmware provides SMBIOS type 41 string also.
|
||||
the PCI device. The attribute will be created only if the
|
||||
firmware naming mechanism is implemented for the device:
|
||||
a SMBIOS type 41 record with a non-empty reference
|
||||
designation, or a Device Name _DSM that returns the name
|
||||
as a string or a buffer. The value read is empty when the
|
||||
firmware implements the _DSM but gives the device no name,
|
||||
which per PCI Firmware r3.3, sec 4.6.7 the firmware reports
|
||||
as a NULL string. ACPI _DSM string name will be given
|
||||
priority if the system firmware provides SMBIOS type 41
|
||||
string also.
|
||||
Users:
|
||||
Userspace applications interested in knowing the
|
||||
firmware assigned name of the PCI device.
|
||||
|
|
|
|||
|
|
@ -8,4 +8,3 @@ PCI Native Host Bridge and Endpoint Drivers
|
|||
:maxdepth: 2
|
||||
|
||||
pci-controller-drivers
|
||||
rcar-pcie-firmware
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=================================================
|
||||
Firmware of PCIe controller for Renesas R-Car V4H
|
||||
=================================================
|
||||
|
||||
Renesas R-Car V4H (r8a779g0) has a PCIe controller, requiring a specific
|
||||
firmware download during startup.
|
||||
|
||||
However, Renesas currently cannot distribute the firmware free of charge.
|
||||
|
||||
The firmware file "104_PCIe_fw_addr_data_ver1.05.txt" (note that the file name
|
||||
might be different between different datasheet revisions) can be found in the
|
||||
datasheet encoded as text, and as such, the file's content must be converted
|
||||
back to binary form. This can be achieved using the following example script:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ awk '/^\s*0x[0-9A-Fa-f]{4}\s+0x[0-9A-Fa-f]{4}/ { print substr($2,5,2) substr($2,3,2) }' \
|
||||
104_PCIe_fw_addr_data_ver1.05.txt | \
|
||||
xxd -p -r > rcar_gen4_pcie.bin
|
||||
|
||||
Once the text content has been converted into a binary firmware file, verify
|
||||
its checksum as follows:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ sha1sum rcar_gen4_pcie.bin
|
||||
1d0bd4b189b4eb009f5d564b1f93a79112994945 rcar_gen4_pcie.bin
|
||||
|
||||
The resulting binary file called "rcar_gen4_pcie.bin" should be placed in the
|
||||
"/lib/firmware" directory before the driver runs.
|
||||
|
|
@ -1,670 +0,0 @@
|
|||
NVIDIA Tegra PCIe controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be:
|
||||
- "nvidia,tegra20-pcie": for Tegra20
|
||||
- "nvidia,tegra30-pcie": for Tegra30
|
||||
- "nvidia,tegra124-pcie": for Tegra124 and Tegra132
|
||||
- "nvidia,tegra210-pcie": for Tegra210
|
||||
- "nvidia,tegra186-pcie": for Tegra186
|
||||
- power-domains: To ungate power partition by BPMP powergate driver. Must
|
||||
contain BPMP phandle and PCIe power partition ID. This is required only
|
||||
for Tegra186.
|
||||
- device_type: Must be "pci"
|
||||
- reg: A list of physical base address and length for each set of controller
|
||||
registers. Must contain an entry for each entry in the reg-names property.
|
||||
- reg-names: Must include the following entries:
|
||||
"pads": PADS registers
|
||||
"afi": AFI registers
|
||||
"cs": configuration space region
|
||||
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
||||
entry for each entry in the interrupt-names property.
|
||||
- interrupt-names: Must include the following entries:
|
||||
"intr": The Tegra interrupt that is asserted for controller interrupts
|
||||
"msi": The Tegra interrupt that is asserted when an MSI is received
|
||||
- bus-range: Range of bus numbers associated with this controller
|
||||
- #address-cells: Address representation for root ports (must be 3)
|
||||
- cell 0 specifies the bus and device numbers of the root port:
|
||||
[23:16]: bus number
|
||||
[15:11]: device number
|
||||
- cell 1 denotes the upper 32 address bits and should be 0
|
||||
- cell 2 contains the lower 32 address bits and is used to translate to the
|
||||
CPU address space
|
||||
- #size-cells: Size representation for root ports (must be 2)
|
||||
- ranges: Describes the translation of addresses for root ports and standard
|
||||
PCI regions. The entries must be 6 cells each, where the first three cells
|
||||
correspond to the address as described for the #address-cells property
|
||||
above, the fourth cell is the physical CPU address to translate to and the
|
||||
fifth and six cells are as described for the #size-cells property above.
|
||||
- The first two entries are expected to translate the addresses for the root
|
||||
port registers, which are referenced by the assigned-addresses property of
|
||||
the root port nodes (see below).
|
||||
- The remaining entries setup the mapping for the standard I/O, memory and
|
||||
prefetchable PCI regions. The first cell determines the type of region
|
||||
that is setup:
|
||||
- 0x81000000: I/O memory region
|
||||
- 0x82000000: non-prefetchable memory region
|
||||
- 0xc2000000: prefetchable memory region
|
||||
Please refer to the standard PCI bus binding document for a more detailed
|
||||
explanation.
|
||||
- #interrupt-cells: Size representation for interrupts (must be 1)
|
||||
- interrupt-map-mask and interrupt-map: Standard PCI IRQ mapping properties
|
||||
Please refer to the standard PCI bus binding document for a more detailed
|
||||
explanation.
|
||||
- clocks: Must contain an entry for each entry in clock-names.
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
- clock-names: Must include the following entries:
|
||||
- pex
|
||||
- afi
|
||||
- pll_e
|
||||
- cml (not required for Tegra20)
|
||||
- resets: Must contain an entry for each entry in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names: Must include the following entries:
|
||||
- pex
|
||||
- afi
|
||||
- pcie_x
|
||||
|
||||
Optional properties:
|
||||
- pinctrl-names: A list of pinctrl state names. Must contain the following
|
||||
entries:
|
||||
- "default": active state, puts PCIe I/O out of deep power down state
|
||||
- "idle": puts PCIe I/O into deep power down state
|
||||
- pinctrl-0: phandle for the default/active state of pin configurations.
|
||||
- pinctrl-1: phandle for the idle state of pin configurations.
|
||||
|
||||
Required properties on Tegra124 and later (deprecated):
|
||||
- phys: Must contain an entry for each entry in phy-names.
|
||||
- phy-names: Must include the following entries:
|
||||
- pcie
|
||||
|
||||
These properties are deprecated in favour of per-lane PHYs define in each of
|
||||
the root ports (see below).
|
||||
|
||||
Power supplies for Tegra20:
|
||||
- avdd-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
- vdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
- avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
|
||||
supply 1.05 V.
|
||||
- avdd-plle-supply: Power supply for PLLE, which is shared with SATA. Must
|
||||
supply 1.05 V.
|
||||
- vddio-pex-clk-supply: Power supply for PCIe clock. Must supply 3.3 V.
|
||||
|
||||
Power supplies for Tegra30:
|
||||
- Required:
|
||||
- avdd-pex-pll-supply: Power supply for dedicated (internal) PCIe PLL. Must
|
||||
supply 1.05 V.
|
||||
- avdd-plle-supply: Power supply for PLLE, which is shared with SATA. Must
|
||||
supply 1.05 V.
|
||||
- vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
|
||||
supply 1.8 V.
|
||||
- hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
|
||||
Must supply 3.3 V.
|
||||
- Optional:
|
||||
- If lanes 0 to 3 are used:
|
||||
- avdd-pexa-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
- vdd-pexa-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
- If lanes 4 or 5 are used:
|
||||
- avdd-pexb-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
- vdd-pexb-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
|
||||
Power supplies for Tegra124:
|
||||
- Required:
|
||||
- avddio-pex-supply: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
- dvddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
- hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
|
||||
Must supply 3.3 V.
|
||||
- vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
|
||||
supply 2.8-3.3 V.
|
||||
|
||||
Power supplies for Tegra210:
|
||||
- Required:
|
||||
- hvddio-pex-supply: High-voltage supply for PCIe I/O and PCIe output
|
||||
clocks. Must supply 1.8 V.
|
||||
- dvddio-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
- vddio-pex-ctl-supply: Power supply for PCIe control I/O partition. Must
|
||||
supply 1.8 V.
|
||||
|
||||
Power supplies for Tegra186:
|
||||
- Required:
|
||||
- dvdd-pex-supply: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
- hvdd-pex-pll-supply: High-voltage supply for PLLE (shared with USB3). Must
|
||||
supply 1.8 V.
|
||||
- hvdd-pex-supply: High-voltage supply for PCIe I/O and PCIe output clocks.
|
||||
Must supply 1.8 V.
|
||||
- vddio-pexctl-aud-supply: Power supply for PCIe side band signals. Must
|
||||
supply 1.8 V.
|
||||
|
||||
Root ports are defined as subnodes of the PCIe controller node.
|
||||
|
||||
Required properties:
|
||||
- device_type: Must be "pci"
|
||||
- assigned-addresses: Address and size of the port configuration registers
|
||||
- reg: PCI bus address of the root port
|
||||
- #address-cells: Must be 3
|
||||
- #size-cells: Must be 2
|
||||
- ranges: Sub-ranges distributed from the PCIe controller node. An empty
|
||||
property is sufficient.
|
||||
- nvidia,num-lanes: Number of lanes to use for this port. Valid combinations
|
||||
are:
|
||||
- Root port 0 uses 4 lanes, root port 1 is unused.
|
||||
- Both root ports use 2 lanes.
|
||||
|
||||
Required properties for Tegra124 and later:
|
||||
- phys: Must contain an phandle to a PHY for each entry in phy-names.
|
||||
- phy-names: Must include an entry for each active lane. Note that the number
|
||||
of entries does not have to (though usually will) be equal to the specified
|
||||
number of lanes in the nvidia,num-lanes property. Entries are of the form
|
||||
"pcie-N": where N ranges from 0 to the value specified in nvidia,num-lanes.
|
||||
|
||||
Examples:
|
||||
=========
|
||||
|
||||
Tegra20:
|
||||
--------
|
||||
|
||||
SoC DTSI:
|
||||
|
||||
pcie-controller@80003000 {
|
||||
compatible = "nvidia,tegra20-pcie";
|
||||
device_type = "pci";
|
||||
reg = <0x80003000 0x00000800 /* PADS registers */
|
||||
0x80003800 0x00000200 /* AFI registers */
|
||||
0x90000000 0x10000000>; /* configuration space */
|
||||
reg-names = "pads", "afi", "cs";
|
||||
interrupts = <0 98 0x04 /* controller interrupt */
|
||||
0 99 0x04>; /* MSI interrupt */
|
||||
interrupt-names = "intr", "msi";
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
bus-range = <0x00 0xff>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges = <0x82000000 0 0x80000000 0x80000000 0 0x00001000 /* port 0 registers */
|
||||
0x82000000 0 0x80001000 0x80001000 0 0x00001000 /* port 1 registers */
|
||||
0x81000000 0 0 0x82000000 0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0 0xa0000000 0xa0000000 0 0x10000000 /* non-prefetchable memory */
|
||||
0xc2000000 0 0xb0000000 0xb0000000 0 0x10000000>; /* prefetchable memory */
|
||||
|
||||
clocks = <&tegra_car 70>, <&tegra_car 72>, <&tegra_car 118>;
|
||||
clock-names = "pex", "afi", "pll_e";
|
||||
resets = <&tegra_car 70>, <&tegra_car 72>, <&tegra_car 74>;
|
||||
reset-names = "pex", "afi", "pcie_x";
|
||||
status = "disabled";
|
||||
|
||||
pci@1,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82000800 0 0x80000000 0 0x1000>;
|
||||
reg = <0x000800 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <2>;
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001000 0 0x80001000 0 0x1000>;
|
||||
reg = <0x001000 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
Board DTS:
|
||||
|
||||
pcie-controller@80003000 {
|
||||
status = "okay";
|
||||
|
||||
vdd-supply = <&pci_vdd_reg>;
|
||||
pex-clk-supply = <&pci_clk_reg>;
|
||||
|
||||
/* root port 00:01.0 */
|
||||
pci@1,0 {
|
||||
status = "okay";
|
||||
|
||||
/* bridge 01:00.0 (optional) */
|
||||
pci@0,0 {
|
||||
reg = <0x010000 0 0 0 0>;
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
device_type = "pci";
|
||||
|
||||
/* endpoint 02:00.0 */
|
||||
pci@0,0 {
|
||||
reg = <0x020000 0 0 0 0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Note that devices on the PCI bus are dynamically discovered using PCI's bus
|
||||
enumeration and therefore don't need corresponding device nodes in DT. However
|
||||
if a device on the PCI bus provides a non-probeable bus such as I2C or SPI,
|
||||
device nodes need to be added in order to allow the bus' children to be
|
||||
instantiated at the proper location in the operating system's device tree (as
|
||||
illustrated by the optional nodes in the example above).
|
||||
|
||||
Tegra30:
|
||||
--------
|
||||
|
||||
SoC DTSI:
|
||||
|
||||
pcie-controller@3000 {
|
||||
compatible = "nvidia,tegra30-pcie";
|
||||
device_type = "pci";
|
||||
reg = <0x00003000 0x00000800 /* PADS registers */
|
||||
0x00003800 0x00000200 /* AFI registers */
|
||||
0x10000000 0x10000000>; /* configuration space */
|
||||
reg-names = "pads", "afi", "cs";
|
||||
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH /* controller interrupt */
|
||||
GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
|
||||
interrupt-names = "intr", "msi";
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
bus-range = <0x00 0xff>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges = <0x82000000 0 0x00000000 0x00000000 0 0x00001000 /* port 0 configuration space */
|
||||
0x82000000 0 0x00001000 0x00001000 0 0x00001000 /* port 1 configuration space */
|
||||
0x82000000 0 0x00004000 0x00004000 0 0x00001000 /* port 2 configuration space */
|
||||
0x81000000 0 0 0x02000000 0 0x00010000 /* downstream I/O */
|
||||
0x82000000 0 0x20000000 0x20000000 0 0x08000000 /* non-prefetchable memory */
|
||||
0xc2000000 0 0x28000000 0x28000000 0 0x18000000>; /* prefetchable memory */
|
||||
|
||||
clocks = <&tegra_car TEGRA30_CLK_PCIE>,
|
||||
<&tegra_car TEGRA30_CLK_AFI>,
|
||||
<&tegra_car TEGRA30_CLK_PLL_E>,
|
||||
<&tegra_car TEGRA30_CLK_CML0>;
|
||||
clock-names = "pex", "afi", "pll_e", "cml";
|
||||
resets = <&tegra_car 70>,
|
||||
<&tegra_car 72>,
|
||||
<&tegra_car 74>;
|
||||
reset-names = "pex", "afi", "pcie_x";
|
||||
status = "disabled";
|
||||
|
||||
pci@1,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82000800 0 0x00000000 0 0x1000>;
|
||||
reg = <0x000800 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <2>;
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001000 0 0x00001000 0 0x1000>;
|
||||
reg = <0x001000 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <2>;
|
||||
};
|
||||
|
||||
pci@3,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001800 0 0x00004000 0 0x1000>;
|
||||
reg = <0x001800 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
Board DTS:
|
||||
|
||||
pcie-controller@3000 {
|
||||
status = "okay";
|
||||
|
||||
avdd-pexa-supply = <&ldo1_reg>;
|
||||
vdd-pexa-supply = <&ldo1_reg>;
|
||||
avdd-pexb-supply = <&ldo1_reg>;
|
||||
vdd-pexb-supply = <&ldo1_reg>;
|
||||
avdd-pex-pll-supply = <&ldo1_reg>;
|
||||
avdd-plle-supply = <&ldo1_reg>;
|
||||
vddio-pex-ctl-supply = <&sys_3v3_reg>;
|
||||
hvdd-pex-supply = <&sys_3v3_pexs_reg>;
|
||||
|
||||
pci@1,0 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
pci@3,0 {
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
Tegra124:
|
||||
---------
|
||||
|
||||
SoC DTSI:
|
||||
|
||||
pcie-controller@1003000 {
|
||||
compatible = "nvidia,tegra124-pcie";
|
||||
device_type = "pci";
|
||||
reg = <0x0 0x01003000 0x0 0x00000800 /* PADS registers */
|
||||
0x0 0x01003800 0x0 0x00000800 /* AFI registers */
|
||||
0x0 0x02000000 0x0 0x10000000>; /* configuration space */
|
||||
reg-names = "pads", "afi", "cs";
|
||||
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
|
||||
<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
|
||||
interrupt-names = "intr", "msi";
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
bus-range = <0x00 0xff>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges = <0x82000000 0 0x01000000 0x0 0x01000000 0 0x00001000 /* port 0 configuration space */
|
||||
0x82000000 0 0x01001000 0x0 0x01001000 0 0x00001000 /* port 1 configuration space */
|
||||
0x81000000 0 0x0 0x0 0x12000000 0 0x00010000 /* downstream I/O (64 KiB) */
|
||||
0x82000000 0 0x13000000 0x0 0x13000000 0 0x0d000000 /* non-prefetchable memory (208 MiB) */
|
||||
0xc2000000 0 0x20000000 0x0 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */
|
||||
|
||||
clocks = <&tegra_car TEGRA124_CLK_PCIE>,
|
||||
<&tegra_car TEGRA124_CLK_AFI>,
|
||||
<&tegra_car TEGRA124_CLK_PLL_E>,
|
||||
<&tegra_car TEGRA124_CLK_CML0>;
|
||||
clock-names = "pex", "afi", "pll_e", "cml";
|
||||
resets = <&tegra_car 70>,
|
||||
<&tegra_car 72>,
|
||||
<&tegra_car 74>;
|
||||
reset-names = "pex", "afi", "pcie_x";
|
||||
status = "disabled";
|
||||
|
||||
pci@1,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>;
|
||||
reg = <0x000800 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <2>;
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>;
|
||||
reg = <0x001000 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
Board DTS:
|
||||
|
||||
pcie-controller@1003000 {
|
||||
status = "okay";
|
||||
|
||||
avddio-pex-supply = <&vdd_1v05_run>;
|
||||
dvddio-pex-supply = <&vdd_1v05_run>;
|
||||
avdd-pex-pll-supply = <&vdd_1v05_run>;
|
||||
hvdd-pex-supply = <&vdd_3v3_lp0>;
|
||||
hvdd-pex-pll-e-supply = <&vdd_3v3_lp0>;
|
||||
vddio-pex-ctl-supply = <&vdd_3v3_lp0>;
|
||||
avdd-pll-erefe-supply = <&avdd_1v05_run>;
|
||||
|
||||
/* Mini PCIe */
|
||||
pci@1,0 {
|
||||
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-4}>;
|
||||
phy-names = "pcie-0";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
/* Gigabit Ethernet */
|
||||
pci@2,0 {
|
||||
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-2}>;
|
||||
phy-names = "pcie-0";
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
Tegra210:
|
||||
---------
|
||||
|
||||
SoC DTSI:
|
||||
|
||||
pcie-controller@1003000 {
|
||||
compatible = "nvidia,tegra210-pcie";
|
||||
device_type = "pci";
|
||||
reg = <0x0 0x01003000 0x0 0x00000800 /* PADS registers */
|
||||
0x0 0x01003800 0x0 0x00000800 /* AFI registers */
|
||||
0x0 0x02000000 0x0 0x10000000>; /* configuration space */
|
||||
reg-names = "pads", "afi", "cs";
|
||||
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
|
||||
<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
|
||||
interrupt-names = "intr", "msi";
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
bus-range = <0x00 0xff>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges = <0x82000000 0 0x01000000 0x0 0x01000000 0 0x00001000 /* port 0 configuration space */
|
||||
0x82000000 0 0x01001000 0x0 0x01001000 0 0x00001000 /* port 1 configuration space */
|
||||
0x81000000 0 0x0 0x0 0x12000000 0 0x00010000 /* downstream I/O (64 KiB) */
|
||||
0x82000000 0 0x13000000 0x0 0x13000000 0 0x0d000000 /* non-prefetchable memory (208 MiB) */
|
||||
0xc2000000 0 0x20000000 0x0 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */
|
||||
|
||||
clocks = <&tegra_car TEGRA210_CLK_PCIE>,
|
||||
<&tegra_car TEGRA210_CLK_AFI>,
|
||||
<&tegra_car TEGRA210_CLK_PLL_E>,
|
||||
<&tegra_car TEGRA210_CLK_CML0>;
|
||||
clock-names = "pex", "afi", "pll_e", "cml";
|
||||
resets = <&tegra_car 70>,
|
||||
<&tegra_car 72>,
|
||||
<&tegra_car 74>;
|
||||
reset-names = "pex", "afi", "pcie_x";
|
||||
status = "disabled";
|
||||
|
||||
pci@1,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>;
|
||||
reg = <0x000800 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <4>;
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>;
|
||||
reg = <0x001000 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
Board DTS:
|
||||
|
||||
pcie-controller@1003000 {
|
||||
status = "okay";
|
||||
|
||||
avdd-pll-uerefe-supply = <&avdd_1v05_pll>;
|
||||
hvddio-pex-supply = <&vdd_1v8>;
|
||||
dvddio-pex-supply = <&vdd_pex_1v05>;
|
||||
dvdd-pex-pll-supply = <&vdd_pex_1v05>;
|
||||
hvdd-pex-pll-e-supply = <&vdd_1v8>;
|
||||
vddio-pex-ctl-supply = <&vdd_1v8>;
|
||||
|
||||
pci@1,0 {
|
||||
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-0}>,
|
||||
<&{/padctl@7009f000/pads/pcie/lanes/pcie-1}>,
|
||||
<&{/padctl@7009f000/pads/pcie/lanes/pcie-2}>,
|
||||
<&{/padctl@7009f000/pads/pcie/lanes/pcie-3}>;
|
||||
phy-names = "pcie-0", "pcie-1", "pcie-2", "pcie-3";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
phys = <&{/padctl@7009f000/pads/pcie/lanes/pcie-4}>;
|
||||
phy-names = "pcie-0";
|
||||
status = "okay";
|
||||
};
|
||||
};
|
||||
|
||||
Tegra186:
|
||||
---------
|
||||
|
||||
SoC DTSI:
|
||||
|
||||
pcie@10003000 {
|
||||
compatible = "nvidia,tegra186-pcie";
|
||||
power-domains = <&bpmp TEGRA186_POWER_DOMAIN_PCX>;
|
||||
device_type = "pci";
|
||||
reg = <0x0 0x10003000 0x0 0x00000800 /* PADS registers */
|
||||
0x0 0x10003800 0x0 0x00000800 /* AFI registers */
|
||||
0x0 0x40000000 0x0 0x10000000>; /* configuration space */
|
||||
reg-names = "pads", "afi", "cs";
|
||||
|
||||
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
|
||||
<GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
|
||||
interrupt-names = "intr", "msi";
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0 0 0 0 &gic GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
bus-range = <0x00 0xff>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges = <0x82000000 0 0x10000000 0x0 0x10000000 0 0x00001000 /* port 0 configuration space */
|
||||
0x82000000 0 0x10001000 0x0 0x10001000 0 0x00001000 /* port 1 configuration space */
|
||||
0x82000000 0 0x10004000 0x0 0x10004000 0 0x00001000 /* port 2 configuration space */
|
||||
0x81000000 0 0x0 0x0 0x50000000 0 0x00010000 /* downstream I/O (64 KiB) */
|
||||
0x82000000 0 0x50100000 0x0 0x50100000 0 0x07F00000 /* non-prefetchable memory (127 MiB) */
|
||||
0xc2000000 0 0x58000000 0x0 0x58000000 0 0x28000000>; /* prefetchable memory (640 MiB) */
|
||||
|
||||
clocks = <&bpmp TEGRA186_CLK_AFI>,
|
||||
<&bpmp TEGRA186_CLK_PCIE>,
|
||||
<&bpmp TEGRA186_CLK_PLLE>;
|
||||
clock-names = "afi", "pex", "pll_e";
|
||||
|
||||
resets = <&bpmp TEGRA186_RESET_AFI>,
|
||||
<&bpmp TEGRA186_RESET_PCIE>,
|
||||
<&bpmp TEGRA186_RESET_PCIEXCLK>;
|
||||
reset-names = "afi", "pex", "pcie_x";
|
||||
|
||||
status = "disabled";
|
||||
|
||||
pci@1,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82000800 0 0x10000000 0 0x1000>;
|
||||
reg = <0x000800 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <2>;
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001000 0 0x10001000 0 0x1000>;
|
||||
reg = <0x001000 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <1>;
|
||||
};
|
||||
|
||||
pci@3,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001800 0 0x10004000 0 0x1000>;
|
||||
reg = <0x001800 0 0 0 0>;
|
||||
status = "disabled";
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
Board DTS:
|
||||
|
||||
pcie@10003000 {
|
||||
status = "okay";
|
||||
|
||||
dvdd-pex-supply = <&vdd_pex>;
|
||||
hvdd-pex-pll-supply = <&vdd_1v8>;
|
||||
hvdd-pex-supply = <&vdd_1v8>;
|
||||
vddio-pexctl-aud-supply = <&vdd_1v8>;
|
||||
|
||||
pci@1,0 {
|
||||
nvidia,num-lanes = <4>;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
nvidia,num-lanes = <0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pci@3,0 {
|
||||
nvidia,num-lanes = <1>;
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
532
Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.yaml
Normal file
532
Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.yaml
Normal file
|
|
@ -0,0 +1,532 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/pci/nvidia,tegra20-pcie.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NVIDIA Tegra PCIe controller
|
||||
|
||||
maintainers:
|
||||
- Thierry Reding <thierry.reding@gmail.com>
|
||||
- Jon Hunter <jonathanh@nvidia.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- nvidia,tegra20-pcie
|
||||
- nvidia,tegra30-pcie
|
||||
- nvidia,tegra124-pcie
|
||||
- nvidia,tegra210-pcie
|
||||
- nvidia,tegra186-pcie
|
||||
|
||||
- items:
|
||||
- const: nvidia,tegra132-pcie
|
||||
- const: nvidia,tegra124-pcie
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: PADS registers
|
||||
- description: AFI registers
|
||||
- description: configuration space region
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: pads
|
||||
- const: afi
|
||||
- const: cs
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: PCIe controller interrupt
|
||||
- description: MSI controller interrupt
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: intr
|
||||
- const: msi
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: PCI controller clock
|
||||
- description: AFI interface clock
|
||||
- description: reference PLL clock
|
||||
- description: CML clock
|
||||
minItems: 3
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: pex
|
||||
- const: afi
|
||||
- const: pll_e
|
||||
- const: cml
|
||||
minItems: 3
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: AFI interface reset
|
||||
- description: PCI controller reset
|
||||
- description: PCI bus reset
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: pex
|
||||
- const: afi
|
||||
- const: pcie_x
|
||||
|
||||
interconnects:
|
||||
items:
|
||||
- description: AFI memory read client
|
||||
- description: AFI memory write client
|
||||
|
||||
interconnect-names:
|
||||
items:
|
||||
- const: dma-mem # read
|
||||
- const: write
|
||||
|
||||
iommus:
|
||||
maxItems: 1
|
||||
|
||||
operating-points-v2:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
|
||||
power-domains:
|
||||
items:
|
||||
- description: phandle to the core power domain
|
||||
|
||||
pinctrl-names:
|
||||
items:
|
||||
# active state, puts PCIe I/O out of deep power down state
|
||||
- const: default
|
||||
# puts PCIe I/O into deep power down state
|
||||
- const: idle
|
||||
|
||||
patternProperties:
|
||||
"^pci@1?[0-9a-f](,[0-7])?$":
|
||||
description: Root ports are defined as subnodes of the PCIe controller
|
||||
node.
|
||||
|
||||
Note that devices on the PCI bus are dynamically discovered using PCI's
|
||||
bus enumeration and therefore don't need corresponding device nodes in
|
||||
DT. However if a device on the PCI bus provides a non-probeable bus such
|
||||
as I2C or SPI, device nodes need to be added in order to allow the bus'
|
||||
children to be instantiated at the proper location in the operating
|
||||
system's device tree (as illustrated by the optional nodes in the
|
||||
examples below).
|
||||
|
||||
$ref: /schemas/pci/pci-pci-bridge.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
nvidia,num-lanes:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Number of lanes to use for this port. Valid combinations
|
||||
are:
|
||||
|
||||
- Root port 0 uses 4 lanes, root port 1 is unused.
|
||||
- Both root ports use 2 lanes.
|
||||
enum: [ 0, 1, 2, 4 ]
|
||||
|
||||
phys:
|
||||
description: Must contain an phandle to a PHY for each entry in
|
||||
phy-names.
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
phy-names:
|
||||
description: Must include an entry for each active lane. Note that the
|
||||
number of entries does not have to (though usually will) be equal to
|
||||
the specified number of lanes in the nvidia,num-lanes property.
|
||||
Entries are of the form "pcie-N", where N ranges from 0 to the value
|
||||
specified in nvidia,num-lanes.
|
||||
items:
|
||||
enum:
|
||||
- pcie-0
|
||||
- pcie-1
|
||||
- pcie-2
|
||||
- pcie-3
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
required:
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- resets
|
||||
- reset-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/pinctrl/pinctrl-consumer.yaml#
|
||||
- $ref: /schemas/pci/pci-host-bridge.yaml#
|
||||
- $ref: pci-iommu.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: nvidia,tegra20-pcie
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
maxItems: 3
|
||||
|
||||
avdd-pex-supply:
|
||||
description: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
|
||||
vdd-pex-supply:
|
||||
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
|
||||
avdd-pex-pll-supply:
|
||||
description: Power supply for dedicated (internal) PCIe PLL. Must
|
||||
supply 1.05 V.
|
||||
|
||||
avdd-plle-supply:
|
||||
description: Power supply for PLLE, which is shared with SATA. Must
|
||||
supply 1.05 V.
|
||||
|
||||
vddio-pex-clk-supply:
|
||||
description: Power supply for PCIe clock. Must supply 3.3 V.
|
||||
|
||||
required:
|
||||
- avdd-pex-supply
|
||||
- vdd-pex-supply
|
||||
- avdd-pex-pll-supply
|
||||
- avdd-plle-supply
|
||||
- vddio-pex-clk-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: nvidia,tegra30-pcie
|
||||
then:
|
||||
properties:
|
||||
avdd-pex-pll-supply:
|
||||
description: Power supply for dedicated (internal) PCIe PLL. Must
|
||||
supply 1.05 V.
|
||||
|
||||
avdd-plle-supply:
|
||||
description: Power supply for PLLE, which is shared with SATA. Must
|
||||
supply 1.05 V.
|
||||
|
||||
vddio-pex-ctl-supply:
|
||||
description: Power supply for PCIe control I/O partition. Must
|
||||
supply 1.8 V.
|
||||
|
||||
hvdd-pex-supply:
|
||||
description: High-voltage supply for PCIe I/O and PCIe output
|
||||
clocks. Must supply 3.3 V.
|
||||
|
||||
avdd-pexa-supply:
|
||||
description: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
Required if lanes 0 through 3 are used.
|
||||
|
||||
vdd-pexa-supply:
|
||||
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
Required if lanes 0 through 3 are used.
|
||||
|
||||
avdd-pexb-supply:
|
||||
description: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
Required if lanes 4 and 5 are used.
|
||||
|
||||
vdd-pexb-supply:
|
||||
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
Required if lanes 4 and 5 are used.
|
||||
|
||||
required:
|
||||
- avdd-pex-pll-supply
|
||||
- avdd-plle-supply
|
||||
- vddio-pex-ctl-supply
|
||||
- hvdd-pex-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: nvidia,tegra124-pcie
|
||||
then:
|
||||
properties:
|
||||
phys:
|
||||
minItems: 1
|
||||
maxItems: 1
|
||||
deprecated: true
|
||||
|
||||
phy-names:
|
||||
items:
|
||||
- const: pcie
|
||||
deprecated: true
|
||||
|
||||
avddio-pex-supply:
|
||||
description: Power supply for analog PCIe logic. Must supply 1.05 V.
|
||||
|
||||
dvddio-pex-supply:
|
||||
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
|
||||
hvdd-pex-supply:
|
||||
description: High-voltage supply for PCIe I/O and PCIe output
|
||||
clocks. Must supply 3.3 V.
|
||||
|
||||
vddio-pex-ctl-supply:
|
||||
description: Power supply for PCIe control I/O partition. Must
|
||||
supply 2.8-3.3 V.
|
||||
|
||||
avdd-pex-pll-supply:
|
||||
deprecated: true
|
||||
|
||||
hvdd-pex-pll-e-supply:
|
||||
deprecated: true
|
||||
|
||||
avdd-pll-erefe-supply:
|
||||
deprecated: true
|
||||
|
||||
required:
|
||||
- avddio-pex-supply
|
||||
- dvddio-pex-supply
|
||||
- hvdd-pex-supply
|
||||
- vddio-pex-ctl-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: nvidia,tegra210-pcie
|
||||
then:
|
||||
properties:
|
||||
hvddio-pex-supply:
|
||||
description: High-voltage supply for PCIe I/O and PCIe output
|
||||
clocks. Must supply 1.8 V.
|
||||
|
||||
dvddio-pex-supply:
|
||||
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
|
||||
vddio-pex-ctl-supply:
|
||||
description: Power supply for PCIe control I/O partition. Must
|
||||
supply 1.8 V.
|
||||
|
||||
avdd-pll-uerefe-supply:
|
||||
deprecated: true
|
||||
|
||||
dvdd-pex-pll-supply:
|
||||
deprecated: true
|
||||
|
||||
hvdd-pex-pll-e-supply:
|
||||
deprecated: true
|
||||
|
||||
required:
|
||||
- hvddio-pex-supply
|
||||
- dvddio-pex-supply
|
||||
- vddio-pex-ctl-supply
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: nvidia,tegra186-pcie
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 3
|
||||
|
||||
clock-names:
|
||||
maxItems: 3
|
||||
|
||||
power-domains:
|
||||
description: To ungate power partition by BPMP powergate driver.
|
||||
Must contain BPMP phandle and PCIe power partition ID.
|
||||
|
||||
dvdd-pex-supply:
|
||||
description: Power supply for digital PCIe I/O. Must supply 1.05 V.
|
||||
|
||||
hvdd-pex-pll-supply:
|
||||
description: High-voltage supply for PLLE (shared with USB3). Must
|
||||
supply 1.8 V.
|
||||
|
||||
hvdd-pex-supply:
|
||||
description: High-voltage supply for PCIe I/O and PCIe output
|
||||
clocks. Must supply 1.8 V.
|
||||
|
||||
vddio-pexctl-aud-supply:
|
||||
description: Power supply for PCIe side band signals. Must supply
|
||||
1.8 V.
|
||||
|
||||
required:
|
||||
- dvdd-pex-supply
|
||||
- hvdd-pex-pll-supply
|
||||
- hvdd-pex-supply
|
||||
- vddio-pexctl-aud-supply
|
||||
|
||||
examples:
|
||||
# Tegra20
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
pcie@80003000 {
|
||||
compatible = "nvidia,tegra20-pcie";
|
||||
device_type = "pci";
|
||||
reg = <0x80003000 0x00000800>, /* PADS registers */
|
||||
<0x80003800 0x00000200>, /* AFI registers */
|
||||
<0x90000000 0x10000000>; /* configuration space */
|
||||
reg-names = "pads", "afi", "cs";
|
||||
interrupts = <0 98 0x04>, /* controller interrupt */
|
||||
<0 99 0x04>; /* MSI interrupt */
|
||||
interrupt-names = "intr", "msi";
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0 0 0 0 &intc GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
bus-range = <0x00 0xff>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges = <0x02000000 0 0x80000000 0x80000000 0 0x00001000>, /* port 0 registers */
|
||||
<0x02000000 0 0x80001000 0x80001000 0 0x00001000>, /* port 1 registers */
|
||||
<0x01000000 0 0 0x82000000 0 0x00010000>, /* downstream I/O */
|
||||
<0x02000000 0 0xa0000000 0xa0000000 0 0x10000000>, /* non-prefetchable memory */
|
||||
<0x42000000 0 0xb0000000 0xb0000000 0 0x10000000>; /* prefetchable memory */
|
||||
|
||||
clocks = <&tegra_car 70>, <&tegra_car 72>, <&tegra_car 118>;
|
||||
clock-names = "pex", "afi", "pll_e";
|
||||
resets = <&tegra_car 70>, <&tegra_car 72>, <&tegra_car 74>;
|
||||
reset-names = "pex", "afi", "pcie_x";
|
||||
|
||||
avdd-pex-supply = <&pci_vdd_reg>;
|
||||
vdd-pex-supply = <&pci_vdd_reg>;
|
||||
avdd-pex-pll-supply = <&pci_vdd_reg>;
|
||||
avdd-plle-supply = <&pci_vdd_reg>;
|
||||
vddio-pex-clk-supply = <&pci_clk_reg>;
|
||||
|
||||
/* root port 00:01.0 */
|
||||
pci@1,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82000800 0 0x80000000 0 0x1000>;
|
||||
reg = <0x000800 0 0 0 0>;
|
||||
bus-range = <0x00 0xff>;
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <2>;
|
||||
|
||||
/* bridge 01:00.0 (optional) */
|
||||
pci@0,0 {
|
||||
reg = <0x010000 0 0 0 0>;
|
||||
bus-range = <0x00 0xff>;
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
device_type = "pci";
|
||||
|
||||
/* endpoint 02:00.0 */
|
||||
ethernet@0,0 {
|
||||
reg = <0x020000 0 0 0 0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001000 0 0x80001000 0 0x1000>;
|
||||
reg = <0x001000 0 0 0 0>;
|
||||
bus-range = <0x00 0xff>;
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
# Tegra210
|
||||
- |
|
||||
#include <dt-bindings/clock/tegra210-car.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
pcie@1003000 {
|
||||
compatible = "nvidia,tegra210-pcie";
|
||||
device_type = "pci";
|
||||
reg = <0x01003000 0x00000800>, /* PADS registers */
|
||||
<0x01003800 0x00000800>, /* AFI registers */
|
||||
<0x02000000 0x10000000>; /* configuration space */
|
||||
reg-names = "pads", "afi", "cs";
|
||||
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
|
||||
<GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>; /* MSI interrupt */
|
||||
interrupt-names = "intr", "msi";
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
interrupt-map-mask = <0 0 0 0>;
|
||||
interrupt-map = <0 0 0 0 &gic GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
|
||||
|
||||
bus-range = <0x00 0xff>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
|
||||
ranges = <0x02000000 0 0x01000000 0x01000000 0 0x00001000>, /* port 0 configuration space */
|
||||
<0x02000000 0 0x01001000 0x01001000 0 0x00001000>, /* port 1 configuration space */
|
||||
<0x01000000 0 0x0 0x12000000 0 0x00010000>, /* downstream I/O (64 KiB) */
|
||||
<0x02000000 0 0x13000000 0x13000000 0 0x0d000000>, /* non-prefetchable memory (208 MiB) */
|
||||
<0x42000000 0 0x20000000 0x20000000 0 0x20000000>; /* prefetchable memory (512 MiB) */
|
||||
|
||||
clocks = <&tegra_car TEGRA210_CLK_PCIE>,
|
||||
<&tegra_car TEGRA210_CLK_AFI>,
|
||||
<&tegra_car TEGRA210_CLK_PLL_E>,
|
||||
<&tegra_car TEGRA210_CLK_CML0>;
|
||||
clock-names = "pex", "afi", "pll_e", "cml";
|
||||
resets = <&tegra_car 70>,
|
||||
<&tegra_car 72>,
|
||||
<&tegra_car 74>;
|
||||
reset-names = "pex", "afi", "pcie_x";
|
||||
|
||||
avdd-pll-uerefe-supply = <&avdd_1v05_pll>;
|
||||
hvddio-pex-supply = <&vdd_1v8>;
|
||||
dvddio-pex-supply = <&vdd_pex_1v05>;
|
||||
dvdd-pex-pll-supply = <&vdd_pex_1v05>;
|
||||
hvdd-pex-pll-e-supply = <&vdd_1v8>;
|
||||
vddio-pex-ctl-supply = <&vdd_1v8>;
|
||||
|
||||
pci@1,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82000800 0 0x01000000 0 0x1000>;
|
||||
reg = <0x000800 0 0 0 0>;
|
||||
bus-range = <0x00 0xff>;
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <4>;
|
||||
|
||||
phys = <&phy_pcie0>, <&phy_pcie1>, <&phy_pcie2>, <&phy_pcie3>;
|
||||
phy-names = "pcie-0", "pcie-1", "pcie-2", "pcie-3";
|
||||
};
|
||||
|
||||
pci@2,0 {
|
||||
device_type = "pci";
|
||||
assigned-addresses = <0x82001000 0 0x01001000 0 0x1000>;
|
||||
reg = <0x001000 0 0 0 0>;
|
||||
bus-range = <0x00 0xff>;
|
||||
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
nvidia,num-lanes = <1>;
|
||||
|
||||
phys = <&phy_pcie4>;
|
||||
phy-names = "pcie-0";
|
||||
};
|
||||
};
|
||||
|
|
@ -56,11 +56,14 @@ properties:
|
|||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: pwr
|
||||
- const: core
|
||||
|
||||
max-link-speed:
|
||||
maximum: 4
|
||||
|
|
@ -109,8 +112,8 @@ examples:
|
|||
clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
|
||||
clock-names = "core", "ref";
|
||||
power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 624>;
|
||||
reset-names = "pwr";
|
||||
resets = <&cpg 624>,<&cpg 1121>;
|
||||
reset-names = "pwr", "core";
|
||||
max-link-speed = <4>;
|
||||
num-lanes = <2>;
|
||||
max-functions = /bits/ 8 <2>;
|
||||
|
|
|
|||
|
|
@ -10,30 +10,20 @@ title: Renesas R-Car Gen4 PCIe Host
|
|||
maintainers:
|
||||
- Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dw-pcie.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r8a779f0-pcie # R-Car S4-8
|
||||
- renesas,r8a779g0-pcie # R-Car V4H
|
||||
- renesas,r8a779h0-pcie # R-Car V4M
|
||||
- const: renesas,rcar-gen4-pcie # R-Car Gen4
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r8a779f0-pcie # R-Car S4-8
|
||||
- renesas,r8a779g0-pcie # R-Car V4H
|
||||
- renesas,r8a779h0-pcie # R-Car V4M
|
||||
- const: renesas,rcar-gen4-pcie # R-Car Gen4
|
||||
|
||||
reg:
|
||||
maxItems: 7
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: dbi
|
||||
- const: dbi2
|
||||
- const: atu
|
||||
- const: dma
|
||||
- const: app
|
||||
- const: phy
|
||||
- const: config
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r8a78000-pcie4 # R-Car X5H PCIe4
|
||||
- const: renesas,rcar-gen5-pcie4 # R-Car Gen5 PCIe4
|
||||
|
||||
interrupts:
|
||||
maxItems: 4
|
||||
|
|
@ -57,11 +47,14 @@ properties:
|
|||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
reset-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: pwr
|
||||
- const: core
|
||||
|
||||
max-link-speed:
|
||||
maximum: 4
|
||||
|
|
@ -84,6 +77,46 @@ required:
|
|||
- resets
|
||||
- reset-names
|
||||
|
||||
allOf:
|
||||
- $ref: snps,dw-pcie.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,rcar-gen4-pcie
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 7
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: dbi
|
||||
- const: dbi2
|
||||
- const: atu
|
||||
- const: dma
|
||||
- const: app
|
||||
- const: phy
|
||||
- const: config
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,rcar-gen5-pcie4
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 6
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: dbi
|
||||
- const: dbi2
|
||||
- const: atu
|
||||
- const: dma
|
||||
- const: app
|
||||
- const: config
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
|
@ -111,8 +144,8 @@ examples:
|
|||
clocks = <&cpg CPG_MOD 624>, <&pcie0_clkref>;
|
||||
clock-names = "core", "ref";
|
||||
power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>;
|
||||
resets = <&cpg 624>;
|
||||
reset-names = "pwr";
|
||||
resets = <&cpg 624>,<&cpg 1121>;
|
||||
reset-names = "pwr", "core";
|
||||
max-link-speed = <4>;
|
||||
num-lanes = <2>;
|
||||
#address-cells = <3>;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ description: |
|
|||
PCIe host controller found in Renesas RZ/G3S and similar SoCs complies
|
||||
with PCIe Base Specification 4.0 and supports different link speeds
|
||||
depending on the SoC variant:
|
||||
- Gen2 (5 GT/s): RZ/G3S
|
||||
- Gen2 (5 GT/s): RZ/G3{L,S}
|
||||
- Gen3 (8 GT/s): RZ/G3E, RZ/V2H(P), RZ/V2N
|
||||
|
||||
properties:
|
||||
|
|
@ -21,6 +21,7 @@ properties:
|
|||
oneOf:
|
||||
- enum:
|
||||
- renesas,r9a08g045-pcie # RZ/G3S
|
||||
- renesas,r9a08g046-pcie # RZ/G3L
|
||||
- renesas,r9a09g047-pcie # RZ/G3E
|
||||
- renesas,r9a09g057-pcie # RZ/V2H(P)
|
||||
- items:
|
||||
|
|
@ -87,14 +88,18 @@ properties:
|
|||
interrupt-controller: true
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
items:
|
||||
- description: System clock
|
||||
- description: PM control clock or clock for L1 substate handling
|
||||
- description: PM control clock
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: aclk
|
||||
- enum: [pm, pmu]
|
||||
- const: pmu
|
||||
|
||||
resets:
|
||||
minItems: 1
|
||||
|
|
@ -215,6 +220,8 @@ allOf:
|
|||
maxItems: 16
|
||||
interrupt-names:
|
||||
maxItems: 16
|
||||
clocks:
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: aclk
|
||||
|
|
@ -223,6 +230,28 @@ allOf:
|
|||
minItems: 7
|
||||
reset-names:
|
||||
minItems: 7
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,r9a08g046-pcie
|
||||
then:
|
||||
properties:
|
||||
interrupts:
|
||||
maxItems: 16
|
||||
interrupt-names:
|
||||
maxItems: 16
|
||||
clocks:
|
||||
minItems: 3
|
||||
clock-names:
|
||||
items:
|
||||
- const: aclk
|
||||
- const: pm
|
||||
- const: pmu
|
||||
resets:
|
||||
maxItems: 1
|
||||
reset-names:
|
||||
maxItems: 1
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -236,6 +265,8 @@ allOf:
|
|||
minItems: 23
|
||||
interrupt-names:
|
||||
minItems: 23
|
||||
clocks:
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: aclk
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ properties:
|
|||
description:
|
||||
GPIO controlling the RESX# pin.
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
'#gpio-cells':
|
||||
const: 2
|
||||
|
||||
vdd18-supply: true
|
||||
|
||||
vdd09-supply: true
|
||||
|
|
@ -128,7 +133,7 @@ examples:
|
|||
ranges;
|
||||
bus-range = <0x01 0xff>;
|
||||
|
||||
pcie@0,0 {
|
||||
tc9563: pcie@0,0 {
|
||||
compatible = "pci1179,0623";
|
||||
|
||||
reg = <0x10000 0x0 0x0 0x0 0x0>;
|
||||
|
|
@ -149,6 +154,9 @@ examples:
|
|||
|
||||
resx-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
|
||||
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
|
||||
pcie@1,0 {
|
||||
compatible = "pciclass,0604";
|
||||
reg = <0x20800 0x0 0x0 0x0 0x0>;
|
||||
|
|
@ -158,6 +166,8 @@ examples:
|
|||
ranges;
|
||||
bus-range = <0x03 0xff>;
|
||||
|
||||
reset-gpios = <&tc9563 2 GPIO_ACTIVE_LOW>;
|
||||
|
||||
toshiba,no-dfe-support;
|
||||
};
|
||||
|
||||
|
|
@ -170,6 +180,8 @@ examples:
|
|||
ranges;
|
||||
bus-range = <0x04 0xff>;
|
||||
|
||||
reset-gpios = <&tc9563 3 GPIO_ACTIVE_LOW>;
|
||||
|
||||
toshiba,tx-amplitude-microvolt = <10>;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@ properties:
|
|||
description:
|
||||
Maps a Requester ID to an MSI controller and associated MSI sideband data.
|
||||
|
||||
resets:
|
||||
description: Reset for the PCIe controller.
|
||||
maxItems: 1
|
||||
|
||||
ranges:
|
||||
maxItems: 2
|
||||
|
||||
|
|
@ -61,6 +65,17 @@ properties:
|
|||
|
||||
interrupt-controller: true
|
||||
|
||||
patternProperties:
|
||||
'^pcie@[0-2],0$':
|
||||
type: object
|
||||
$ref: /schemas/pci/pci-pci-bridge.yaml#
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- reg
|
||||
- reg-names
|
||||
|
|
@ -76,6 +91,9 @@ unevaluatedProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/reset/xlnx-versal-resets.h>
|
||||
|
||||
versal {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
|
@ -99,6 +117,16 @@ examples:
|
|||
reg = <0x0 0xfca10000 0x0 0x1000>,
|
||||
<0x6 0x00000000 0x0 0x10000000>;
|
||||
reg-names = "cpm_slcr", "cfg";
|
||||
resets = <&versal_reset VERSAL_RST_PCIE_CORE0>;
|
||||
|
||||
pcie@0,0 {
|
||||
device_type = "pci";
|
||||
reg = <0x0 0x0 0x0 0x0 0x0>;
|
||||
reset-gpios = <&gpio1 38 GPIO_ACTIVE_LOW>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
};
|
||||
pcie_intc_0: interrupt-controller {
|
||||
#address-cells = <0>;
|
||||
#interrupt-cells = <1>;
|
||||
|
|
@ -127,6 +155,16 @@ examples:
|
|||
<0x06 0x00000000 0x00 0x1000000>,
|
||||
<0x00 0xfce20000 0x00 0x1000000>;
|
||||
reg-names = "cpm_slcr", "cfg", "cpm_csr";
|
||||
resets = <&versal_reset VERSAL_RST_PCIE_CORE0>;
|
||||
|
||||
pcie@0,0 {
|
||||
device_type = "pci";
|
||||
reg = <0x0 0x0 0x0 0x0 0x0>;
|
||||
reset-gpios = <&gpio1 38 GPIO_ACTIVE_LOW>;
|
||||
#address-cells = <3>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
};
|
||||
|
||||
pcie_intc_1: interrupt-controller {
|
||||
#address-cells = <0>;
|
||||
|
|
|
|||
|
|
@ -167,9 +167,11 @@ In this case the initiator and target pci_devices are known and the P2P subsyste
|
|||
is used to determine the mapping type. The phys_addr_t-based DMA API is used to
|
||||
establish the dma_addr_t.
|
||||
|
||||
Lifecycle is controlled by DMABUF move_notify(). When the exporting driver wants
|
||||
Lifecycle is controlled by DMABUF revocation. When the exporting driver wants
|
||||
to remove() it must deliver an invalidation shutdown to all DMABUF importing
|
||||
drivers through move_notify() and synchronously DMA unmap all the MMIO.
|
||||
drivers through dma_buf_invalidate_mappings() and synchronously DMA unmap all
|
||||
the MMIO. Importers unable to complete that unmap within bounded time have to
|
||||
be rejected when they attach, which dma_buf_attach_revocable() checks for.
|
||||
|
||||
No importing driver can continue to have a DMA map to the MMIO after the
|
||||
exporting driver has destroyed its p2p_provider.
|
||||
|
|
|
|||
|
|
@ -21110,7 +21110,6 @@ M: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
|
|||
L: linux-pci@vger.kernel.org
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/PCI/controller/rcar-pcie-firmware.rst
|
||||
F: Documentation/devicetree/bindings/pci/*rcar*
|
||||
F: drivers/pci/controller/*rcar*
|
||||
F: drivers/pci/controller/dwc/*rcar*
|
||||
|
|
|
|||
|
|
@ -993,6 +993,25 @@ static void acpi_pci_root_release_info(struct pci_host_bridge *bridge)
|
|||
__acpi_pci_root_release_info(bridge->release_data);
|
||||
}
|
||||
|
||||
#define OSC_OWNER(ctrl, bit, flag) \
|
||||
do { \
|
||||
if (!((ctrl) & (bit))) \
|
||||
flag = 0; \
|
||||
} while (0)
|
||||
|
||||
#define FLAG(x) ((x) ? '+' : '-')
|
||||
|
||||
/*
|
||||
* _OSC control bits for the features implemented by the PCIe port driver,
|
||||
* i.e., the ones "pcie_ports=native" applies to. LTR and SHPC hotplug are
|
||||
* negotiated via _OSC as well, but they are not portdrv services, so
|
||||
* "pcie_ports=" has no bearing on them.
|
||||
*/
|
||||
#define OSC_PCIE_PORT_SERVICE_CONTROLS (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | \
|
||||
OSC_PCI_EXPRESS_PME_CONTROL | \
|
||||
OSC_PCI_EXPRESS_AER_CONTROL | \
|
||||
OSC_PCI_EXPRESS_DPC_CONTROL)
|
||||
|
||||
struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
|
||||
struct acpi_pci_root_ops *ops,
|
||||
struct acpi_pci_root_info *info,
|
||||
|
|
@ -1003,6 +1022,7 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
|
|||
int node = acpi_get_node(device->handle);
|
||||
struct pci_bus *bus;
|
||||
struct pci_host_bridge *host_bridge;
|
||||
u32 ctrl, ext_ctrl, override = 0;
|
||||
|
||||
info->root = root;
|
||||
info->bridge = device;
|
||||
|
|
@ -1028,21 +1048,49 @@ struct pci_bus *acpi_pci_root_create(struct acpi_pci_root *root,
|
|||
goto out_release_info;
|
||||
|
||||
host_bridge = to_pci_host_bridge(bus->bridge);
|
||||
if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
|
||||
host_bridge->native_pcie_hotplug = 0;
|
||||
if (!(root->osc_control_set & OSC_PCI_SHPC_NATIVE_HP_CONTROL))
|
||||
host_bridge->native_shpc_hotplug = 0;
|
||||
if (!(root->osc_control_set & OSC_PCI_EXPRESS_AER_CONTROL))
|
||||
host_bridge->native_aer = 0;
|
||||
if (!(root->osc_control_set & OSC_PCI_EXPRESS_PME_CONTROL))
|
||||
host_bridge->native_pme = 0;
|
||||
if (!(root->osc_control_set & OSC_PCI_EXPRESS_LTR_CONTROL))
|
||||
host_bridge->native_ltr = 0;
|
||||
if (!(root->osc_control_set & OSC_PCI_EXPRESS_DPC_CONTROL))
|
||||
host_bridge->native_dpc = 0;
|
||||
|
||||
if (!(root->osc_ext_control_set & OSC_CXL_ERROR_REPORTING_CONTROL))
|
||||
host_bridge->native_cxl_error = 0;
|
||||
ctrl = root->osc_control_set;
|
||||
ext_ctrl = root->osc_ext_control_set;
|
||||
|
||||
/*
|
||||
* If the user specified "pcie_ports=native", use the PCIe port
|
||||
* services regardless of what _OSC says, i.e., proceed as though the
|
||||
* platform had granted us control of them. "pcie_ports=dpc-native"
|
||||
* does the same for DPC alone. This may conflict with firmware that
|
||||
* expects to own those features.
|
||||
*/
|
||||
if (pcie_ports_native)
|
||||
override = OSC_PCIE_PORT_SERVICE_CONTROLS & ~ctrl;
|
||||
else if (pcie_ports_dpc_native)
|
||||
override = OSC_PCI_EXPRESS_DPC_CONTROL & ~ctrl;
|
||||
|
||||
if (override) {
|
||||
decode_osc_control(root, pcie_ports_native ?
|
||||
"OS forcing control (\"pcie_ports=native\") of" :
|
||||
"OS forcing control (\"pcie_ports=dpc-native\") of",
|
||||
override);
|
||||
ctrl |= override;
|
||||
}
|
||||
|
||||
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_NATIVE_HP_CONTROL,
|
||||
host_bridge->native_pcie_hotplug);
|
||||
OSC_OWNER(ctrl, OSC_PCI_SHPC_NATIVE_HP_CONTROL,
|
||||
host_bridge->native_shpc_hotplug);
|
||||
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_AER_CONTROL, host_bridge->native_aer);
|
||||
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_PME_CONTROL, host_bridge->native_pme);
|
||||
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_LTR_CONTROL, host_bridge->native_ltr);
|
||||
OSC_OWNER(ctrl, OSC_PCI_EXPRESS_DPC_CONTROL, host_bridge->native_dpc);
|
||||
|
||||
OSC_OWNER(ext_ctrl, OSC_CXL_ERROR_REPORTING_CONTROL,
|
||||
host_bridge->native_cxl_error);
|
||||
|
||||
dev_info(&root->device->dev, "OS native features: SHPCHotplug%c PCIeHotplug%c PME%c AER%c DPC%c LTR%c\n",
|
||||
FLAG(host_bridge->native_shpc_hotplug),
|
||||
FLAG(host_bridge->native_pcie_hotplug),
|
||||
FLAG(host_bridge->native_pme),
|
||||
FLAG(host_bridge->native_aer),
|
||||
FLAG(host_bridge->native_dpc),
|
||||
FLAG(host_bridge->native_ltr));
|
||||
|
||||
acpi_dev_power_up_children_with_adr(device);
|
||||
|
||||
|
|
|
|||
|
|
@ -1875,6 +1875,18 @@ config GPIO_LTC4283
|
|||
|
||||
endmenu
|
||||
|
||||
config GPIO_TC9563
|
||||
tristate "Toshiba TC9563 GPIO support"
|
||||
depends on PCI_PWRCTRL_TC9563 || COMPILE_TEST
|
||||
default m if ARCH_QCOM
|
||||
select AUXILIARY_BUS
|
||||
select GPIO_REGMAP
|
||||
help
|
||||
This enables support for the GPIO controller embedded in the Toshiba
|
||||
TC9563 (and Qualcomm QPS615). This device connects to the host
|
||||
via PCIe port, which is the upstream port on an internal PCIe
|
||||
switch.
|
||||
|
||||
menu "PCI GPIO expanders"
|
||||
depends on PCI
|
||||
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
|
|||
obj-$(CONFIG_GPIO_TANGIER) += gpio-tangier.o
|
||||
obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
|
||||
obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
|
||||
obj-$(CONFIG_GPIO_TC9563) += gpio-tc9563.o
|
||||
obj-$(CONFIG_GPIO_TEGRA186) += gpio-tegra186.o
|
||||
obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o
|
||||
obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o
|
||||
|
|
|
|||
99
drivers/gpio/gpio-tc9563.c
Normal file
99
drivers/gpio/gpio-tc9563.c
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2026 by RISCstar Solutions Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The Toshiba TC9563 implements a PCIe Gen 3 switch that connects an
|
||||
* upstream x4 port to two downstream PCIe x2 ports. It incorporates
|
||||
* an internal endpoint on a internal PCIe port that implements two
|
||||
* Synopsys XGMAC Ethernet interfaces.
|
||||
*
|
||||
* 37 GPIOs are also implemented by an embedded GPIO controller. Three
|
||||
* registers control the first 32 GPIOs (other than 20 and 21, which are
|
||||
* reserved). Three other registers control GPIOs 32 through 36. GPIOs
|
||||
* 22-24, 27-28, 31, and 34 are treated as "input only".
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/regmap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/soc/qcom/tc9563.h>
|
||||
|
||||
/*
|
||||
* There are two sets of registers, each representing (up to) 32 GPIOs with a
|
||||
* stride of 4 bytes (IN1 is 4 bytes past IN0, EN1 is 4 bytes past EN0, etc.).
|
||||
*/
|
||||
#define TC9563_GPIO_COUNT 37
|
||||
#define TC9563_GPIO_PER_REG 32
|
||||
#define TC9563_GPIO_REG_STRIDE 4
|
||||
|
||||
static int tc9563_gpio_init_valid_mask(struct gpio_chip *gc,
|
||||
unsigned long *valid_mask,
|
||||
unsigned int ngpios)
|
||||
{
|
||||
/* GPIOs 20 and 21 are reserved */
|
||||
bitmap_fill(valid_mask, ngpios);
|
||||
bitmap_clear(valid_mask, 20, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc9563_gpio_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct gpio_regmap_config config = {
|
||||
.parent = &adev->dev,
|
||||
.ngpio = TC9563_GPIO_COUNT,
|
||||
.reg_stride = TC9563_GPIO_REG_STRIDE,
|
||||
.ngpio_per_reg = TC9563_GPIO_PER_REG,
|
||||
.reg_dat_base = GPIO_REGMAP_ADDR(TC9563_GPIO_IN0_OFFSET),
|
||||
.reg_set_base = GPIO_REGMAP_ADDR(TC9563_GPIO_OUT0_OFFSET),
|
||||
.reg_dir_in_base = GPIO_REGMAP_ADDR(TC9563_GPIO_EN0_OFFSET),
|
||||
.init_valid_mask = tc9563_gpio_init_valid_mask,
|
||||
};
|
||||
DECLARE_BITMAP(fixed_dir_mask, TC9563_GPIO_COUNT);
|
||||
DECLARE_BITMAP(fixed_dir_out, TC9563_GPIO_COUNT);
|
||||
|
||||
config.regmap = dev_get_platdata(&adev->dev);
|
||||
if (!config.regmap)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Only some of our GPIOs are fixed direction:
|
||||
* 22, 23, 24, 27, 28, 31, and 34 are input-only.
|
||||
*/
|
||||
bitmap_zero(fixed_dir_mask, TC9563_GPIO_COUNT);
|
||||
bitmap_set(fixed_dir_mask, 22, 3);
|
||||
bitmap_set(fixed_dir_mask, 27, 2);
|
||||
set_bit(31, fixed_dir_mask);
|
||||
set_bit(34, fixed_dir_mask);
|
||||
config.fixed_direction_mask = fixed_dir_mask;
|
||||
|
||||
bitmap_zero(fixed_dir_out, TC9563_GPIO_COUNT);
|
||||
config.fixed_direction_output = fixed_dir_out;
|
||||
|
||||
return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&adev->dev, &config));
|
||||
};
|
||||
|
||||
static const struct auxiliary_device_id tc9563_gpio_ids[] = {
|
||||
{ "pci_pwrctrl_tc9563." TC9563_GPIO_DEV_NAME },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, tc9563_gpio_ids);
|
||||
|
||||
static struct auxiliary_driver tc9563_gpio_driver = {
|
||||
.name = TC9563_GPIO_DEV_NAME,
|
||||
.probe = tc9563_gpio_probe,
|
||||
.id_table = tc9563_gpio_ids,
|
||||
};
|
||||
module_auxiliary_driver(tc9563_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Alex Elder <elder@riscstar.com>");
|
||||
MODULE_AUTHOR("Daniel Thompson <daniel@riscstar.com>");
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>");
|
||||
MODULE_DESCRIPTION("Toshiba TC9563 GPIO Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -210,24 +210,21 @@ static const char *cdns_pcie_hpa_ltssm_status_string(enum cdns_pcie_hpa_ltssm lt
|
|||
static int ltssm_status_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct cdns_pcie *pci = s->private;
|
||||
enum cdns_pcie_lga_ltssm lga_ltssm;
|
||||
enum cdns_pcie_hpa_ltssm hpa_ltssm;
|
||||
const char *str_ltssm;
|
||||
u32 val;
|
||||
u32 val, ltssm_val;
|
||||
|
||||
if (pci->is_hpa) {
|
||||
val = cdns_pcie_hpa_readl(pci, REG_BANK_IP_REG,
|
||||
CDNS_PCIE_HPA_PHY_DBG_STS_REG0);
|
||||
hpa_ltssm = FIELD_GET(CDNS_PCIE_HPA_LTSSM_STATUS_MASK, val);
|
||||
str_ltssm = cdns_pcie_hpa_ltssm_status_string(hpa_ltssm);
|
||||
ltssm_val = FIELD_GET(CDNS_PCIE_HPA_LTSSM_STATUS_MASK, val);
|
||||
str_ltssm = cdns_pcie_hpa_ltssm_status_string(ltssm_val);
|
||||
} else {
|
||||
val = cdns_pcie_readl(pci, CDNS_PCIE_LM_BASE);
|
||||
lga_ltssm = FIELD_GET(CDNS_PCIE_LGA_LTSSM_STATUS_MASK, val);
|
||||
str_ltssm = cdns_pcie_lga_ltssm_status_string(lga_ltssm);
|
||||
ltssm_val = FIELD_GET(CDNS_PCIE_LGA_LTSSM_STATUS_MASK, val);
|
||||
str_ltssm = cdns_pcie_lga_ltssm_status_string(ltssm_val);
|
||||
}
|
||||
|
||||
seq_printf(s, "%s (0x%02x)\n", str_ltssm,
|
||||
pci->is_hpa ? hpa_ltssm : lga_ltssm);
|
||||
seq_printf(s, "%s (0x%02x)\n", str_ltssm, ltssm_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,6 @@
|
|||
|
||||
#define CDNS_PLAT_CPU_TO_BUS_ADDR 0x0FFFFFFF
|
||||
|
||||
/**
|
||||
* struct cdns_plat_pcie - private data for this PCIe platform driver
|
||||
* @pcie: Cadence PCIe controller
|
||||
*/
|
||||
struct cdns_plat_pcie {
|
||||
struct cdns_pcie *pcie;
|
||||
};
|
||||
|
||||
static const struct of_device_id cdns_plat_pcie_of_match[];
|
||||
|
||||
static u64 cdns_plat_cpu_addr_fixup(struct cdns_pcie *pcie, u64 cpu_addr)
|
||||
|
|
@ -36,12 +28,11 @@ static const struct cdns_pcie_ops cdns_plat_ops = {
|
|||
static int cdns_plat_pcie_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct cdns_plat_pcie_of_data *data;
|
||||
struct cdns_plat_pcie *cdns_plat_pcie;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pci_host_bridge *bridge;
|
||||
struct cdns_pcie *pcie;
|
||||
struct cdns_pcie_ep *ep;
|
||||
struct cdns_pcie_rc *rc;
|
||||
int phy_count;
|
||||
bool is_rc;
|
||||
int ret;
|
||||
|
||||
|
|
@ -52,11 +43,6 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
|
|||
is_rc = data->is_rc;
|
||||
|
||||
pr_debug(" Started %s with is_rc: %d\n", __func__, is_rc);
|
||||
cdns_plat_pcie = devm_kzalloc(dev, sizeof(*cdns_plat_pcie), GFP_KERNEL);
|
||||
if (!cdns_plat_pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, cdns_plat_pcie);
|
||||
if (is_rc) {
|
||||
if (!IS_ENABLED(CONFIG_PCIE_CADENCE_PLAT_HOST))
|
||||
return -ENODEV;
|
||||
|
|
@ -68,9 +54,9 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
|
|||
rc = pci_host_bridge_priv(bridge);
|
||||
rc->pcie.dev = dev;
|
||||
rc->pcie.ops = &cdns_plat_ops;
|
||||
cdns_plat_pcie->pcie = &rc->pcie;
|
||||
pcie = &rc->pcie;
|
||||
|
||||
ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
|
||||
ret = cdns_pcie_init_phy(dev, pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to init phy\n");
|
||||
return ret;
|
||||
|
|
@ -95,9 +81,9 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
|
|||
|
||||
ep->pcie.dev = dev;
|
||||
ep->pcie.ops = &cdns_plat_ops;
|
||||
cdns_plat_pcie->pcie = &ep->pcie;
|
||||
pcie = &ep->pcie;
|
||||
|
||||
ret = cdns_pcie_init_phy(dev, cdns_plat_pcie->pcie);
|
||||
ret = cdns_pcie_init_phy(dev, pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to init phy\n");
|
||||
return ret;
|
||||
|
|
@ -115,18 +101,17 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev)
|
|||
goto err_init;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, pcie);
|
||||
|
||||
return 0;
|
||||
|
||||
err_init:
|
||||
err_get_sync:
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
cdns_pcie_disable_phy(cdns_plat_pcie->pcie);
|
||||
phy_count = cdns_plat_pcie->pcie->phy_count;
|
||||
while (phy_count--)
|
||||
device_link_del(cdns_plat_pcie->pcie->link[phy_count]);
|
||||
cdns_pcie_disable_phy(pcie);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cdns_plat_pcie_shutdown(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -222,7 +222,6 @@ int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie)
|
|||
struct device_node *np = dev->of_node;
|
||||
int phy_count;
|
||||
struct phy **phy;
|
||||
struct device_link **link;
|
||||
int i;
|
||||
int ret;
|
||||
const char *name;
|
||||
|
|
@ -238,10 +237,6 @@ int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie)
|
|||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
link = devm_kcalloc(dev, phy_count, sizeof(*link), GFP_KERNEL);
|
||||
if (!link)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < phy_count; i++) {
|
||||
of_property_read_string_index(np, "phy-names", i, &name);
|
||||
phy[i] = devm_phy_get(dev, name);
|
||||
|
|
@ -249,17 +244,10 @@ int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie)
|
|||
ret = PTR_ERR(phy[i]);
|
||||
goto err_phy;
|
||||
}
|
||||
link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS);
|
||||
if (!link[i]) {
|
||||
devm_phy_put(dev, phy[i]);
|
||||
ret = -EINVAL;
|
||||
goto err_phy;
|
||||
}
|
||||
}
|
||||
|
||||
pcie->phy_count = phy_count;
|
||||
pcie->phy = phy;
|
||||
pcie->link = link;
|
||||
|
||||
ret = cdns_pcie_enable_phy(pcie);
|
||||
if (ret)
|
||||
|
|
@ -268,10 +256,8 @@ int cdns_pcie_init_phy(struct device *dev, struct cdns_pcie *pcie)
|
|||
return 0;
|
||||
|
||||
err_phy:
|
||||
while (--i >= 0) {
|
||||
device_link_del(link[i]);
|
||||
while (--i >= 0)
|
||||
devm_phy_put(dev, phy[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,7 +260,6 @@ struct cdns_plat_pcie_of_data {
|
|||
* @is_hpa: indicates if the architecture is HPA
|
||||
* @phy_count: number of supported PHY devices
|
||||
* @phy: list of pointers to specific PHY control blocks
|
||||
* @link: list of pointers to corresponding device link representations
|
||||
* @ops: Platform-specific ops to control various inputs from Cadence PCIe
|
||||
* wrapper
|
||||
* @cdns_pcie_reg_offsets: Register bank offsets for different SoC
|
||||
|
|
@ -276,7 +275,6 @@ struct cdns_pcie {
|
|||
bool is_hpa;
|
||||
int phy_count;
|
||||
struct phy **phy;
|
||||
struct device_link **link;
|
||||
const struct cdns_pcie_ops *ops;
|
||||
const struct cdns_plat_pcie_of_data *cdns_pcie_reg_offsets;
|
||||
int max_link_speed;
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ config PCIE_DW_PLAT_HOST
|
|||
Designware IP.
|
||||
This controller can work either as EP or RC. In order to enable
|
||||
host-specific features PCIE_DW_PLAT_HOST must be selected and in
|
||||
order to enable device-specific features PCI_DW_PLAT_EP must be
|
||||
order to enable device-specific features PCIE_DW_PLAT_EP must be
|
||||
selected.
|
||||
|
||||
config PCIE_DW_PLAT_EP
|
||||
|
|
@ -310,7 +310,7 @@ config PCIE_DW_PLAT_EP
|
|||
in Designware IP.
|
||||
This controller can work either as EP or RC. In order to enable
|
||||
host-specific features PCIE_DW_PLAT_HOST must be selected and in
|
||||
order to enable device-specific features PCI_DW_PLAT_EP must be
|
||||
order to enable device-specific features PCIE_DW_PLAT_EP must be
|
||||
selected.
|
||||
|
||||
config PCIE_QCOM_COMMON
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
|
|
@ -214,7 +213,7 @@ static int dra7xx_pcie_handle_msi(struct dw_pcie_rp *pp, int index)
|
|||
|
||||
pos = find_first_bit(&val, MAX_MSI_IRQS_PER_CTRL);
|
||||
while (pos != MAX_MSI_IRQS_PER_CTRL) {
|
||||
generic_handle_domain_irq(pp->irq_domain,
|
||||
generic_handle_domain_irq(pp->msi_irq_domain,
|
||||
(index * MAX_MSI_IRQS_PER_CTRL) + pos);
|
||||
pos++;
|
||||
pos = find_next_bit(&val, MAX_MSI_IRQS_PER_CTRL, pos);
|
||||
|
|
@ -680,7 +679,6 @@ static int dra7xx_pcie_probe(struct platform_device *pdev)
|
|||
int i;
|
||||
int phy_count;
|
||||
struct phy **phy;
|
||||
struct device_link **link;
|
||||
void __iomem *base;
|
||||
struct dw_pcie *pci;
|
||||
struct dra7xx_pcie *dra7xx;
|
||||
|
|
@ -728,30 +726,16 @@ static int dra7xx_pcie_probe(struct platform_device *pdev)
|
|||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
link = devm_kcalloc(dev, phy_count, sizeof(*link), GFP_KERNEL);
|
||||
if (!link)
|
||||
return -ENOMEM;
|
||||
|
||||
dra7xx->clk = devm_clk_get_optional(dev, NULL);
|
||||
dra7xx->clk = devm_clk_get_optional_enabled(dev, NULL);
|
||||
if (IS_ERR(dra7xx->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(dra7xx->clk),
|
||||
"clock request failed");
|
||||
|
||||
ret = clk_prepare_enable(dra7xx->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < phy_count; i++) {
|
||||
snprintf(name, sizeof(name), "pcie-phy%d", i);
|
||||
phy[i] = devm_phy_get(dev, name);
|
||||
if (IS_ERR(phy[i]))
|
||||
return PTR_ERR(phy[i]);
|
||||
|
||||
link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS);
|
||||
if (!link[i]) {
|
||||
ret = -EINVAL;
|
||||
goto err_link;
|
||||
}
|
||||
}
|
||||
|
||||
dra7xx->base = base;
|
||||
|
|
@ -853,10 +837,6 @@ static int dra7xx_pcie_probe(struct platform_device *pdev)
|
|||
pm_runtime_disable(dev);
|
||||
dra7xx_pcie_disable_phy(dra7xx);
|
||||
|
||||
err_link:
|
||||
while (--i >= 0)
|
||||
device_link_del(link[i]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,6 +80,18 @@
|
|||
#define IMX95_SID_MASK GENMASK(5, 0)
|
||||
#define IMX95_MAX_LUT 32
|
||||
|
||||
#define IMX95_PCIE_PHY_REG_ADDR 0x3008
|
||||
#define IMX95_PCIE_PHY_REG_EN BIT(31)
|
||||
#define IMX95_PCIE_PHY_REG_ADDR_MASK GENMASK(15, 0)
|
||||
|
||||
#define IMX95_PCIE_PHY_REG_DATA 0x300c
|
||||
|
||||
#define IMX95_PCIE_PHY_MPLLB_OVRD_IN 0x2004
|
||||
#define IMX95_PCIE_PHY_MPLLB_OVRD_BW_EN BIT(10)
|
||||
|
||||
#define IMX95_PCIE_PHY_MPLLB_BW_IN 0x2005
|
||||
#define IMX95_PCIE_PHY_MPLLB_BW_VAL 0x8c
|
||||
|
||||
#define IMX95_PCIE_RST_CTRL 0x3010
|
||||
#define IMX95_PCIE_COLD_RST BIT(0)
|
||||
|
||||
|
|
@ -180,8 +192,12 @@ struct imx_pcie {
|
|||
struct imx_lut_data luts[IMX95_MAX_LUT];
|
||||
/* power domain for pcie */
|
||||
struct device *pd_pcie;
|
||||
/* device link for pcie power domain */
|
||||
struct device_link *pd_link;
|
||||
/* power domain for pcie phy */
|
||||
struct device *pd_pcie_phy;
|
||||
/* device link for pcie phy power domain */
|
||||
struct device_link *pd_phy_link;
|
||||
struct phy *phy;
|
||||
const struct imx_pcie_drvdata *drvdata;
|
||||
|
||||
|
|
@ -270,8 +286,19 @@ static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void imx95_pcie_phy_write(struct imx_pcie *imx_pcie, int addr, u16 data)
|
||||
{
|
||||
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_REG_ADDR,
|
||||
IMX95_PCIE_PHY_REG_EN, IMX95_PCIE_PHY_REG_EN);
|
||||
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_REG_ADDR,
|
||||
IMX95_PCIE_PHY_REG_ADDR_MASK, addr);
|
||||
regmap_write(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_REG_DATA, data);
|
||||
}
|
||||
|
||||
static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* ERR051624: The Controller Without Vaux Cannot Exit L23 Ready
|
||||
* Through Beacon or PERST# De-assertion
|
||||
|
|
@ -290,6 +317,21 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
|
|||
IMX95_PCIE_PHY_CR_PARA_SEL,
|
||||
IMX95_PCIE_PHY_CR_PARA_SEL);
|
||||
|
||||
/* Flush the IMX95_PCIE_PHY_CR_PARA_SEL update */
|
||||
regmap_read(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0, &val);
|
||||
|
||||
/*
|
||||
* A delay is required between the assertion of
|
||||
* IMX95_PCIE_PHY_CR_PARA_SEL and subsequent PHY register write
|
||||
* operation to ensure values are successfully written.
|
||||
*/
|
||||
udelay(200);
|
||||
|
||||
imx95_pcie_phy_write(imx_pcie, IMX95_PCIE_PHY_MPLLB_BW_IN,
|
||||
IMX95_PCIE_PHY_MPLLB_BW_VAL);
|
||||
imx95_pcie_phy_write(imx_pcie, IMX95_PCIE_PHY_MPLLB_OVRD_IN,
|
||||
IMX95_PCIE_PHY_MPLLB_OVRD_BW_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -542,7 +584,7 @@ static int imx_setup_phy_mpll(struct imx_pcie *imx_pcie)
|
|||
return 0;
|
||||
|
||||
for (i = 0; i < imx_pcie->num_clks; i++)
|
||||
if (strncmp(clks[i].id, "pcie_phy", 8) == 0)
|
||||
if (clks[i].id && strncmp(clks[i].id, "pcie_phy", 8) == 0)
|
||||
phy_rate = clk_get_rate(clks[i].clk);
|
||||
|
||||
switch (phy_rate) {
|
||||
|
|
@ -639,10 +681,34 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
|
|||
}
|
||||
#endif
|
||||
|
||||
static void imx_pcie_detach_pd_action(void *data)
|
||||
{
|
||||
struct imx_pcie *imx_pcie = data;
|
||||
|
||||
if (!IS_ERR_OR_NULL(imx_pcie->pd_phy_link)) {
|
||||
device_link_del(imx_pcie->pd_phy_link);
|
||||
imx_pcie->pd_phy_link = NULL;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(imx_pcie->pd_link)) {
|
||||
device_link_del(imx_pcie->pd_link);
|
||||
imx_pcie->pd_link = NULL;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(imx_pcie->pd_pcie_phy)) {
|
||||
dev_pm_domain_detach(imx_pcie->pd_pcie_phy, true);
|
||||
imx_pcie->pd_pcie_phy = NULL;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(imx_pcie->pd_pcie)) {
|
||||
dev_pm_domain_detach(imx_pcie->pd_pcie, true);
|
||||
imx_pcie->pd_pcie = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int imx_pcie_attach_pd(struct device *dev)
|
||||
{
|
||||
struct imx_pcie *imx_pcie = dev_get_drvdata(dev);
|
||||
struct device_link *link;
|
||||
|
||||
/* Do nothing when in a single power domain */
|
||||
if (dev->pm_domain)
|
||||
|
|
@ -654,11 +720,11 @@ static int imx_pcie_attach_pd(struct device *dev)
|
|||
/* Do nothing when power domain missing */
|
||||
if (!imx_pcie->pd_pcie)
|
||||
return 0;
|
||||
link = device_link_add(dev, imx_pcie->pd_pcie,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (!link) {
|
||||
imx_pcie->pd_link = device_link_add(dev, imx_pcie->pd_pcie,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (!imx_pcie->pd_link) {
|
||||
dev_err(dev, "Failed to add device_link to pcie pd\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -667,11 +733,11 @@ static int imx_pcie_attach_pd(struct device *dev)
|
|||
if (IS_ERR(imx_pcie->pd_pcie_phy))
|
||||
return PTR_ERR(imx_pcie->pd_pcie_phy);
|
||||
|
||||
link = device_link_add(dev, imx_pcie->pd_pcie_phy,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (!link) {
|
||||
imx_pcie->pd_phy_link = device_link_add(dev, imx_pcie->pd_pcie_phy,
|
||||
DL_FLAG_STATELESS |
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_RPM_ACTIVE);
|
||||
if (!imx_pcie->pd_phy_link) {
|
||||
dev_err(dev, "Failed to add device_link to pcie_phy pd\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -681,12 +747,8 @@ static int imx_pcie_attach_pd(struct device *dev)
|
|||
|
||||
static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_REF_CLK_EN);
|
||||
else
|
||||
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_REF_CLK_EN);
|
||||
regmap_assign_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_REF_CLK_EN, enable);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -802,12 +864,8 @@ static void imx_pcie_clk_disable(struct imx_pcie *imx_pcie)
|
|||
|
||||
static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
||||
{
|
||||
if (assert)
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||
IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
|
||||
else
|
||||
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||
IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
|
||||
regmap_assign_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
|
||||
IMX6SX_GPR12_PCIE_TEST_POWERDOWN, assert);
|
||||
|
||||
/* Force PCIe PHY reset */
|
||||
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET,
|
||||
|
|
@ -817,12 +875,8 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
|||
|
||||
static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
||||
{
|
||||
if (assert)
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
else
|
||||
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
regmap_assign_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD, assert);
|
||||
|
||||
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_SW_RST,
|
||||
assert ? IMX6Q_GPR1_PCIE_SW_RST : 0);
|
||||
|
|
@ -835,12 +889,8 @@ static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
|||
|
||||
static int imx6q_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
|
||||
{
|
||||
if (assert)
|
||||
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
else
|
||||
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD);
|
||||
regmap_assign_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1,
|
||||
IMX6Q_GPR1_PCIE_TEST_PD, assert);
|
||||
|
||||
if (!assert)
|
||||
usleep_range(200, 500);
|
||||
|
|
@ -955,9 +1005,9 @@ static int imx_pcie_wait_for_speed_change(struct imx_pcie *imx_pcie)
|
|||
unsigned int retries;
|
||||
|
||||
for (retries = 0; retries < 200; retries++) {
|
||||
tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
tmp = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
|
||||
/* Test if the speed change finished. */
|
||||
if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
|
||||
if (!(tmp & SPEED_CHANGE))
|
||||
return 0;
|
||||
usleep_range(100, 1000);
|
||||
}
|
||||
|
|
@ -1040,9 +1090,9 @@ static int imx_pcie_start_link(struct dw_pcie *pci)
|
|||
* Start Directed Speed Change so the best possible
|
||||
* speed both link partners support can be negotiated.
|
||||
*/
|
||||
tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
tmp |= PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
|
||||
tmp = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
|
||||
tmp |= SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, tmp);
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
|
||||
ret = imx_pcie_wait_for_speed_change(imx_pcie);
|
||||
|
|
@ -1058,8 +1108,8 @@ static int imx_pcie_start_link(struct dw_pcie *pci)
|
|||
|
||||
err_reset_phy:
|
||||
dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
|
||||
dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0),
|
||||
dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG1));
|
||||
dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG0),
|
||||
dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG1));
|
||||
imx_pcie_reset_phy(imx_pcie);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1389,7 +1439,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
if (!pp->skip_pwrctrl_off) {
|
||||
ret = pci_pwrctrl_power_on_devices(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to power on pwrctrl devices\n");
|
||||
dev_err_probe(dev, ret, "failed to power on pwrctrl devices\n");
|
||||
goto err_reg_disable;
|
||||
}
|
||||
}
|
||||
|
|
@ -1498,18 +1548,18 @@ static void imx_pcie_host_post_init(struct dw_pcie_rp *pp)
|
|||
/*
|
||||
* ERR051586: Compliance with 8GT/s Receiver Impedance ECN
|
||||
*
|
||||
* The default value of GEN3_RELATED_OFF[GEN3_ZRXDC_NONCOMPL]
|
||||
* The default value of GEN3_CTRL[GEN3_ZRXDC_NONCOMPL]
|
||||
* is 1 which makes receiver non-compliant with the ZRX-DC
|
||||
* parameter for 2.5 GT/s when operating at 8 GT/s or higher.
|
||||
* It causes unnecessary timeout in L1.
|
||||
*
|
||||
* Workaround: Program GEN3_RELATED_OFF[GEN3_ZRXDC_NONCOMPL]
|
||||
* Workaround: Program GEN3_CTRL[GEN3_ZRXDC_NONCOMPL]
|
||||
* to 0.
|
||||
*/
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
|
||||
val &= ~GEN3_CTRL_GEN3_ZRXDC_NONCOMPL;
|
||||
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
}
|
||||
|
||||
|
|
@ -1521,7 +1571,7 @@ static void imx_pcie_host_post_init(struct dw_pcie_rp *pp)
|
|||
}
|
||||
|
||||
/*
|
||||
* In old DWC implementations, PCIE_ATU_INHIBIT_PAYLOAD in iATU Ctrl2
|
||||
* In old DWC implementations, ATU_INHIBIT_PAYLOAD in iATU Ctrl2
|
||||
* register is reserved, so the generic DWC implementation of sending the
|
||||
* PME_Turn_Off message using a dummy MMIO write cannot be used.
|
||||
*/
|
||||
|
|
@ -1836,7 +1886,8 @@ static int imx_pcie_probe(struct platform_device *pdev)
|
|||
return dev_err_probe(dev, imx_pcie->num_clks,
|
||||
"failed to get clocks\n");
|
||||
for (i = 0; i < imx_pcie->num_clks; i++)
|
||||
if (strncmp(imx_pcie->clks[i].id, "extref", 6) == 0)
|
||||
if (imx_pcie->clks[i].id &&
|
||||
strncmp(imx_pcie->clks[i].id, "extref", 6) == 0)
|
||||
imx_pcie->enable_ext_refclk = true;
|
||||
|
||||
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_PHYDRV)) {
|
||||
|
|
@ -1951,6 +2002,10 @@ static int imx_pcie_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, imx_pcie);
|
||||
|
||||
ret = devm_add_action_or_reset(dev, imx_pcie_detach_pd_action, imx_pcie);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = imx_pcie_attach_pd(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -1975,9 +2030,11 @@ static int imx_pcie_probe(struct platform_device *pdev)
|
|||
pm_runtime_no_callbacks(dev);
|
||||
ret = devm_pm_runtime_set_active_enabled(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto err_pwrctrl_destroy;
|
||||
}
|
||||
|
||||
pci->pp.suspend_poweroff = true;
|
||||
|
||||
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_SKIP_L23_READY))
|
||||
pci->pp.skip_l23_ready = true;
|
||||
if (imx_check_flag(imx_pcie, IMX_PCIE_FLAG_KEEP_MSI_CAP))
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/of_pci.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/resource.h>
|
||||
#include <linux/signal.h>
|
||||
|
|
@ -129,7 +130,6 @@ struct keystone_pcie {
|
|||
int num_lanes;
|
||||
u32 num_viewport;
|
||||
struct phy **phy;
|
||||
struct device_link **link;
|
||||
struct device_node *msi_intc_np;
|
||||
struct irq_domain *intx_irq_domain;
|
||||
struct device_node *np;
|
||||
|
|
@ -309,7 +309,7 @@ static int ks_pcie_msi_host_init(struct dw_pcie_rp *pp)
|
|||
dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, ks_pcie->app.start);
|
||||
|
||||
pp->msi_irq_chip = &ks_pcie_msi_irq_chip;
|
||||
return dw_pcie_allocate_domains(pp);
|
||||
return dw_pcie_allocate_msi_domains(pp);
|
||||
}
|
||||
|
||||
static void ks_pcie_handle_intx_irq(struct keystone_pcie *ks_pcie,
|
||||
|
|
@ -497,8 +497,8 @@ static bool ks_pcie_link_up(struct dw_pcie *pci)
|
|||
{
|
||||
u32 val;
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0);
|
||||
return (val & PORT_LOGIC_LTSSM_STATE_MASK) == PORT_LOGIC_LTSSM_STATE_L0;
|
||||
val = dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG0);
|
||||
return (val & PORT_LINK_DEBUG0_LTSSM_MASK) == DW_PCIE_LTSSM_L0;
|
||||
}
|
||||
|
||||
static void ks_pcie_stop_link(struct dw_pcie *pci)
|
||||
|
|
@ -633,7 +633,7 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc)
|
|||
|
||||
vector = offset + (pos << 3);
|
||||
dev_dbg(dev, "irq: bit %d, vector %d\n", pos, vector);
|
||||
generic_handle_domain_irq(pp->irq_domain, vector);
|
||||
generic_handle_domain_irq(pp->msi_irq_domain, vector);
|
||||
}
|
||||
|
||||
chained_irq_exit(chip, desc);
|
||||
|
|
@ -1131,7 +1131,6 @@ static int ks_pcie_probe(struct platform_device *pdev)
|
|||
enum dw_pcie_device_mode mode;
|
||||
struct dw_pcie *pci;
|
||||
struct keystone_pcie *ks_pcie;
|
||||
struct device_link **link;
|
||||
struct gpio_desc *gpiod;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
|
|
@ -1202,31 +1201,17 @@ static int ks_pcie_probe(struct platform_device *pdev)
|
|||
if (!phy)
|
||||
return -ENOMEM;
|
||||
|
||||
link = devm_kcalloc(dev, num_lanes, sizeof(*link), GFP_KERNEL);
|
||||
if (!link)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < num_lanes; i++) {
|
||||
snprintf(name, sizeof(name), "pcie-phy%d", i);
|
||||
phy[i] = devm_phy_optional_get(dev, name);
|
||||
if (IS_ERR(phy[i])) {
|
||||
ret = PTR_ERR(phy[i]);
|
||||
goto err_link;
|
||||
}
|
||||
|
||||
if (!phy[i])
|
||||
continue;
|
||||
|
||||
link[i] = device_link_add(dev, &phy[i]->dev, DL_FLAG_STATELESS);
|
||||
if (!link[i]) {
|
||||
ret = -EINVAL;
|
||||
goto err_link;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
ks_pcie->np = np;
|
||||
ks_pcie->pci = pci;
|
||||
ks_pcie->link = link;
|
||||
ks_pcie->num_lanes = num_lanes;
|
||||
ks_pcie->phy = phy;
|
||||
|
||||
|
|
@ -1236,7 +1221,7 @@ static int ks_pcie_probe(struct platform_device *pdev)
|
|||
ret = PTR_ERR(gpiod);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get reset GPIO\n");
|
||||
goto err_link;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Obtain references to the PHYs */
|
||||
|
|
@ -1251,7 +1236,7 @@ static int ks_pcie_probe(struct platform_device *pdev)
|
|||
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable phy\n");
|
||||
goto err_link;
|
||||
goto err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ks_pcie);
|
||||
|
|
@ -1338,25 +1323,18 @@ static int ks_pcie_probe(struct platform_device *pdev)
|
|||
pm_runtime_disable(dev);
|
||||
ks_pcie_disable_phy(ks_pcie);
|
||||
|
||||
err_link:
|
||||
while (--i >= 0 && link[i])
|
||||
device_link_del(link[i]);
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ks_pcie_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct keystone_pcie *ks_pcie = platform_get_drvdata(pdev);
|
||||
struct device_link **link = ks_pcie->link;
|
||||
int num_lanes = ks_pcie->num_lanes;
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
pm_runtime_put(dev);
|
||||
pm_runtime_disable(dev);
|
||||
ks_pcie_disable_phy(ks_pcie);
|
||||
while (num_lanes--)
|
||||
device_link_del(link[num_lanes]);
|
||||
}
|
||||
|
||||
static struct platform_driver ks_pcie_driver = {
|
||||
|
|
|
|||
|
|
@ -419,6 +419,7 @@ static struct platform_driver ls_pcie_driver = {
|
|||
.of_match_table = ls_pcie_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
.pm = &ls_pcie_pm_ops,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ls_pcie_driver);
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ static void dw_pcie_ep_clear_ib_maps(struct dw_pcie_ep *ep, u8 func_no, enum pci
|
|||
/* Tear down the BAR Match Mode mapping, if any. */
|
||||
if (ep_func->bar_to_atu[bar]) {
|
||||
atu_index = ep_func->bar_to_atu[bar] - 1;
|
||||
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, atu_index);
|
||||
dw_pcie_disable_atu(pci, ATU_REGION_DIR_IB, atu_index);
|
||||
clear_bit(atu_index, ep->ib_window_map);
|
||||
ep_func->bar_to_atu[bar] = 0;
|
||||
return;
|
||||
|
|
@ -177,7 +177,7 @@ static void dw_pcie_ep_clear_ib_maps(struct dw_pcie_ep *ep, u8 func_no, enum pci
|
|||
if (!indexes)
|
||||
return;
|
||||
for (i = 0; i < num; i++) {
|
||||
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, indexes[i]);
|
||||
dw_pcie_disable_atu(pci, ATU_REGION_DIR_IB, indexes[i]);
|
||||
clear_bit(indexes[i], ep->ib_window_map);
|
||||
}
|
||||
devm_kfree(dev, indexes);
|
||||
|
|
@ -647,7 +647,7 @@ static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
|
|||
return;
|
||||
|
||||
ep->outbound_addr[atu_index] = 0;
|
||||
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, atu_index);
|
||||
dw_pcie_disable_atu(pci, ATU_REGION_DIR_OB, atu_index);
|
||||
clear_bit(atu_index, ep->ob_window_map);
|
||||
}
|
||||
|
||||
|
|
@ -1077,10 +1077,10 @@ int dw_pcie_ep_raise_msix_irq_doorbell(struct dw_pcie_ep *ep, u8 func_no,
|
|||
if (!ep_func || !ep_func->msix_cap)
|
||||
return -EINVAL;
|
||||
|
||||
msg_data = (func_no << PCIE_MSIX_DOORBELL_PF_SHIFT) |
|
||||
msg_data = FIELD_PREP(MSIX_DOORBELL_PF_MASK, func_no) |
|
||||
(interrupt_num - 1);
|
||||
|
||||
dw_pcie_writel_dbi(pci, PCIE_MSIX_DOORBELL, msg_data);
|
||||
dw_pcie_writel_dbi(pci, MSIX_DOORBELL, msg_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1194,6 +1194,9 @@ void dw_pcie_ep_deinit(struct dw_pcie_ep *ep)
|
|||
epc->mem->window.page_size);
|
||||
|
||||
pci_epc_mem_exit(epc);
|
||||
|
||||
if (ep->ops->post_deinit)
|
||||
ep->ops->post_deinit(ep);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_ep_deinit);
|
||||
|
||||
|
|
@ -1553,7 +1556,7 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
|
|||
ep->page_size);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to initialize address space\n");
|
||||
return ret;
|
||||
goto err_deinit;
|
||||
}
|
||||
|
||||
ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
|
||||
|
|
@ -1568,6 +1571,9 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
|
|||
|
||||
err_exit_epc_mem:
|
||||
pci_epc_mem_exit(epc);
|
||||
err_deinit:
|
||||
if (ep->ops->post_deinit)
|
||||
ep->ops->post_deinit(ep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ void dw_handle_msi_irq(struct dw_pcie_rp *pp)
|
|||
continue;
|
||||
|
||||
for_each_set_bit(pos, &status, MAX_MSI_IRQS_PER_CTRL)
|
||||
generic_handle_demux_domain_irq(pp->irq_domain, irq_off + pos);
|
||||
generic_handle_demux_domain_irq(pp->msi_irq_domain, irq_off + pos);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ static const struct irq_domain_ops dw_pcie_msi_domain_ops = {
|
|||
.free = dw_pcie_irq_domain_free,
|
||||
};
|
||||
|
||||
int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)
|
||||
int dw_pcie_allocate_msi_domains(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
struct irq_domain_info info = {
|
||||
|
|
@ -217,15 +217,15 @@ int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)
|
|||
.host_data = pp,
|
||||
};
|
||||
|
||||
pp->irq_domain = msi_create_parent_irq_domain(&info, &dw_pcie_msi_parent_ops);
|
||||
if (!pp->irq_domain) {
|
||||
pp->msi_irq_domain = msi_create_parent_irq_domain(&info, &dw_pcie_msi_parent_ops);
|
||||
if (!pp->msi_irq_domain) {
|
||||
dev_err(pci->dev, "Failed to create IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_allocate_domains);
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_allocate_msi_domains);
|
||||
|
||||
void dw_pcie_free_msi(struct dw_pcie_rp *pp)
|
||||
{
|
||||
|
|
@ -236,7 +236,7 @@ void dw_pcie_free_msi(struct dw_pcie_rp *pp)
|
|||
irq_set_chained_handler_and_data(pp->msi_irq[ctrl], NULL, NULL);
|
||||
}
|
||||
|
||||
irq_domain_remove(pp->irq_domain);
|
||||
irq_domain_remove(pp->msi_irq_domain);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_free_msi);
|
||||
|
||||
|
|
@ -342,7 +342,7 @@ int dw_pcie_msi_host_init(struct dw_pcie_rp *pp)
|
|||
|
||||
pp->msi_irq_chip = &dw_pci_msi_bottom_irq_chip;
|
||||
|
||||
ret = dw_pcie_allocate_domains(pp);
|
||||
ret = dw_pcie_allocate_msi_domains(pp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -440,7 +440,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
|
|||
atu.parent_bus_addr = pp->cfg0_base + SZ_1M;
|
||||
/* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */
|
||||
atu.size = SZ_1M;
|
||||
atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;
|
||||
atu.ctrl2 = ATU_CFG_SHIFT_MODE_ENABLE;
|
||||
ret = dw_pcie_prog_outbound_atu(pci, &atu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -455,7 +455,7 @@ static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp)
|
|||
atu.type = PCIE_TLP_TYPE_CFG1_RDWR;
|
||||
atu.parent_bus_addr = pp->cfg0_base + SZ_2M;
|
||||
atu.size = (SZ_1M * bus_range_max) - SZ_2M;
|
||||
atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE;
|
||||
atu.ctrl2 = ATU_CFG_SHIFT_MODE_ENABLE;
|
||||
|
||||
return dw_pcie_prog_outbound_atu(pci, &atu);
|
||||
}
|
||||
|
|
@ -747,8 +747,8 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus,
|
|||
if (!dw_pcie_link_up(pci))
|
||||
return NULL;
|
||||
|
||||
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
|
||||
PCIE_ATU_FUNC(PCI_FUNC(devfn));
|
||||
busdev = ATU_BUS(bus->number) | ATU_DEV(PCI_SLOT(devfn)) |
|
||||
ATU_FUNC(PCI_FUNC(devfn));
|
||||
|
||||
if (pci_is_root_bus(bus->parent))
|
||||
type = PCIE_TLP_TYPE_CFG0_RDWR;
|
||||
|
|
@ -884,10 +884,10 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
|
|||
* the MEM/IO (dma-)ranges setups.
|
||||
*/
|
||||
for (i = 0; i < pci->num_ob_windows; i++)
|
||||
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
|
||||
dw_pcie_disable_atu(pci, ATU_REGION_DIR_OB, i);
|
||||
|
||||
for (i = 0; i < pci->num_ib_windows; i++)
|
||||
dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_IB, i);
|
||||
dw_pcie_disable_atu(pci, ATU_REGION_DIR_IB, i);
|
||||
|
||||
/*
|
||||
* NOTE: For outbound address translation, outbound iATU at index 0 is
|
||||
|
|
@ -918,6 +918,20 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
|
|||
atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset;
|
||||
atu.pci_addr = entry->res->start - entry->offset;
|
||||
|
||||
if (pp->bypass_ob_mem_iatu) {
|
||||
/*
|
||||
* The no-match pass-through path preserves the correct
|
||||
* PCI address only for identity-mapped MEM ranges.
|
||||
*/
|
||||
if (atu.parent_bus_addr != atu.pci_addr) {
|
||||
dev_err(pci->dev,
|
||||
"Cannot bypass outbound iATU for non-identity MEM range %pr\n",
|
||||
entry->res);
|
||||
return -EINVAL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Adjust iATU size if MSG TLP region was allocated before */
|
||||
if (pp->msg_res && pp->msg_res->parent == entry->res)
|
||||
res_size = resource_size(entry->res) -
|
||||
|
|
@ -964,8 +978,7 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
|
|||
|
||||
ret = dw_pcie_prog_outbound_atu(pci, &atu);
|
||||
if (ret) {
|
||||
dev_err(pci->dev, "Failed to set IO range %pr\n",
|
||||
entry->res);
|
||||
dev_err(pci->dev, "Failed to set IO range\n");
|
||||
return ret;
|
||||
}
|
||||
ob_iatu_index++;
|
||||
|
|
@ -1165,9 +1178,9 @@ int dw_pcie_setup_rc(struct dw_pcie_rp *pp)
|
|||
/* Program correct class for RC */
|
||||
dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
val |= PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
||||
val = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
|
||||
val |= SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, val);
|
||||
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
|
||||
|
|
@ -1224,14 +1237,21 @@ static int dw_pcie_pme_turn_off(struct dw_pcie *pci)
|
|||
|
||||
int dw_pcie_suspend_noirq(struct dw_pcie *pci)
|
||||
{
|
||||
bool pme_capable = false;
|
||||
bool d3cold, pme_capable = false;
|
||||
int ret = 0;
|
||||
u32 val;
|
||||
|
||||
if (!dw_pcie_link_up(pci))
|
||||
goto stop_link;
|
||||
|
||||
if (!pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable))
|
||||
d3cold = pci_host_common_d3cold_possible(pci->pp.bridge, &pme_capable);
|
||||
|
||||
/*
|
||||
* Enter L2 if D3cold is possible, or if the platform loses RC power
|
||||
* during suspend (in which case L2 entry is required to notify
|
||||
* endpoints before power loss, regardless of D3cold support).
|
||||
*/
|
||||
if (!d3cold && !pci->pp.suspend_poweroff)
|
||||
return 0;
|
||||
|
||||
if (pci->pp.ops->pme_turn_off) {
|
||||
|
|
|
|||
|
|
@ -436,9 +436,9 @@ static inline void __iomem *dw_pcie_select_atu(struct dw_pcie *pci, u32 dir,
|
|||
u32 index)
|
||||
{
|
||||
if (dw_pcie_cap_is(pci, IATU_UNROLL))
|
||||
return pci->atu_base + PCIE_ATU_UNROLL_BASE(dir, index);
|
||||
return pci->atu_base + ATU_UNROLL_BASE(dir, index);
|
||||
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, dir | index);
|
||||
dw_pcie_writel_dbi(pci, IATU_VIEWPORT, dir | index);
|
||||
return pci->atu_base;
|
||||
}
|
||||
|
||||
|
|
@ -480,13 +480,13 @@ static void dw_pcie_writel_atu(struct dw_pcie *pci, u32 dir, u32 index,
|
|||
|
||||
static inline u32 dw_pcie_readl_atu_ob(struct dw_pcie *pci, u32 index, u32 reg)
|
||||
{
|
||||
return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_OB, index, reg);
|
||||
return dw_pcie_readl_atu(pci, ATU_REGION_DIR_OB, index, reg);
|
||||
}
|
||||
|
||||
static inline void dw_pcie_writel_atu_ob(struct dw_pcie *pci, u32 index, u32 reg,
|
||||
u32 val)
|
||||
{
|
||||
dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_OB, index, reg, val);
|
||||
dw_pcie_writel_atu(pci, ATU_REGION_DIR_OB, index, reg, val);
|
||||
}
|
||||
|
||||
static inline u32 dw_pcie_enable_ecrc(u32 val)
|
||||
|
|
@ -527,7 +527,7 @@ static inline u32 dw_pcie_enable_ecrc(u32 val)
|
|||
* handle the scenario is to program TD bit by default.
|
||||
*/
|
||||
|
||||
return val | PCIE_ATU_TD;
|
||||
return val | ATU_TD;
|
||||
}
|
||||
|
||||
int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
|
||||
|
|
@ -548,44 +548,46 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_BASE,
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, ATU_LOWER_BASE,
|
||||
lower_32_bits(parent_bus_addr));
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_BASE,
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, ATU_UPPER_BASE,
|
||||
upper_32_bits(parent_bus_addr));
|
||||
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LIMIT,
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, ATU_LIMIT,
|
||||
lower_32_bits(limit_addr));
|
||||
if (dw_pcie_ver_is_ge(pci, 460A))
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_LIMIT,
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, ATU_UPPER_LIMIT,
|
||||
upper_32_bits(limit_addr));
|
||||
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_LOWER_TARGET,
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, ATU_LOWER_TARGET,
|
||||
lower_32_bits(atu->pci_addr));
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_UPPER_TARGET,
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, ATU_UPPER_TARGET,
|
||||
upper_32_bits(atu->pci_addr));
|
||||
|
||||
val = atu->type | atu->routing | PCIE_ATU_FUNC_NUM(atu->func_no);
|
||||
val = atu->type | atu->routing | ATU_FUNC_NUM(atu->func_no);
|
||||
if (upper_32_bits(limit_addr) > upper_32_bits(parent_bus_addr) &&
|
||||
dw_pcie_ver_is_ge(pci, 460A))
|
||||
val |= PCIE_ATU_INCREASE_REGION_SIZE;
|
||||
val |= ATU_INCREASE_REGION_SIZE;
|
||||
if (!dw_pcie_ver_is_ge(pci, 510A))
|
||||
val = dw_pcie_enable_ecrc(val);
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val);
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, ATU_REGION_CTRL1, val);
|
||||
|
||||
val = PCIE_ATU_ENABLE | atu->ctrl2;
|
||||
val = ATU_ENABLE | atu->ctrl2;
|
||||
if (atu->type == PCIE_TLP_TYPE_MSG) {
|
||||
/* The data-less messages only for now */
|
||||
val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code;
|
||||
val |= ATU_INHIBIT_PAYLOAD | atu->code;
|
||||
}
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL2, val);
|
||||
if (dw_pcie_ver_is_ge(pci, 460A))
|
||||
val |= ATU_DMA_BYPASS;
|
||||
dw_pcie_writel_atu_ob(pci, atu->index, ATU_REGION_CTRL2, val);
|
||||
|
||||
/*
|
||||
* Make sure ATU enable takes effect before any subsequent config
|
||||
* and I/O accesses.
|
||||
*/
|
||||
for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
|
||||
val = dw_pcie_readl_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL2);
|
||||
if (val & PCIE_ATU_ENABLE)
|
||||
val = dw_pcie_readl_atu_ob(pci, atu->index, ATU_REGION_CTRL2);
|
||||
if (val & ATU_ENABLE)
|
||||
return 0;
|
||||
|
||||
mdelay(LINK_WAIT_IATU);
|
||||
|
|
@ -598,13 +600,13 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci,
|
|||
|
||||
static inline u32 dw_pcie_readl_atu_ib(struct dw_pcie *pci, u32 index, u32 reg)
|
||||
{
|
||||
return dw_pcie_readl_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg);
|
||||
return dw_pcie_readl_atu(pci, ATU_REGION_DIR_IB, index, reg);
|
||||
}
|
||||
|
||||
static inline void dw_pcie_writel_atu_ib(struct dw_pcie *pci, u32 index, u32 reg,
|
||||
u32 val)
|
||||
{
|
||||
dw_pcie_writel_atu(pci, PCIE_ATU_REGION_DIR_IB, index, reg, val);
|
||||
dw_pcie_writel_atu(pci, ATU_REGION_DIR_IB, index, reg, val);
|
||||
}
|
||||
|
||||
int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
|
||||
|
|
@ -622,36 +624,36 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, int index, int type,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_BASE,
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_LOWER_BASE,
|
||||
lower_32_bits(pci_addr));
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_BASE,
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_UPPER_BASE,
|
||||
upper_32_bits(pci_addr));
|
||||
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LIMIT,
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_LIMIT,
|
||||
lower_32_bits(limit_addr));
|
||||
if (dw_pcie_ver_is_ge(pci, 460A))
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_LIMIT,
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_UPPER_LIMIT,
|
||||
upper_32_bits(limit_addr));
|
||||
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_LOWER_TARGET,
|
||||
lower_32_bits(parent_bus_addr));
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_UPPER_TARGET,
|
||||
upper_32_bits(parent_bus_addr));
|
||||
|
||||
val = type;
|
||||
if (upper_32_bits(limit_addr) > upper_32_bits(pci_addr) &&
|
||||
dw_pcie_ver_is_ge(pci, 460A))
|
||||
val |= PCIE_ATU_INCREASE_REGION_SIZE;
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, val);
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2, PCIE_ATU_ENABLE);
|
||||
val |= ATU_INCREASE_REGION_SIZE;
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_REGION_CTRL1, val);
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_REGION_CTRL2, ATU_ENABLE);
|
||||
|
||||
/*
|
||||
* Make sure ATU enable takes effect before any subsequent config
|
||||
* and I/O accesses.
|
||||
*/
|
||||
for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
|
||||
val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);
|
||||
if (val & PCIE_ATU_ENABLE)
|
||||
val = dw_pcie_readl_atu_ib(pci, index, ATU_REGION_CTRL2);
|
||||
if (val & ATU_ENABLE)
|
||||
return 0;
|
||||
|
||||
mdelay(LINK_WAIT_IATU);
|
||||
|
|
@ -671,24 +673,24 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
|||
!IS_ALIGNED(parent_bus_addr, size))
|
||||
return -EINVAL;
|
||||
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_LOWER_TARGET,
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_LOWER_TARGET,
|
||||
lower_32_bits(parent_bus_addr));
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_UPPER_TARGET,
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_UPPER_TARGET,
|
||||
upper_32_bits(parent_bus_addr));
|
||||
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL1, type |
|
||||
PCIE_ATU_FUNC_NUM(func_no));
|
||||
dw_pcie_writel_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2,
|
||||
PCIE_ATU_ENABLE | PCIE_ATU_FUNC_NUM_MATCH_EN |
|
||||
PCIE_ATU_BAR_MODE_ENABLE | (bar << 8));
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_REGION_CTRL1, type |
|
||||
ATU_FUNC_NUM(func_no));
|
||||
dw_pcie_writel_atu_ib(pci, index, ATU_REGION_CTRL2,
|
||||
ATU_ENABLE | ATU_FUNC_NUM_MATCH_EN |
|
||||
ATU_BAR_MODE_ENABLE | (bar << 8));
|
||||
|
||||
/*
|
||||
* Make sure ATU enable takes effect before any subsequent config
|
||||
* and I/O accesses.
|
||||
*/
|
||||
for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
|
||||
val = dw_pcie_readl_atu_ib(pci, index, PCIE_ATU_REGION_CTRL2);
|
||||
if (val & PCIE_ATU_ENABLE)
|
||||
val = dw_pcie_readl_atu_ib(pci, index, ATU_REGION_CTRL2);
|
||||
if (val & ATU_ENABLE)
|
||||
return 0;
|
||||
|
||||
mdelay(LINK_WAIT_IATU);
|
||||
|
|
@ -701,7 +703,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
|
|||
|
||||
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index)
|
||||
{
|
||||
dw_pcie_writel_atu(pci, dir, index, PCIE_ATU_REGION_CTRL2, 0);
|
||||
dw_pcie_writel_atu(pci, dir, index, ATU_REGION_CTRL2, 0);
|
||||
}
|
||||
|
||||
const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm)
|
||||
|
|
@ -824,9 +826,9 @@ bool dw_pcie_link_up(struct dw_pcie *pci)
|
|||
if (pci->ops && pci->ops->link_up)
|
||||
return pci->ops->link_up(pci);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG1);
|
||||
return ((val & PCIE_PORT_DEBUG1_LINK_UP) &&
|
||||
(!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING)));
|
||||
val = dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG1);
|
||||
return ((val & PORT_LINK_DEBUG1_LINK_UP) &&
|
||||
(!(val & PORT_LINK_DEBUG1_LINK_IN_TRAINING)));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_link_up);
|
||||
|
||||
|
|
@ -834,9 +836,9 @@ void dw_pcie_upconfig_setup(struct dw_pcie *pci)
|
|||
{
|
||||
u32 val;
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL);
|
||||
val |= PORT_MLTI_UPCFG_SUPPORT;
|
||||
dw_pcie_writel_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL, val);
|
||||
val = dw_pcie_readl_dbi(pci, MULTI_LANE_CTRL);
|
||||
val |= MULTI_LANE_CTRL_UPCFG_SUPPORT;
|
||||
dw_pcie_writel_dbi(pci, MULTI_LANE_CTRL, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_pcie_upconfig_setup);
|
||||
|
||||
|
|
@ -894,36 +896,36 @@ static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes)
|
|||
return;
|
||||
|
||||
/* Set the number of lanes */
|
||||
plc = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
|
||||
plc &= ~PORT_LINK_FAST_LINK_MODE;
|
||||
plc &= ~PORT_LINK_MODE_MASK;
|
||||
plc = dw_pcie_readl_dbi(pci, PORT_LINK_CTRL);
|
||||
plc &= ~PORT_LINK_CTRL_FLM;
|
||||
plc &= ~PORT_LINK_CTRL_LC_MASK;
|
||||
|
||||
/* Set link width speed control register */
|
||||
lwsc = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
lwsc &= ~PORT_LOGIC_LINK_WIDTH_MASK;
|
||||
lwsc |= PORT_LOGIC_LINK_WIDTH_1_LANES;
|
||||
lwsc = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
|
||||
lwsc &= ~LINK_WIDTH_MASK;
|
||||
lwsc |= LINK_WIDTH_1_LANES;
|
||||
switch (num_lanes) {
|
||||
case 1:
|
||||
plc |= PORT_LINK_MODE_1_LANES;
|
||||
plc |= PORT_LINK_CTRL_LC_1_LANES;
|
||||
break;
|
||||
case 2:
|
||||
plc |= PORT_LINK_MODE_2_LANES;
|
||||
plc |= PORT_LINK_CTRL_LC_2_LANES;
|
||||
break;
|
||||
case 4:
|
||||
plc |= PORT_LINK_MODE_4_LANES;
|
||||
plc |= PORT_LINK_CTRL_LC_4_LANES;
|
||||
break;
|
||||
case 8:
|
||||
plc |= PORT_LINK_MODE_8_LANES;
|
||||
plc |= PORT_LINK_CTRL_LC_8_LANES;
|
||||
break;
|
||||
case 16:
|
||||
plc |= PORT_LINK_MODE_16_LANES;
|
||||
plc |= PORT_LINK_CTRL_LC_16_LANES;
|
||||
break;
|
||||
default:
|
||||
dev_err(pci->dev, "num-lanes %u: invalid value\n", num_lanes);
|
||||
return;
|
||||
}
|
||||
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, plc);
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, lwsc);
|
||||
dw_pcie_writel_dbi(pci, PORT_LINK_CTRL, plc);
|
||||
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, lwsc);
|
||||
|
||||
cap = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
|
||||
lnkcap = dw_pcie_readl_dbi(pci, cap + PCI_EXP_LNKCAP);
|
||||
|
|
@ -937,48 +939,48 @@ void dw_pcie_iatu_detect(struct dw_pcie *pci)
|
|||
u32 val, min, dir;
|
||||
u64 max;
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT);
|
||||
val = dw_pcie_readl_dbi(pci, IATU_VIEWPORT);
|
||||
if (val == 0xFFFFFFFF) {
|
||||
dw_pcie_cap_set(pci, IATU_UNROLL);
|
||||
|
||||
max_region = min((int)pci->atu_size / 512, 256);
|
||||
} else {
|
||||
pci->atu_base = pci->dbi_base + PCIE_ATU_VIEWPORT_BASE;
|
||||
pci->atu_size = PCIE_ATU_VIEWPORT_SIZE;
|
||||
pci->atu_base = pci->dbi_base + ATU_VIEWPORT_BASE;
|
||||
pci->atu_size = ATU_VIEWPORT_SIZE;
|
||||
|
||||
dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, 0xFF);
|
||||
max_region = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) + 1;
|
||||
dw_pcie_writel_dbi(pci, IATU_VIEWPORT, 0xFF);
|
||||
max_region = dw_pcie_readl_dbi(pci, IATU_VIEWPORT) + 1;
|
||||
}
|
||||
|
||||
for (ob = 0; ob < max_region; ob++) {
|
||||
dw_pcie_writel_atu_ob(pci, ob, PCIE_ATU_LOWER_TARGET, 0x11110000);
|
||||
val = dw_pcie_readl_atu_ob(pci, ob, PCIE_ATU_LOWER_TARGET);
|
||||
dw_pcie_writel_atu_ob(pci, ob, ATU_LOWER_TARGET, 0x11110000);
|
||||
val = dw_pcie_readl_atu_ob(pci, ob, ATU_LOWER_TARGET);
|
||||
if (val != 0x11110000)
|
||||
break;
|
||||
}
|
||||
|
||||
for (ib = 0; ib < max_region; ib++) {
|
||||
dw_pcie_writel_atu_ib(pci, ib, PCIE_ATU_LOWER_TARGET, 0x11110000);
|
||||
val = dw_pcie_readl_atu_ib(pci, ib, PCIE_ATU_LOWER_TARGET);
|
||||
dw_pcie_writel_atu_ib(pci, ib, ATU_LOWER_TARGET, 0x11110000);
|
||||
val = dw_pcie_readl_atu_ib(pci, ib, ATU_LOWER_TARGET);
|
||||
if (val != 0x11110000)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ob) {
|
||||
dir = PCIE_ATU_REGION_DIR_OB;
|
||||
dir = ATU_REGION_DIR_OB;
|
||||
} else if (ib) {
|
||||
dir = PCIE_ATU_REGION_DIR_IB;
|
||||
dir = ATU_REGION_DIR_IB;
|
||||
} else {
|
||||
dev_err(pci->dev, "No iATU regions found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dw_pcie_writel_atu(pci, dir, 0, PCIE_ATU_LIMIT, 0x0);
|
||||
min = dw_pcie_readl_atu(pci, dir, 0, PCIE_ATU_LIMIT);
|
||||
dw_pcie_writel_atu(pci, dir, 0, ATU_LIMIT, 0x0);
|
||||
min = dw_pcie_readl_atu(pci, dir, 0, ATU_LIMIT);
|
||||
|
||||
if (dw_pcie_ver_is_ge(pci, 460A)) {
|
||||
dw_pcie_writel_atu(pci, dir, 0, PCIE_ATU_UPPER_LIMIT, 0xFFFFFFFF);
|
||||
max = dw_pcie_readl_atu(pci, dir, 0, PCIE_ATU_UPPER_LIMIT);
|
||||
dw_pcie_writel_atu(pci, dir, 0, ATU_UPPER_LIMIT, 0xFFFFFFFF);
|
||||
max = dw_pcie_readl_atu(pci, dir, 0, ATU_UPPER_LIMIT);
|
||||
} else {
|
||||
max = 0;
|
||||
}
|
||||
|
|
@ -1273,19 +1275,19 @@ void dw_pcie_setup(struct dw_pcie *pci)
|
|||
|
||||
/* Configure Gen1 N_FTS */
|
||||
if (pci->n_fts[0]) {
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR);
|
||||
val &= ~(PORT_AFR_N_FTS_MASK | PORT_AFR_CC_N_FTS_MASK);
|
||||
val |= PORT_AFR_N_FTS(pci->n_fts[0]);
|
||||
val |= PORT_AFR_CC_N_FTS(pci->n_fts[0]);
|
||||
dw_pcie_writel_dbi(pci, PCIE_PORT_AFR, val);
|
||||
val = dw_pcie_readl_dbi(pci, PORT_AFAC);
|
||||
val &= ~(PORT_AFAC_N_FTS_MASK | PORT_AFAC_CC_N_FTS_MASK);
|
||||
val |= PORT_AFAC_N_FTS(pci->n_fts[0]);
|
||||
val |= PORT_AFAC_CC_N_FTS(pci->n_fts[0]);
|
||||
dw_pcie_writel_dbi(pci, PORT_AFAC, val);
|
||||
}
|
||||
|
||||
/* Configure Gen2+ N_FTS */
|
||||
if (pci->n_fts[1]) {
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
val &= ~PORT_LOGIC_N_FTS_MASK;
|
||||
val = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
|
||||
val &= ~N_FTS_MASK;
|
||||
val |= pci->n_fts[1];
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
||||
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, val);
|
||||
}
|
||||
|
||||
if (dw_pcie_cap_is(pci, CDM_CHECK)) {
|
||||
|
|
@ -1295,10 +1297,10 @@ void dw_pcie_setup(struct dw_pcie *pci)
|
|||
dw_pcie_writel_dbi(pci, PCIE_PL_CHK_REG_CONTROL_STATUS, val);
|
||||
}
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
|
||||
val &= ~PORT_LINK_FAST_LINK_MODE;
|
||||
val |= PORT_LINK_DLL_LINK_EN;
|
||||
dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
|
||||
val = dw_pcie_readl_dbi(pci, PORT_LINK_CTRL);
|
||||
val &= ~PORT_LINK_CTRL_FLM;
|
||||
val |= PORT_LINK_CTRL_DLE;
|
||||
dw_pcie_writel_dbi(pci, PORT_LINK_CTRL, val);
|
||||
|
||||
dw_pcie_link_set_max_link_width(pci, pci->num_lanes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,38 +71,40 @@
|
|||
#define LINK_WAIT_IATU 9
|
||||
|
||||
/* Synopsys-specific PCIe configuration registers */
|
||||
#define PCIE_PORT_FORCE 0x708
|
||||
#define PORT_FORCE_LINK 0x708 /* Port Force Link */
|
||||
/* Bit[7:0] LINK_NUM: Link Number. Not used for endpoint */
|
||||
#define PORT_LINK_NUM_MASK GENMASK(7, 0)
|
||||
#define PORT_FORCE_DO_DESKEW_FOR_SRIS BIT(23)
|
||||
#define PORT_FORCE_LINK_NUM_MASK GENMASK(7, 0) /* Link Number. Not used for endpoint */
|
||||
#define PORT_FORCE_LINK_DDFS BIT(23) /* do deskew for sris */
|
||||
|
||||
#define PCIE_PORT_AFR 0x70C
|
||||
#define PORT_AFR_N_FTS_MASK GENMASK(15, 8)
|
||||
#define PORT_AFR_N_FTS(n) FIELD_PREP(PORT_AFR_N_FTS_MASK, n)
|
||||
#define PORT_AFR_CC_N_FTS_MASK GENMASK(23, 16)
|
||||
#define PORT_AFR_CC_N_FTS(n) FIELD_PREP(PORT_AFR_CC_N_FTS_MASK, n)
|
||||
#define PORT_AFR_ENTER_ASPM BIT(30)
|
||||
#define PORT_AFR_L0S_ENTRANCE_LAT_SHIFT 24
|
||||
#define PORT_AFR_L0S_ENTRANCE_LAT_MASK GENMASK(26, 24)
|
||||
#define PORT_AFR_L1_ENTRANCE_LAT_SHIFT 27
|
||||
#define PORT_AFR_L1_ENTRANCE_LAT_MASK GENMASK(29, 27)
|
||||
#define PORT_AFAC 0x70C /* Ack Frequency and L0-L1 ASPM Control */
|
||||
/* Ack the number of Fast Training Sequence mask */
|
||||
#define PORT_AFAC_N_FTS_MASK GENMASK(15, 8)
|
||||
/* Ack the number of Fast Training Sequence */
|
||||
#define PORT_AFAC_N_FTS(n) FIELD_PREP(PORT_AFAC_N_FTS_MASK, n)
|
||||
#define PORT_AFAC_CC_N_FTS_MASK GENMASK(23, 16) /* Common Clock N_FTS mask */
|
||||
/* Common Clock N_FTS */
|
||||
#define PORT_AFAC_CC_N_FTS(n) FIELD_PREP(PORT_AFAC_CC_N_FTS_MASK, n)
|
||||
#define PORT_AFAC_ENTER_ASPM BIT(30) /* ASPM L1 Entry Control */
|
||||
#define PORT_AFAC_L0S_ELM GENMASK(26, 24) /* L0s Entrance Latency mask */
|
||||
#define PORT_AFAC_L1_ELM GENMASK(29, 27) /* L1 Entrance Latency mask */
|
||||
|
||||
#define PCIE_PORT_LINK_CONTROL 0x710
|
||||
#define PORT_LINK_DLL_LINK_EN BIT(5)
|
||||
#define PORT_LINK_FAST_LINK_MODE BIT(7)
|
||||
#define PORT_LINK_MODE_MASK GENMASK(21, 16)
|
||||
#define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n)
|
||||
#define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1)
|
||||
#define PORT_LINK_MODE_2_LANES PORT_LINK_MODE(0x3)
|
||||
#define PORT_LINK_MODE_4_LANES PORT_LINK_MODE(0x7)
|
||||
#define PORT_LINK_MODE_8_LANES PORT_LINK_MODE(0xf)
|
||||
#define PORT_LINK_MODE_16_LANES PORT_LINK_MODE(0x1f)
|
||||
#define PORT_LINK_CTRL 0x710 /* Port Link Control Register */
|
||||
#define PORT_LINK_CTRL_DLE BIT(5) /* DLL Link Enable */
|
||||
#define PORT_LINK_CTRL_FLM BIT(7) /* Fast Link Mode */
|
||||
#define PORT_LINK_CTRL_LC_MASK GENMASK(21, 16) /* Link Mode Enable (LINK_CAPABLE) mask */
|
||||
/* Link Mode Enable (LINK_CAPABLE) */
|
||||
#define PORT_LINK_CTRL_LC(n) FIELD_PREP(PORT_LINK_CTRL_LC_MASK, n)
|
||||
#define PORT_LINK_CTRL_LC_1_LANES PORT_LINK_CTRL_LC(0x1) /* Link Mode 1 lane */
|
||||
#define PORT_LINK_CTRL_LC_2_LANES PORT_LINK_CTRL_LC(0x3) /* Link Mode 2 lane */
|
||||
#define PORT_LINK_CTRL_LC_4_LANES PORT_LINK_CTRL_LC(0x7) /* Link Mode 4 lane */
|
||||
#define PORT_LINK_CTRL_LC_8_LANES PORT_LINK_CTRL_LC(0xf) /* Link Mode 8 lane */
|
||||
#define PORT_LINK_CTRL_LC_16_LANES PORT_LINK_CTRL_LC(0x1f) /* Link Mode 16 lane */
|
||||
|
||||
#define PCIE_PORT_LANE_SKEW 0x714
|
||||
#define PORT_LANE_SKEW_INSERT_MASK GENMASK(23, 0)
|
||||
#define PORT_LANE_SKEW 0x714 /* Lane Skew */
|
||||
#define PORT_LANE_SKEW_INSERT_MASK GENMASK(23, 0)
|
||||
|
||||
/*
|
||||
* PCIE_TIMER_CTRL_MAX_FUNC_NUM: Timer Control and Max Function Number
|
||||
* TIMER_CTRL_MAX_FUNC_NUM: Timer Control and Max Function Number
|
||||
* Register.
|
||||
*
|
||||
* This register holds the ack frequency, latency, replay, fast link
|
||||
|
|
@ -116,68 +118,79 @@
|
|||
* 0x2 (SF_64): Scaling Factor is 64 (1ms is 16us)
|
||||
* 0x3 (SF_16): Scaling Factor is 16 (1ms is 64us)
|
||||
*/
|
||||
#define PCIE_TIMER_CTRL_MAX_FUNC_NUM 0x718
|
||||
#define PORT_FLT_SF_MASK GENMASK(30, 29)
|
||||
#define PORT_FLT_SF_VAL_1024 0x0
|
||||
#define PORT_FLT_SF_VAL_256 0x1
|
||||
#define PORT_FLT_SF_VAL_64 0x2
|
||||
#define PORT_FLT_SF_VAL_16 0x3
|
||||
#define TIMER_CTRL_MAX_FUNC_NUM 0x718
|
||||
#define PORT_FLT_SF_MASK GENMASK(30, 29)
|
||||
#define PORT_FLT_SF_VAL_1024 0x0
|
||||
#define PORT_FLT_SF_VAL_256 0x1
|
||||
#define PORT_FLT_SF_VAL_64 0x2
|
||||
#define PORT_FLT_SF_VAL_16 0x3
|
||||
|
||||
#define PCIE_PORT_DEBUG0 0x728
|
||||
#define PORT_LOGIC_LTSSM_STATE_MASK 0x3f
|
||||
#define PORT_LOGIC_LTSSM_STATE_L0 0x11
|
||||
#define PCIE_PORT_DEBUG1 0x72C
|
||||
#define PCIE_PORT_DEBUG1_LINK_UP BIT(4)
|
||||
#define PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29)
|
||||
#define PORT_LINK_DEBUG0 0x728 /* Port Link Debug Register 0 */
|
||||
#define PORT_LINK_DEBUG0_LTSSM_MASK 0x3f /* LTSSM current state mask */
|
||||
#define PORT_LINK_DEBUG1 0x72C /* Port Link Debug Register 1 */
|
||||
/* LTSSM reports PHY link up or LTSSM is in Loopback */
|
||||
#define PORT_LINK_DEBUG1_LINK_UP BIT(4)
|
||||
#define PORT_LINK_DEBUG1_LINK_IN_TRAINING BIT(29) /* LTSSM performing link training */
|
||||
|
||||
#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
|
||||
#define PORT_LOGIC_N_FTS_MASK GENMASK(7, 0)
|
||||
#define PORT_LOGIC_SPEED_CHANGE BIT(17)
|
||||
#define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8)
|
||||
#define PORT_LOGIC_LINK_WIDTH(n) FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n)
|
||||
#define PORT_LOGIC_LINK_WIDTH_1_LANES PORT_LOGIC_LINK_WIDTH(0x1)
|
||||
#define PORT_LOGIC_LINK_WIDTH_2_LANES PORT_LOGIC_LINK_WIDTH(0x2)
|
||||
#define PORT_LOGIC_LINK_WIDTH_4_LANES PORT_LOGIC_LINK_WIDTH(0x4)
|
||||
#define PORT_LOGIC_LINK_WIDTH_8_LANES PORT_LOGIC_LINK_WIDTH(0x8)
|
||||
#define LINK_WIDTH_SPEED_CTRL 0x80C /* Link Width and Speed Change Control */
|
||||
#define N_FTS_MASK GENMASK(7, 0)
|
||||
#define SPEED_CHANGE BIT(17)
|
||||
#define LINK_WIDTH_MASK GENMASK(12, 8)
|
||||
#define LINK_WIDTH(n) FIELD_PREP(LINK_WIDTH_MASK, n)
|
||||
#define LINK_WIDTH_1_LANES LINK_WIDTH(0x1)
|
||||
#define LINK_WIDTH_2_LANES LINK_WIDTH(0x2)
|
||||
#define LINK_WIDTH_4_LANES LINK_WIDTH(0x4)
|
||||
#define LINK_WIDTH_8_LANES LINK_WIDTH(0x8)
|
||||
|
||||
/* Integrated MSI Reception Module Lower Address */
|
||||
#define PCIE_MSI_ADDR_LO 0x820
|
||||
/* Integrated MSI Reception Module Upper Address */
|
||||
#define PCIE_MSI_ADDR_HI 0x824
|
||||
/* Integrated MSI Reception Module Interrupt0 Enable */
|
||||
#define PCIE_MSI_INTR0_ENABLE 0x828
|
||||
/* Integrated MSI Reception Module Interrupt0 Mask */
|
||||
#define PCIE_MSI_INTR0_MASK 0x82C
|
||||
/* Integrated MSI Reception Module Interrupt0 Status */
|
||||
#define PCIE_MSI_INTR0_STATUS 0x830
|
||||
|
||||
#define GEN3_RELATED_OFF 0x890
|
||||
#define GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL BIT(0)
|
||||
#define GEN3_RELATED_OFF_EQ_PHASE_2_3 BIT(9)
|
||||
#define GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS BIT(13)
|
||||
#define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16)
|
||||
#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24
|
||||
#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK GENMASK(25, 24)
|
||||
#define GEN3_CTRL 0x890 /* Gen3 Control Register */
|
||||
/* Gen3 Receiver Impedance ZRX-DC Not Compliant */
|
||||
#define GEN3_CTRL_GEN3_ZRXDC_NONCOMPL BIT(0)
|
||||
#define GEN3_CTRL_EQ_PHASE_2_3 BIT(9) /* Equalization Phase 2 and Phase 3 */
|
||||
#define GEN3_CTRL_RXEQ_RGRDLESS_RXTS BIT(13)
|
||||
#define GEN3_CTRL_GEN3_EQ_DISABLE BIT(16) /* Equalization Disable */
|
||||
#define GEN3_CTRL_RATE_SHADOW_SEL_MASK GENMASK(25, 24) /* Rate Shadow Select mask */
|
||||
|
||||
#define GEN3_EQ_CONTROL_OFF 0x8A8
|
||||
#define GEN3_EQ_CONTROL_OFF_FB_MODE GENMASK(3, 0)
|
||||
#define GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE BIT(4)
|
||||
#define GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC GENMASK(23, 8)
|
||||
#define GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL BIT(24)
|
||||
#define GEN3_EQ_CTRL 0x8A8 /* Gen3 EQ Control */
|
||||
#define GEN3_EQ_CTRL_FB_MODE GENMASK(3, 0) /* Feedback Mode */
|
||||
#define GEN3_EQ_CTRL_PHASE23_EXIT_MODE BIT(4) /* Behavior After 24 ms Timeout */
|
||||
#define GEN3_EQ_CTRL_PSET_REQ_VEC GENMASK(23, 8) /* Preset Request Vector */
|
||||
/* Include or not the FOM feedback from the initial preset */
|
||||
#define GEN3_EQ_CTRL_FOM_INC_INITIAL_EVAL BIT(24)
|
||||
|
||||
#define GEN3_EQ_FB_MODE_DIR_CHANGE_OFF 0x8AC
|
||||
#define GEN3_EQ_FMDC_T_MIN_PHASE23 GENMASK(4, 0)
|
||||
#define GEN3_EQ_FMDC_N_EVALS GENMASK(9, 5)
|
||||
#define GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA GENMASK(13, 10)
|
||||
#define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA GENMASK(17, 14)
|
||||
/* Gen3 EQ Direction Change Feedback Mode Control */
|
||||
#define GEN3_EQ_FB_MODE_DIR_CHANGE 0x8AC
|
||||
#define GEN3_EQ_FMDC_T_MIN_PHASE23 GENMASK(4, 0)
|
||||
#define GEN3_EQ_FMDC_N_EVALS GENMASK(9, 5)
|
||||
#define GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA GENMASK(13, 10)
|
||||
#define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA GENMASK(17, 14)
|
||||
|
||||
#define COHERENCY_CONTROL_1_OFF 0x8E0
|
||||
#define CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK GENMASK(31, 2)
|
||||
#define CFG_MEMTYPE_VALUE BIT(0)
|
||||
#define MISC_CONTROL_1 0x8BC /* DBI Read-Only Write Enable */
|
||||
#define DBI_RO_WR_EN BIT(0) /* Write to RO Registers Using DBI */
|
||||
|
||||
#define COHERENCY_CONTROL_2_OFF 0x8E4
|
||||
#define COHERENCY_CONTROL_3_OFF 0x8E8
|
||||
#define COHERENCY_CTRL1 0x8E0 /* Cache Coherency Control Register 1 */
|
||||
/* Boundary Lower Address For Memory Type */
|
||||
#define CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK GENMASK(31, 2)
|
||||
#define CFG_MEMTYPE_VALUE BIT(0) /* Sets the memory type */
|
||||
|
||||
#define PCIE_PORT_MULTI_LANE_CTRL 0x8C0
|
||||
#define PORT_MLTI_UPCFG_SUPPORT BIT(7)
|
||||
#define COHERENCY_CTRL2 0x8E4 /* Cache Coherency Control Register 2 */
|
||||
#define COHERENCY_CTRL3 0x8E8 /* Cache Coherency Control Register 3 */
|
||||
|
||||
#define PCIE_VERSION_NUMBER 0x8F8
|
||||
#define PCIE_VERSION_TYPE 0x8FC
|
||||
#define MULTI_LANE_CTRL 0x8C0 /* UpConfigure Multi-lane Control */
|
||||
#define MULTI_LANE_CTRL_UPCFG_SUPPORT BIT(7) /* Upconfigure Support */
|
||||
|
||||
#define PCIE_VERSION_NUMBER 0x8F8 /* PCIe Controller IIP Release Version Number */
|
||||
#define PCIE_VERSION_TYPE 0x8FC /* PCIe Controller IIP Release Version Type */
|
||||
|
||||
/*
|
||||
* iATU inbound and outbound windows CSRs. Before the IP-core v4.80a each
|
||||
|
|
@ -186,38 +199,36 @@
|
|||
* v4.80a in a way so the viewport was unrolled into the directly accessible
|
||||
* iATU/eDMA CSRs space.
|
||||
*/
|
||||
#define PCIE_ATU_VIEWPORT 0x900
|
||||
#define PCIE_ATU_REGION_DIR_IB BIT(31)
|
||||
#define PCIE_ATU_REGION_DIR_OB 0
|
||||
#define PCIE_ATU_VIEWPORT_BASE 0x904
|
||||
#define PCIE_ATU_UNROLL_BASE(dir, index) \
|
||||
(((index) << 9) | ((dir == PCIE_ATU_REGION_DIR_IB) ? BIT(8) : 0))
|
||||
#define PCIE_ATU_VIEWPORT_SIZE 0x2C
|
||||
#define PCIE_ATU_REGION_CTRL1 0x000
|
||||
#define PCIE_ATU_INCREASE_REGION_SIZE BIT(13)
|
||||
#define PCIE_ATU_TD BIT(8)
|
||||
#define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20)
|
||||
#define PCIE_ATU_REGION_CTRL2 0x004
|
||||
#define PCIE_ATU_ENABLE BIT(31)
|
||||
#define PCIE_ATU_BAR_MODE_ENABLE BIT(30)
|
||||
#define PCIE_ATU_CFG_SHIFT_MODE_ENABLE BIT(28)
|
||||
#define PCIE_ATU_INHIBIT_PAYLOAD BIT(22)
|
||||
#define PCIE_ATU_FUNC_NUM_MATCH_EN BIT(19)
|
||||
#define PCIE_ATU_LOWER_BASE 0x008
|
||||
#define PCIE_ATU_UPPER_BASE 0x00C
|
||||
#define PCIE_ATU_LIMIT 0x010
|
||||
#define PCIE_ATU_LOWER_TARGET 0x014
|
||||
#define PCIE_ATU_BUS(x) FIELD_PREP(GENMASK(31, 24), x)
|
||||
#define PCIE_ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x)
|
||||
#define PCIE_ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x)
|
||||
#define PCIE_ATU_UPPER_TARGET 0x018
|
||||
#define PCIE_ATU_UPPER_LIMIT 0x020
|
||||
#define IATU_VIEWPORT 0x900 /* iATU Index */
|
||||
#define ATU_REGION_DIR_IB BIT(31)
|
||||
#define ATU_REGION_DIR_OB 0
|
||||
#define ATU_VIEWPORT_BASE 0x904
|
||||
#define ATU_UNROLL_BASE(dir, index) \
|
||||
(((index) << 9) | ((dir == ATU_REGION_DIR_IB) ? BIT(8) : 0))
|
||||
#define ATU_VIEWPORT_SIZE 0x2C
|
||||
#define ATU_REGION_CTRL1 0x000
|
||||
#define ATU_INCREASE_REGION_SIZE BIT(13)
|
||||
#define ATU_TD BIT(8)
|
||||
#define ATU_FUNC_NUM(pf) ((pf) << 20)
|
||||
#define ATU_REGION_CTRL2 0x004
|
||||
#define ATU_ENABLE BIT(31)
|
||||
#define ATU_BAR_MODE_ENABLE BIT(30)
|
||||
#define ATU_CFG_SHIFT_MODE_ENABLE BIT(28)
|
||||
#define ATU_DMA_BYPASS BIT(27)
|
||||
#define ATU_INHIBIT_PAYLOAD BIT(22)
|
||||
#define ATU_FUNC_NUM_MATCH_EN BIT(19)
|
||||
#define ATU_LOWER_BASE 0x008
|
||||
#define ATU_UPPER_BASE 0x00C
|
||||
#define ATU_LIMIT 0x010
|
||||
#define ATU_LOWER_TARGET 0x014
|
||||
#define ATU_BUS(x) FIELD_PREP(GENMASK(31, 24), x)
|
||||
#define ATU_DEV(x) FIELD_PREP(GENMASK(23, 19), x)
|
||||
#define ATU_FUNC(x) FIELD_PREP(GENMASK(18, 16), x)
|
||||
#define ATU_UPPER_TARGET 0x018
|
||||
#define ATU_UPPER_LIMIT 0x020
|
||||
|
||||
#define PCIE_MISC_CONTROL_1_OFF 0x8BC
|
||||
#define PCIE_DBI_RO_WR_EN BIT(0)
|
||||
|
||||
#define PCIE_MSIX_DOORBELL 0x948
|
||||
#define PCIE_MSIX_DOORBELL_PF_SHIFT 24
|
||||
#define MSIX_DOORBELL 0x948
|
||||
#define MSIX_DOORBELL_PF_MASK GENMASK(28, 24)
|
||||
|
||||
/*
|
||||
* eDMA CSRs. DW PCIe IP-core v4.70a and older had the eDMA registers accessible
|
||||
|
|
@ -258,37 +269,24 @@
|
|||
#define MARGINING_MAXLANES GENMASK(20, 16)
|
||||
#define MARGINING_SAMPLE_RATE_TIMING GENMASK(13, 8)
|
||||
#define MARGINING_SAMPLE_RATE_VOLTAGE GENMASK(5, 0)
|
||||
/*
|
||||
* iATU Unroll-specific register definitions
|
||||
* From 4.80 core version the address translation will be made by unroll
|
||||
*/
|
||||
#define PCIE_ATU_UNR_REGION_CTRL1 0x00
|
||||
#define PCIE_ATU_UNR_REGION_CTRL2 0x04
|
||||
#define PCIE_ATU_UNR_LOWER_BASE 0x08
|
||||
#define PCIE_ATU_UNR_UPPER_BASE 0x0C
|
||||
#define PCIE_ATU_UNR_LOWER_LIMIT 0x10
|
||||
#define PCIE_ATU_UNR_LOWER_TARGET 0x14
|
||||
#define PCIE_ATU_UNR_UPPER_TARGET 0x18
|
||||
#define PCIE_ATU_UNR_UPPER_LIMIT 0x20
|
||||
|
||||
/*
|
||||
* RAS-DES register definitions
|
||||
*/
|
||||
#define PCIE_RAS_DES_EVENT_COUNTER_CONTROL 0x8
|
||||
#define RAS_DES_EVENT_COUNTER_CONTROL 0x8
|
||||
#define EVENT_COUNTER_ALL_CLEAR 0x3
|
||||
#define EVENT_COUNTER_ENABLE_ALL 0x7
|
||||
#define EVENT_COUNTER_ENABLE_SHIFT 2
|
||||
#define EVENT_COUNTER_EVENT_SEL_MASK GENMASK(7, 0)
|
||||
#define EVENT_COUNTER_EVENT_SEL_SHIFT 16
|
||||
#define EVENT_COUNTER_ENABLE_MASK GENMASK(4, 2)
|
||||
#define EVENT_COUNTER_EVENT_SEL_MASK GENMASK(23, 16)
|
||||
#define EVENT_COUNTER_GROUP_SEL_MASK GENMASK(27, 24)
|
||||
#define EVENT_COUNTER_EVENT_Tx_L0S 0x2
|
||||
#define EVENT_COUNTER_EVENT_Rx_L0S 0x3
|
||||
#define EVENT_COUNTER_EVENT_L1 0x5
|
||||
#define EVENT_COUNTER_EVENT_L1_1 0x7
|
||||
#define EVENT_COUNTER_EVENT_L1_2 0x8
|
||||
#define EVENT_COUNTER_GROUP_SEL_SHIFT 24
|
||||
#define EVENT_COUNTER_GROUP_5 0x5
|
||||
|
||||
#define PCIE_RAS_DES_EVENT_COUNTER_DATA 0xc
|
||||
#define RAS_DES_EVENT_COUNTER_DATA 0xc
|
||||
|
||||
/* PTM register definitions */
|
||||
#define PTM_RES_REQ_CTRL 0x8
|
||||
|
|
@ -376,7 +374,7 @@ enum dw_pcie_core_rst {
|
|||
};
|
||||
|
||||
enum dw_pcie_ltssm {
|
||||
/* Need to align with PCIE_PORT_DEBUG0 bits 0:5 */
|
||||
/* Need to align with PORT_LINK_DEBUG0 bits 0:5 */
|
||||
DW_PCIE_LTSSM_DETECT_QUIET = 0x0,
|
||||
DW_PCIE_LTSSM_DETECT_ACT = 0x1,
|
||||
DW_PCIE_LTSSM_POLL_ACTIVE = 0x2,
|
||||
|
|
@ -454,7 +452,7 @@ struct dw_pcie_rp {
|
|||
int irq;
|
||||
const struct dw_pcie_host_ops *ops;
|
||||
int msi_irq[MAX_MSI_CTRLS];
|
||||
struct irq_domain *irq_domain;
|
||||
struct irq_domain *msi_irq_domain;
|
||||
dma_addr_t msi_data;
|
||||
struct irq_chip *msi_irq_chip;
|
||||
u32 num_vectors;
|
||||
|
|
@ -469,12 +467,16 @@ struct dw_pcie_rp {
|
|||
struct pci_config_window *cfg;
|
||||
bool ecam_enabled;
|
||||
bool native_ecam;
|
||||
bool bypass_ob_mem_iatu;
|
||||
bool skip_l23_ready;
|
||||
bool skip_pwrctrl_off;
|
||||
/* Set if the RC loses power during suspend */
|
||||
bool suspend_poweroff;
|
||||
};
|
||||
|
||||
struct dw_pcie_ep_ops {
|
||||
int (*pre_init)(struct dw_pcie_ep *ep);
|
||||
void (*post_deinit)(struct dw_pcie_ep *ep);
|
||||
int (*init)(struct dw_pcie_ep *ep);
|
||||
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
|
||||
unsigned int type, u16 interrupt_num);
|
||||
|
|
@ -813,9 +815,9 @@ static inline void dw_pcie_dbi_ro_wr_en(struct dw_pcie *pci)
|
|||
u32 reg;
|
||||
u32 val;
|
||||
|
||||
reg = PCIE_MISC_CONTROL_1_OFF;
|
||||
reg = MISC_CONTROL_1;
|
||||
val = dw_pcie_readl_dbi(pci, reg);
|
||||
val |= PCIE_DBI_RO_WR_EN;
|
||||
val |= DBI_RO_WR_EN;
|
||||
dw_pcie_writel_dbi(pci, reg, val);
|
||||
}
|
||||
|
||||
|
|
@ -824,9 +826,9 @@ static inline void dw_pcie_dbi_ro_wr_dis(struct dw_pcie *pci)
|
|||
u32 reg;
|
||||
u32 val;
|
||||
|
||||
reg = PCIE_MISC_CONTROL_1_OFF;
|
||||
reg = MISC_CONTROL_1;
|
||||
val = dw_pcie_readl_dbi(pci, reg);
|
||||
val &= ~PCIE_DBI_RO_WR_EN;
|
||||
val &= ~DBI_RO_WR_EN;
|
||||
dw_pcie_writel_dbi(pci, reg, val);
|
||||
}
|
||||
|
||||
|
|
@ -851,9 +853,9 @@ static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)
|
|||
if (pci->ops && pci->ops->get_ltssm)
|
||||
return pci->ops->get_ltssm(pci);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_PORT_DEBUG0);
|
||||
val = dw_pcie_readl_dbi(pci, PORT_LINK_DEBUG0);
|
||||
|
||||
return (enum dw_pcie_ltssm)FIELD_GET(PORT_LOGIC_LTSSM_STATE_MASK, val);
|
||||
return (enum dw_pcie_ltssm)FIELD_GET(PORT_LINK_DEBUG0_LTSSM_MASK, val);
|
||||
}
|
||||
|
||||
const char *dw_pcie_ltssm_status_string(enum dw_pcie_ltssm ltssm);
|
||||
|
|
@ -868,7 +870,7 @@ void dw_pcie_free_msi(struct dw_pcie_rp *pp);
|
|||
int dw_pcie_setup_rc(struct dw_pcie_rp *pp);
|
||||
int dw_pcie_host_init(struct dw_pcie_rp *pp);
|
||||
void dw_pcie_host_deinit(struct dw_pcie_rp *pp);
|
||||
int dw_pcie_allocate_domains(struct dw_pcie_rp *pp);
|
||||
int dw_pcie_allocate_msi_domains(struct dw_pcie_rp *pp);
|
||||
void __iomem *dw_pcie_own_conf_map_bus(struct pci_bus *bus, unsigned int devfn,
|
||||
int where);
|
||||
#else
|
||||
|
|
@ -909,7 +911,7 @@ static inline void dw_pcie_host_deinit(struct dw_pcie_rp *pp)
|
|||
{
|
||||
}
|
||||
|
||||
static inline int dw_pcie_allocate_domains(struct dw_pcie_rp *pp)
|
||||
static inline int dw_pcie_allocate_msi_domains(struct dw_pcie_rp *pp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <trace/events/pci_controller.h>
|
||||
|
|
|
|||
|
|
@ -388,6 +388,7 @@ static const struct of_device_id eswin_pcie_of_match[] = {
|
|||
{ .compatible = "eswin,eic7700-pcie", .data = &eswin_eic7700_data },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, eswin_pcie_of_match);
|
||||
|
||||
static struct platform_driver eswin_pcie_driver = {
|
||||
.probe = eswin_pcie_probe,
|
||||
|
|
|
|||
|
|
@ -215,9 +215,9 @@ static int fu740_pcie_start_link(struct dw_pcie *pci)
|
|||
tmp |= orig;
|
||||
dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCAP, tmp);
|
||||
|
||||
tmp = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
tmp |= PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
|
||||
tmp = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
|
||||
tmp |= SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, tmp);
|
||||
|
||||
ret = dw_pcie_wait_for_link(pci);
|
||||
if (ret) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/resource.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci)
|
|||
u32 ddr_base_high = upper_32_bits(S32G_MEMORY_BOUNDARY_ADDR);
|
||||
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_3_OFF, 0x0);
|
||||
dw_pcie_writel_dbi(pci, COHERENCY_CTRL3, 0x0);
|
||||
|
||||
/*
|
||||
* Ncore is a cache-coherent interconnect module that enables the
|
||||
|
|
@ -125,9 +125,9 @@ static void s32g_pcie_reset_mstr_ace(struct dw_pcie *pci)
|
|||
* Define the start of DDR as seen by Linux as this boundary between
|
||||
* "memory" and "peripherals", with peripherals being below.
|
||||
*/
|
||||
dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_1_OFF,
|
||||
dw_pcie_writel_dbi(pci, COHERENCY_CTRL1,
|
||||
(ddr_base_low & CFG_MEMTYPE_BOUNDARY_LOW_ADDR_MASK));
|
||||
dw_pcie_writel_dbi(pci, COHERENCY_CONTROL_2_OFF, ddr_base_high);
|
||||
dw_pcie_writel_dbi(pci, COHERENCY_CTRL2, ddr_base_high);
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
}
|
||||
|
||||
|
|
@ -154,13 +154,13 @@ static int s32g_init_pcie_controller(struct dw_pcie_rp *pp)
|
|||
|
||||
dw_pcie_dbi_ro_wr_en(pci);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE);
|
||||
val |= PORT_FORCE_DO_DESKEW_FOR_SRIS;
|
||||
dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val);
|
||||
val = dw_pcie_readl_dbi(pci, PORT_FORCE_LINK);
|
||||
val |= PORT_FORCE_LINK_DDFS;
|
||||
dw_pcie_writel_dbi(pci, PORT_FORCE_LINK, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
val |= GEN3_RELATED_OFF_EQ_PHASE_2_3;
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
|
||||
val |= GEN3_CTRL_EQ_PHASE_2_3;
|
||||
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
|
||||
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ void qcom_pcie_common_set_equalization(struct dw_pcie *pci)
|
|||
u16 speed;
|
||||
|
||||
/*
|
||||
* GEN3_RELATED_OFF register is repurposed to apply equalization
|
||||
* GEN3_CTRL register is repurposed to apply equalization
|
||||
* settings at various data transmission rates through registers namely
|
||||
* GEN3_EQ_*. The RATE_SHADOW_SEL bit field of GEN3_RELATED_OFF
|
||||
* GEN3_EQ_*. The RATE_SHADOW_SEL bit field of GEN3_CTRL
|
||||
* determines the data rate for which these equalization settings are
|
||||
* applied.
|
||||
*/
|
||||
|
|
@ -28,25 +28,25 @@ void qcom_pcie_common_set_equalization(struct dw_pcie *pci)
|
|||
break;
|
||||
}
|
||||
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
|
||||
FIELD_MODIFY(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, ®,
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_CTRL);
|
||||
reg &= ~GEN3_CTRL_GEN3_ZRXDC_NONCOMPL;
|
||||
FIELD_MODIFY(GEN3_CTRL_RATE_SHADOW_SEL_MASK, ®,
|
||||
speed - PCIE_SPEED_8_0GT);
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg);
|
||||
dw_pcie_writel_dbi(pci, GEN3_CTRL, reg);
|
||||
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF);
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE);
|
||||
FIELD_MODIFY(GEN3_EQ_FMDC_T_MIN_PHASE23, ®, 0x1);
|
||||
FIELD_MODIFY(GEN3_EQ_FMDC_N_EVALS, ®, 0xd);
|
||||
FIELD_MODIFY(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, ®, 0x5);
|
||||
FIELD_MODIFY(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, ®, 0x5);
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg);
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE, reg);
|
||||
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
|
||||
reg &= ~(GEN3_EQ_CONTROL_OFF_FB_MODE |
|
||||
GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE |
|
||||
GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL |
|
||||
GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC);
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, reg);
|
||||
reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CTRL);
|
||||
reg &= ~(GEN3_EQ_CTRL_FB_MODE |
|
||||
GEN3_EQ_CTRL_PHASE23_EXIT_MODE |
|
||||
GEN3_EQ_CTRL_FOM_INC_INITIAL_EVAL |
|
||||
GEN3_EQ_CTRL_PSET_REQ_VEC);
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_CTRL, reg);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qcom_pcie_common_set_equalization);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/module.h>
|
||||
|
|
@ -944,6 +945,8 @@ static void qcom_pcie_ep_remove(struct platform_device *pdev)
|
|||
|
||||
if (pcie_ep->link_status == QCOM_PCIE_EP_LINK_DISABLED)
|
||||
return;
|
||||
pci_epc_deinit_notify(pcie_ep->pci.ep.epc);
|
||||
dw_pcie_ep_deinit(&pcie_ep->pci.ep);
|
||||
|
||||
qcom_pcie_disable_resources(pcie_ep);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/pci.h>
|
||||
|
|
@ -73,6 +74,23 @@
|
|||
#define PARF_BDF_TO_SID_TABLE_N 0x2000
|
||||
#define PARF_BDF_TO_SID_CFG 0x2c00
|
||||
|
||||
/*
|
||||
* Outbound blocker range registers. The blocked range has a write pair
|
||||
* (WR_BASE/WR_LIMIT) and a read pair (RD_BASE/RD_LIMIT); each address is
|
||||
* split into a low (32-bit) and a HI (upper 32-bit) register.
|
||||
*/
|
||||
#define PARF_BLOCK_SLV_AXI_WR_BASE 0x360
|
||||
#define PARF_BLOCK_SLV_AXI_WR_BASE_HI 0x364
|
||||
#define PARF_BLOCK_SLV_AXI_WR_LIMIT 0x368
|
||||
#define PARF_BLOCK_SLV_AXI_WR_LIMIT_HI 0x36c
|
||||
#define PARF_BLOCK_SLV_AXI_RD_BASE 0x370
|
||||
#define PARF_BLOCK_SLV_AXI_RD_BASE_HI 0x374
|
||||
#define PARF_BLOCK_SLV_AXI_RD_LIMIT 0x378
|
||||
#define PARF_BLOCK_SLV_AXI_RD_LIMIT_HI 0x37c
|
||||
|
||||
#define PARF_ECAM_BASE 0x380
|
||||
#define PARF_ECAM_BASE_HI 0x384
|
||||
|
||||
/* ELBI registers */
|
||||
#define ELBI_SYS_CTRL 0x04
|
||||
#define ELBI_SYS_STTS 0x08
|
||||
|
|
@ -90,6 +108,7 @@
|
|||
|
||||
/* PARF_SYS_CTRL register fields */
|
||||
#define MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN BIT(29)
|
||||
#define OUTBOUND_BLOCKER_EN BIT(26)
|
||||
#define MST_WAKEUP_EN BIT(13)
|
||||
#define SLV_WAKEUP_EN BIT(12)
|
||||
#define MSTR_ACLK_CGC_DIS BIT(10)
|
||||
|
|
@ -308,6 +327,7 @@ struct qcom_pcie {
|
|||
struct gpio_desc *reset;
|
||||
int global_irq;
|
||||
bool use_pm_opp;
|
||||
struct mutex reset_lock;
|
||||
};
|
||||
|
||||
#define to_qcom_pcie(x) dev_get_drvdata((x)->dev)
|
||||
|
|
@ -445,6 +465,25 @@ static void qcom_pcie_configure_dbi_atu_base(struct qcom_pcie *pcie)
|
|||
}
|
||||
}
|
||||
|
||||
static void qcom_pcie_init_outbound_blocker(struct qcom_pcie *pcie)
|
||||
{
|
||||
struct dw_pcie *pci = pcie->pci;
|
||||
|
||||
/* ECAM base must match the DBI base address */
|
||||
writel(lower_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE);
|
||||
writel(upper_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE_HI);
|
||||
|
||||
writel(0, pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE);
|
||||
writel(0, pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE_HI);
|
||||
writel(U32_MAX, pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT);
|
||||
writel(U32_MAX, pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT_HI);
|
||||
|
||||
writel(0, pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE);
|
||||
writel(0, pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE_HI);
|
||||
writel(U32_MAX, pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT);
|
||||
writel(U32_MAX, pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT_HI);
|
||||
}
|
||||
|
||||
static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie)
|
||||
{
|
||||
struct dw_pcie *pci = pcie->pci;
|
||||
|
|
@ -990,6 +1029,8 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie)
|
|||
|
||||
dw_pcie_dbi_ro_wr_dis(pci);
|
||||
|
||||
qcom_pcie_init_outbound_blocker(pcie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1104,6 +1145,8 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie)
|
|||
|
||||
qcom_pcie_set_slot_cap(pcie->pci);
|
||||
|
||||
qcom_pcie_init_outbound_blocker(pcie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1143,50 +1186,90 @@ static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie)
|
|||
|
||||
static int qcom_pcie_config_sid_1_9_0(struct qcom_pcie *pcie)
|
||||
{
|
||||
/* iommu map structure */
|
||||
struct {
|
||||
u32 bdf;
|
||||
u32 phandle;
|
||||
u32 smmu_sid;
|
||||
u32 smmu_sid_len;
|
||||
} *map;
|
||||
void __iomem *bdf_to_sid_base = pcie->parf + PARF_BDF_TO_SID_TABLE_N;
|
||||
struct device *dev = pcie->pci->dev;
|
||||
struct device_node *iommu_np;
|
||||
u8 qcom_pcie_crc8_table[CRC8_TABLE_SIZE];
|
||||
int i, nr_map, size = 0;
|
||||
u32 smmu_sid_base;
|
||||
const __be32 *map;
|
||||
u32 iommu_cells, entry_cells, phandle, smmu_sid_base;
|
||||
int i, nr_cells, nr_map, size = 0;
|
||||
u32 val;
|
||||
|
||||
of_get_property(dev->of_node, "iommu-map", &size);
|
||||
if (!size)
|
||||
map = of_get_property(dev->of_node, "iommu-map", &size);
|
||||
if (!map || !size)
|
||||
return 0;
|
||||
|
||||
if (size % sizeof(*map)) {
|
||||
dev_err(dev, "Malformed iommu-map property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
nr_cells = size / sizeof(*map);
|
||||
|
||||
/*
|
||||
* Each iommu-map entry is: rid-base (1 cell), phandle (1 cell),
|
||||
* IOMMU specifier (#iommu-cells cells), length (1 cell). Read
|
||||
* #iommu-cells from the IOMMU provider referenced by the first
|
||||
* entry to compute the per-entry stride.
|
||||
*/
|
||||
phandle = be32_to_cpu(map[1]);
|
||||
iommu_np = of_find_node_by_phandle(phandle);
|
||||
if (!iommu_np) {
|
||||
dev_err(dev, "Failed to find IOMMU node in iommu-map\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(iommu_np, "#iommu-cells", &iommu_cells))
|
||||
iommu_cells = 1;
|
||||
of_node_put(iommu_np);
|
||||
|
||||
entry_cells = 3 + iommu_cells;
|
||||
|
||||
/*
|
||||
* Retain backward compatibility with DTs that describe iommu-map
|
||||
* with 4-cell entries against an IOMMU declaring #iommu-cells = 2,
|
||||
* matching the fallback in drivers/of/base.c::of_check_bad_map().
|
||||
*/
|
||||
if (iommu_cells == 2 && !(nr_cells % 4)) {
|
||||
bool legacy = true;
|
||||
|
||||
for (i = 0; i < nr_cells; i += 4) {
|
||||
if (be32_to_cpu(map[i + 1]) != phandle ||
|
||||
be32_to_cpu(map[i + 3]) != 1) {
|
||||
legacy = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (legacy) {
|
||||
dev_warn_once(dev, "iommu-map has 1-cell entries with #iommu-cells=2, using 1-cell\n");
|
||||
entry_cells = 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (nr_cells % entry_cells) {
|
||||
dev_err(dev, "Malformed iommu-map property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
nr_map = nr_cells / entry_cells;
|
||||
|
||||
/* Enable BDF to SID translation by disabling bypass mode (default) */
|
||||
val = readl(pcie->parf + PARF_BDF_TO_SID_CFG);
|
||||
val &= ~BDF_TO_SID_BYPASS;
|
||||
writel(val, pcie->parf + PARF_BDF_TO_SID_CFG);
|
||||
|
||||
map = kzalloc(size, GFP_KERNEL);
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
|
||||
of_property_read_u32_array(dev->of_node, "iommu-map", (u32 *)map,
|
||||
size / sizeof(u32));
|
||||
|
||||
nr_map = size / (sizeof(*map));
|
||||
|
||||
crc8_populate_msb(qcom_pcie_crc8_table, QCOM_PCIE_CRC8_POLYNOMIAL);
|
||||
|
||||
/* Registers need to be zero out first */
|
||||
memset_io(bdf_to_sid_base, 0, CRC8_TABLE_SIZE * sizeof(u32));
|
||||
|
||||
/* Extract the SMMU SID base from the first entry of iommu-map */
|
||||
smmu_sid_base = map[0].smmu_sid;
|
||||
smmu_sid_base = be32_to_cpu(map[2]);
|
||||
|
||||
/* Look for an available entry to hold the mapping */
|
||||
for (i = 0; i < nr_map; i++) {
|
||||
__be16 bdf_be = cpu_to_be16(map[i].bdf);
|
||||
u32 val;
|
||||
u32 bdf = be32_to_cpu(map[i * entry_cells]);
|
||||
u32 sid = be32_to_cpu(map[i * entry_cells + 2]);
|
||||
__be16 bdf_be = cpu_to_be16(bdf);
|
||||
u8 hash;
|
||||
|
||||
hash = crc8(qcom_pcie_crc8_table, (u8 *)&bdf_be, sizeof(bdf_be), 0);
|
||||
|
|
@ -1208,12 +1291,10 @@ static int qcom_pcie_config_sid_1_9_0(struct qcom_pcie *pcie)
|
|||
}
|
||||
|
||||
/* BDF [31:16] | SID [15:8] | NEXT [7:0] */
|
||||
val = map[i].bdf << 16 | (map[i].smmu_sid - smmu_sid_base) << 8 | 0;
|
||||
val = bdf << 16 | (sid - smmu_sid_base) << 8 | 0;
|
||||
writel(val, bdf_to_sid_base + hash * sizeof(u32));
|
||||
}
|
||||
|
||||
kfree(map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1295,9 +1376,9 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
|
|||
writel(DEVICE_TYPE_RC, pcie->parf + PARF_DEVICE_TYPE);
|
||||
writel(BYPASS | MSTR_AXI_CLK_EN | AHB_CLK_EN,
|
||||
pcie->parf + PARF_MHI_CLOCK_RESET_CTRL);
|
||||
writel(GEN3_RELATED_OFF_RXEQ_RGRDLESS_RXTS |
|
||||
GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL,
|
||||
pci->dbi_base + GEN3_RELATED_OFF);
|
||||
writel(GEN3_CTRL_RXEQ_RGRDLESS_RXTS |
|
||||
GEN3_CTRL_GEN3_ZRXDC_NONCOMPL,
|
||||
pci->dbi_base + GEN3_CTRL);
|
||||
|
||||
writel(MST_WAKEUP_EN | SLV_WAKEUP_EN | MSTR_ACLK_CGC_DIS |
|
||||
SLV_ACLK_CGC_DIS | CORE_CLK_CGC_DIS |
|
||||
|
|
@ -1322,6 +1403,8 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
|
|||
for (i = 0; i < 256; i++)
|
||||
writel(0, pcie->parf + PARF_BDF_TO_SID_TABLE_N + (4 * i));
|
||||
|
||||
qcom_pcie_init_outbound_blocker(pcie);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1382,6 +1465,18 @@ static void qcom_pcie_configure_ports(struct qcom_pcie *pcie)
|
|||
dw_pcie_program_t_power_on(pcie->pci, port->l1ss_t_power_on);
|
||||
}
|
||||
|
||||
static void qcom_pcie_enable_outbound_blocker(struct qcom_pcie *pcie)
|
||||
{
|
||||
u32 sys_ctrl;
|
||||
|
||||
sys_ctrl = readl(pcie->parf + PARF_SYS_CTRL);
|
||||
sys_ctrl |= OUTBOUND_BLOCKER_EN;
|
||||
writel(sys_ctrl, pcie->parf + PARF_SYS_CTRL);
|
||||
|
||||
/* Flush the write so the blocker is enabled before this function returns */
|
||||
readl(pcie->parf + PARF_SYS_CTRL);
|
||||
}
|
||||
|
||||
static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
|
||||
|
|
@ -1775,6 +1870,8 @@ static int qcom_pcie_reset_root_port(struct pci_host_bridge *bridge,
|
|||
u32 val;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&pcie->reset_lock);
|
||||
|
||||
/* Wait for the pending transactions to be completed */
|
||||
ret = readl_relaxed_poll_timeout(pcie->parf + PARF_STATUS, val,
|
||||
val & FLUSH_COMPLETED, 10,
|
||||
|
|
@ -1821,6 +1918,9 @@ static int qcom_pcie_reset_root_port(struct pci_host_bridge *bridge,
|
|||
qcom_pcie_start_link(pci);
|
||||
|
||||
ret = dw_pcie_wait_for_link(pci);
|
||||
/* This linkdown might have been a hot-unplug */
|
||||
if (ret == -ENODEV)
|
||||
ret = 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1878,6 +1978,11 @@ static irqreturn_t qcom_pcie_global_irq_thread(int irq, void *data)
|
|||
|
||||
if (test_and_clear_bit(INT_ALL_LINK_DOWN, &status)) {
|
||||
dev_dbg(dev, "Received Link down event\n");
|
||||
|
||||
mutex_lock(&pcie->reset_lock);
|
||||
qcom_pcie_enable_outbound_blocker(pcie);
|
||||
mutex_unlock(&pcie->reset_lock);
|
||||
|
||||
for_each_pci_bridge(port, pp->bridge->bus) {
|
||||
if (pci_pcie_type(port) == PCI_EXP_TYPE_ROOT_PORT)
|
||||
pci_host_handle_link_down(port);
|
||||
|
|
@ -1917,11 +2022,13 @@ static int qcom_pcie_ecam_host_init(struct pci_config_window *cfg)
|
|||
*/
|
||||
raw_spin_lock_init(&pp->lock);
|
||||
|
||||
ret = dw_pcie_msi_host_init(pp);
|
||||
if (ret)
|
||||
return ret;
|
||||
pp->use_imsi_rx = !pci_host_of_has_msi_map(dev);
|
||||
if (pp->use_imsi_rx) {
|
||||
ret = dw_pcie_msi_host_init(pp);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
pp->use_imsi_rx = true;
|
||||
dw_pcie_msi_init(pp);
|
||||
|
||||
return devm_add_action_or_reset(dev, qcom_pci_free_msi, pp);
|
||||
|
|
@ -1953,7 +2060,7 @@ static bool qcom_pcie_is_child_node(struct device *dev,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Parse PERST# from all nodes in depth first manner starting from @np */
|
||||
/* Collect PERST# GPIOs from PCI bridge nodes depth-first, starting at @np */
|
||||
static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
|
||||
struct qcom_pcie_port *port,
|
||||
struct device_node *np)
|
||||
|
|
@ -2019,6 +2126,9 @@ static int qcom_pcie_parse_perst(struct qcom_pcie *pcie,
|
|||
|
||||
parse_child_node:
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
if (!of_node_is_type(child, "pci"))
|
||||
continue;
|
||||
|
||||
ret = qcom_pcie_parse_perst(pcie, port, child);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -2204,6 +2314,10 @@ static int qcom_pcie_probe(struct platform_device *pdev)
|
|||
|
||||
INIT_LIST_HEAD(&pcie->ports);
|
||||
|
||||
ret = devm_mutex_init(dev, &pcie->reset_lock);
|
||||
if (ret)
|
||||
goto err_pm_runtime_put;
|
||||
|
||||
pci->dev = dev;
|
||||
pci->ops = &dw_pcie_ops;
|
||||
pp = &pci->pp;
|
||||
|
|
@ -2341,6 +2455,10 @@ static int qcom_pcie_suspend_noirq(struct device *dev)
|
|||
{
|
||||
struct qcom_pcie *pcie;
|
||||
int ret = 0;
|
||||
const struct qcom_pcie_cfg *pcie_cfg = of_device_get_match_data(dev);
|
||||
|
||||
if (pcie_cfg && pcie_cfg->firmware_managed)
|
||||
return 0;
|
||||
|
||||
pcie = dev_get_drvdata(dev);
|
||||
if (!pcie)
|
||||
|
|
@ -2399,6 +2517,10 @@ static int qcom_pcie_resume_noirq(struct device *dev)
|
|||
{
|
||||
struct qcom_pcie *pcie;
|
||||
int ret;
|
||||
const struct qcom_pcie_cfg *pcie_cfg = of_device_get_match_data(dev);
|
||||
|
||||
if (pcie_cfg && pcie_cfg->firmware_managed)
|
||||
return 0;
|
||||
|
||||
pcie = dev_get_drvdata(dev);
|
||||
if (!pcie)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
|
@ -36,6 +37,7 @@
|
|||
|
||||
/* MSI Capability */
|
||||
#define MSICAP0 0x0050
|
||||
#define MSICAP0_MMESCAP_MASK GENMASK(19, 17)
|
||||
#define MSICAP0_MSIE BIT(16)
|
||||
|
||||
/* PCIe Interrupt Status 0 */
|
||||
|
|
@ -74,6 +76,11 @@
|
|||
#define PCIEPWRMNGCTRL 0x0070
|
||||
#define APP_CLK_REQ_N BIT(11)
|
||||
#define APP_CLK_PM_EN BIT(10)
|
||||
#define APP_READY_ENTR_L23 BIT(6)
|
||||
#define APP_REQ_ENTR_L1 BIT(5)
|
||||
|
||||
/* PCI Express capability */
|
||||
#define EXPCAP(x) (0x0070 + (x))
|
||||
|
||||
#define RCAR_NUM_SPEED_CHANGE_RETRIES 10
|
||||
#define RCAR_MAX_LINK_SPEED 4
|
||||
|
|
@ -87,8 +94,10 @@ MODULE_FIRMWARE(RCAR_GEN4_PCIE_FIRMWARE_NAME);
|
|||
|
||||
struct rcar_gen4_pcie;
|
||||
struct rcar_gen4_pcie_drvdata {
|
||||
void (*additional_common_init)(struct rcar_gen4_pcie *rcar);
|
||||
int (*init)(struct rcar_gen4_pcie *rcar);
|
||||
void (*deinit)(struct rcar_gen4_pcie *rcar);
|
||||
int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable);
|
||||
int (*speed_control)(struct rcar_gen4_pcie *rcar);
|
||||
enum dw_pcie_device_mode mode;
|
||||
};
|
||||
|
||||
|
|
@ -96,7 +105,9 @@ struct rcar_gen4_pcie {
|
|||
struct dw_pcie dw;
|
||||
void __iomem *base;
|
||||
void __iomem *phy_base;
|
||||
struct phy *phy;
|
||||
struct platform_device *pdev;
|
||||
struct reset_control *perst;
|
||||
const struct rcar_gen4_pcie_drvdata *drvdata;
|
||||
};
|
||||
#define to_rcar_gen4_pcie(_dw) container_of(_dw, struct rcar_gen4_pcie, dw)
|
||||
|
|
@ -122,17 +133,17 @@ static int rcar_gen4_pcie_speed_change(struct dw_pcie *dw)
|
|||
u32 val;
|
||||
int i;
|
||||
|
||||
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
val &= ~PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
||||
val = dw_pcie_readl_dbi(dw, LINK_WIDTH_SPEED_CTRL);
|
||||
val &= ~SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(dw, LINK_WIDTH_SPEED_CTRL, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
val |= PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
||||
val = dw_pcie_readl_dbi(dw, LINK_WIDTH_SPEED_CTRL);
|
||||
val |= SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(dw, LINK_WIDTH_SPEED_CTRL, val);
|
||||
|
||||
for (i = 0; i < RCAR_NUM_SPEED_CHANGE_RETRIES; i++) {
|
||||
val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
if (!(val & PORT_LOGIC_SPEED_CHANGE))
|
||||
val = dw_pcie_readl_dbi(dw, LINK_WIDTH_SPEED_CTRL);
|
||||
if (!(val & SPEED_CHANGE))
|
||||
return 0;
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
|
|
@ -140,20 +151,10 @@ static int rcar_gen4_pcie_speed_change(struct dw_pcie *dw)
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable LTSSM of this controller and manually initiate the speed change.
|
||||
* Always return 0.
|
||||
*/
|
||||
static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
|
||||
static int rcar_gen4_pcie_speed_control(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
int i, changes, ret;
|
||||
|
||||
if (rcar->drvdata->ltssm_control) {
|
||||
ret = rcar->drvdata->ltssm_control(rcar, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
struct dw_pcie *dw = &rcar->dw;
|
||||
int i, changes;
|
||||
|
||||
/*
|
||||
* Require direct speed change with retrying here if the max_link_speed
|
||||
|
|
@ -177,6 +178,45 @@ static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen5_pcie_speed_control(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct dw_pcie *dw = &rcar->dw;
|
||||
u32 lnkcap = dw_pcie_readl_dbi(dw, EXPCAP(PCI_EXP_LNKCAP));
|
||||
u32 lnksta = dw_pcie_readw_dbi(dw, EXPCAP(PCI_EXP_LNKSTA));
|
||||
u32 val;
|
||||
|
||||
if ((lnksta & PCI_EXP_LNKSTA_CLS) == (lnkcap & PCI_EXP_LNKCAP_SLS))
|
||||
return 0;
|
||||
|
||||
/* Retrain link */
|
||||
val = dw_pcie_readw_dbi(dw, EXPCAP(PCI_EXP_LNKCTL));
|
||||
val |= PCI_EXP_LNKCTL_RL;
|
||||
dw_pcie_writew_dbi(dw, EXPCAP(PCI_EXP_LNKCTL), val);
|
||||
|
||||
/* Wait for link retrain, 500ms must be enough for all link rates. */
|
||||
return read_poll_timeout(dw_pcie_readw_dbi, lnksta, !(lnksta & PCI_EXP_LNKSTA_LT),
|
||||
1000, 5 * PCIE_RESET_CONFIG_WAIT_MS * USEC_PER_MSEC,
|
||||
false, dw, EXPCAP(PCI_EXP_LNKSTA));
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable LTSSM of this controller and manually initiate the speed change.
|
||||
* Always return 0.
|
||||
*/
|
||||
static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
|
||||
{
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
int ret;
|
||||
|
||||
if (rcar->drvdata->ltssm_control) {
|
||||
ret = rcar->drvdata->ltssm_control(rcar, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return rcar->drvdata->speed_control(rcar);
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_stop_link(struct dw_pcie *dw)
|
||||
{
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
|
|
@ -197,6 +237,8 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
|
|||
return ret;
|
||||
}
|
||||
|
||||
reset_control_deassert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
|
||||
|
||||
if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) {
|
||||
reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
|
||||
/*
|
||||
|
|
@ -241,8 +283,74 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar)
|
|||
reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
|
||||
fsleep(1000);
|
||||
|
||||
if (rcar->drvdata->additional_common_init)
|
||||
rcar->drvdata->additional_common_init(rcar);
|
||||
return 0;
|
||||
|
||||
err_unprepare:
|
||||
reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
|
||||
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_gen4_v4h_v4m_pcie_init(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct dw_pcie *dw = &rcar->dw;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* R-Car Gen4 common initialization. */
|
||||
ret = rcar_gen4_pcie_common_init(rcar);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* R-Car V4H and V4M specific additional initialization. */
|
||||
val = dw_pcie_readl_dbi(dw, PORT_LANE_SKEW);
|
||||
val &= ~PORT_LANE_SKEW_INSERT_MASK;
|
||||
if (dw->num_lanes < 4)
|
||||
val |= BIT(6);
|
||||
dw_pcie_writel_dbi(dw, PORT_LANE_SKEW, val);
|
||||
|
||||
val = readl(rcar->base + PCIEPWRMNGCTRL);
|
||||
val |= APP_CLK_REQ_N | APP_CLK_PM_EN;
|
||||
writel(val, rcar->base + PCIEPWRMNGCTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen5_pcie_init(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct dw_pcie *dw = &rcar->dw;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
/* R-Car Gen4 and Gen5 common initialization. */
|
||||
ret = rcar_gen4_pcie_common_init(rcar);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* R-Car Gen5 specific additional initialization. */
|
||||
ret = phy_init(rcar->phy);
|
||||
if (ret)
|
||||
goto err_unprepare;
|
||||
|
||||
dw_pcie_dbi_ro_wr_en(dw);
|
||||
|
||||
val = dw_pcie_readl_dbi(dw, PORT_LANE_SKEW);
|
||||
val &= ~PORT_LANE_SKEW_INSERT_MASK;
|
||||
if (dw->num_lanes < 8)
|
||||
val |= BIT(6);
|
||||
dw_pcie_writel_dbi(dw, PORT_LANE_SKEW, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(dw, MSICAP0);
|
||||
FIELD_MODIFY(MSICAP0_MMESCAP_MASK, &val, 4);
|
||||
dw_pcie_writel_dbi(dw, MSICAP0, val);
|
||||
|
||||
dw_pcie_dbi_ro_wr_dis(dw);
|
||||
|
||||
val = readl(rcar->base + PCIEPWRMNGCTRL);
|
||||
val |= APP_CLK_REQ_N | APP_CLK_PM_EN |
|
||||
APP_READY_ENTR_L23 | APP_REQ_ENTR_L1;
|
||||
writel(val, rcar->base + PCIEPWRMNGCTRL);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -257,9 +365,16 @@ static void rcar_gen4_pcie_common_deinit(struct rcar_gen4_pcie *rcar)
|
|||
struct dw_pcie *dw = &rcar->dw;
|
||||
|
||||
reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc);
|
||||
reset_control_assert(dw->core_rsts[DW_PCIE_CORE_RST].rstc);
|
||||
clk_bulk_disable_unprepare(DW_PCIE_NUM_CORE_CLKS, dw->core_clks);
|
||||
}
|
||||
|
||||
static void rcar_gen5_pcie_deinit(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
phy_exit(rcar->phy);
|
||||
rcar_gen4_pcie_common_deinit(rcar);
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_prepare(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct device *dev = rcar->dw.dev;
|
||||
|
|
@ -285,12 +400,29 @@ static void rcar_gen4_pcie_unprepare(struct rcar_gen4_pcie *rcar)
|
|||
|
||||
static int rcar_gen4_pcie_get_resources(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
struct device *dev = rcar->dw.dev;
|
||||
struct device_node *root_port;
|
||||
|
||||
rcar->phy_base = devm_platform_ioremap_resource_byname(rcar->pdev, "phy");
|
||||
if (IS_ERR(rcar->phy_base))
|
||||
return PTR_ERR(rcar->phy_base);
|
||||
if (IS_ERR(rcar->phy_base)) {
|
||||
rcar->phy_base = NULL;
|
||||
rcar->phy = devm_phy_get(dev, NULL);
|
||||
if (IS_ERR(rcar->phy))
|
||||
return PTR_ERR(rcar->phy);
|
||||
}
|
||||
|
||||
root_port = of_get_next_available_child(dev->of_node, NULL);
|
||||
if (root_port) {
|
||||
rcar->perst = of_reset_control_get_optional_exclusive(root_port, "perst");
|
||||
of_node_put(root_port);
|
||||
if (IS_ERR(rcar->perst))
|
||||
return dev_err_probe(dev, PTR_ERR(rcar->perst), "Failed to get PERST#\n");
|
||||
}
|
||||
|
||||
/* Renesas-specific registers */
|
||||
rcar->base = devm_platform_ioremap_resource_byname(rcar->pdev, "app");
|
||||
if (IS_ERR(rcar->base))
|
||||
reset_control_put(rcar->perst);
|
||||
|
||||
return PTR_ERR_OR_ZERO(rcar->base);
|
||||
}
|
||||
|
|
@ -411,6 +543,72 @@ static int rcar_gen4_pcie_host_msi_init(struct dw_pcie_rp *pp)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_enable_device(struct pci_host_bridge *bridge,
|
||||
struct pci_dev *dev)
|
||||
{
|
||||
/*
|
||||
* R-Car Gen4 PCIe controller has a hardware limitation of 256 Bytes
|
||||
* Max_Payload_Size (MPS). PCIe specification indicates that the MPS
|
||||
* must not exceed minimum MPS of any element along the packet path.
|
||||
* The controller reports Max_Payload_Size_Supported (MPSS) 256 Bytes
|
||||
* for header type 0 and 128 Bytes for header type 1. The PCIe core
|
||||
* will not allow MPS to be set higher than MPSS; warn here in case
|
||||
* something went very wrong in the core.
|
||||
*
|
||||
* For details, refer to chapter "104.1.1 Features" in either of:
|
||||
* R-Car S4 R19UH0161EJ0140 Rev.1.40 Jul. 31, 2026 or
|
||||
* R-Car V4H R19UH0186EJ0140 Rev.1.40 Aug. 7, 2026 or
|
||||
* R-Car V4M R19UH0217EJ0110 Rev.1.10 Jun. 30, 2026.
|
||||
*/
|
||||
WARN_ON(pcie_get_mps(dev) > 256);
|
||||
|
||||
/*
|
||||
* R-Car Gen4 Reference Manual, chapter 104.4.8 Usage notes for
|
||||
* MRRS (Max Read Request Size) states:
|
||||
*
|
||||
* Please set "Max Read Request Size" to 128 bytes or 256 bytes.
|
||||
* If "Max Read Request Size" is set to anything other than the
|
||||
* above, the transferred data will not match the expected value.
|
||||
*
|
||||
* This limitation also seems to apply to devices issuing MRd TLPs.
|
||||
* This limitation can be triggered by using non-HMB NVMe SSD with
|
||||
* Max_Read_Request_Size 512 Bytes, for example Crucial P5 Plus.
|
||||
* Any write into the SSD (MRd TLP issued by the SSD) longer than
|
||||
* 256 Bytes wraps around at 256 Byte boundary, and the same data
|
||||
* are written into the SSD starting at offset 0 and at 256 Bytes.
|
||||
*
|
||||
* Limit Max_Read_Request_Size to at most 256 Bytes for each
|
||||
* device connected to this PCIe controller to avoid this behavior.
|
||||
*
|
||||
* For details, refer to aforementioned chapter in either of:
|
||||
* R-Car S4 R19UH0161EJ0140 Rev.1.40 Jul. 31, 2026 or
|
||||
* R-Car V4H R19UH0186EJ0140 Rev.1.40 Aug. 7, 2026 or
|
||||
* R-Car V4M R19UH0217EJ0110 Rev.1.10 Jun. 30, 2026.
|
||||
*/
|
||||
bridge->no_inc_mrrs = 1;
|
||||
if (pcie_get_readrq(dev) > 256) {
|
||||
pci_info(dev, "Limiting MRRS to 256 bytes\n");
|
||||
pcie_set_readrq(dev, 256);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_host_perst_assert(struct dw_pcie_rp *pp, bool assert)
|
||||
{
|
||||
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
|
||||
if (dw->pe_rst) {
|
||||
gpiod_set_value_cansleep(dw->pe_rst, assert);
|
||||
} else {
|
||||
if (assert)
|
||||
reset_control_assert(rcar->perst);
|
||||
else
|
||||
reset_control_deassert(rcar->perst);
|
||||
}
|
||||
}
|
||||
|
||||
/* Host mode */
|
||||
static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
|
||||
{
|
||||
|
|
@ -418,9 +616,12 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
int ret;
|
||||
|
||||
gpiod_set_value_cansleep(dw->pe_rst, 1);
|
||||
if (pp->bridge)
|
||||
pp->bridge->enable_device = rcar_gen4_pcie_enable_device;
|
||||
|
||||
ret = rcar_gen4_pcie_common_init(rcar);
|
||||
rcar_gen4_pcie_host_perst_assert(pp, true);
|
||||
|
||||
ret = rcar->drvdata->init(rcar);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -439,12 +640,12 @@ static int rcar_gen4_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
|
||||
msleep(PCIE_T_PVPERL_MS); /* pe_rst requires 100msec delay */
|
||||
|
||||
gpiod_set_value_cansleep(dw->pe_rst, 0);
|
||||
rcar_gen4_pcie_host_perst_assert(pp, false);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
rcar_gen4_pcie_common_deinit(rcar);
|
||||
rcar->drvdata->deinit(rcar);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -453,8 +654,8 @@ static void rcar_gen4_pcie_host_deinit(struct dw_pcie_rp *pp)
|
|||
struct dw_pcie *dw = to_dw_pcie_from_pp(pp);
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
|
||||
gpiod_set_value_cansleep(dw->pe_rst, 1);
|
||||
rcar_gen4_pcie_common_deinit(rcar);
|
||||
rcar_gen4_pcie_host_perst_assert(pp, true);
|
||||
rcar->drvdata->deinit(rcar);
|
||||
}
|
||||
|
||||
static const struct dw_pcie_host_ops rcar_gen4_pcie_host_ops = {
|
||||
|
|
@ -470,6 +671,8 @@ static int rcar_gen4_add_dw_pcie_rp(struct rcar_gen4_pcie *rcar)
|
|||
return -ENODEV;
|
||||
|
||||
pp->num_vectors = MAX_MSI_IRQS;
|
||||
/* Reserve an iATU window for the generic PME_Turn_Off implementation. */
|
||||
pp->use_atu_msg = true;
|
||||
pp->ops = &rcar_gen4_pcie_host_ops;
|
||||
|
||||
return dw_pcie_host_init(pp);
|
||||
|
|
@ -487,7 +690,9 @@ static int rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep)
|
|||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
int ret;
|
||||
|
||||
ret = rcar_gen4_pcie_common_init(rcar);
|
||||
writel(0, rcar->base + PCIEDMAINTSTSEN);
|
||||
|
||||
ret = rcar->drvdata->init(rcar);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -496,10 +701,13 @@ static int rcar_gen4_pcie_ep_pre_init(struct dw_pcie_ep *ep)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_ep_deinit(struct rcar_gen4_pcie *rcar)
|
||||
static void rcar_gen4_pcie_ep_post_deinit(struct dw_pcie_ep *ep)
|
||||
{
|
||||
struct dw_pcie *dw = to_dw_pcie_from_ep(ep);
|
||||
struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
|
||||
|
||||
writel(0, rcar->base + PCIEDMAINTSTSEN);
|
||||
rcar_gen4_pcie_common_deinit(rcar);
|
||||
rcar->drvdata->deinit(rcar);
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
||||
|
|
@ -552,6 +760,7 @@ static unsigned int rcar_gen4_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep,
|
|||
|
||||
static const struct dw_pcie_ep_ops pcie_ep_ops = {
|
||||
.pre_init = rcar_gen4_pcie_ep_pre_init,
|
||||
.post_deinit = rcar_gen4_pcie_ep_post_deinit,
|
||||
.raise_irq = rcar_gen4_pcie_ep_raise_irq,
|
||||
.get_features = rcar_gen4_pcie_ep_get_features,
|
||||
.get_dbi_offset = rcar_gen4_pcie_ep_get_dbi_offset,
|
||||
|
|
@ -570,16 +779,13 @@ static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
|
|||
ep->ops = &pcie_ep_ops;
|
||||
|
||||
ret = dw_pcie_ep_init(ep);
|
||||
if (ret) {
|
||||
rcar_gen4_pcie_ep_deinit(rcar);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = dw_pcie_ep_init_registers(ep);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize DWC endpoint registers\n");
|
||||
dw_pcie_ep_deinit(ep);
|
||||
rcar_gen4_pcie_ep_deinit(rcar);
|
||||
}
|
||||
|
||||
pci_epc_init_notify(ep->epc);
|
||||
|
|
@ -590,7 +796,6 @@ static int rcar_gen4_add_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
|
|||
static void rcar_gen4_remove_dw_pcie_ep(struct rcar_gen4_pcie *rcar)
|
||||
{
|
||||
dw_pcie_ep_deinit(&rcar->dw.ep);
|
||||
rcar_gen4_pcie_ep_deinit(rcar);
|
||||
}
|
||||
|
||||
/* Common */
|
||||
|
|
@ -625,7 +830,7 @@ static int rcar_gen4_pcie_probe(struct platform_device *pdev)
|
|||
|
||||
err = rcar_gen4_pcie_prepare(rcar);
|
||||
if (err)
|
||||
return err;
|
||||
goto err_prepare;
|
||||
|
||||
err = rcar_gen4_add_dw_pcie(rcar);
|
||||
if (err)
|
||||
|
|
@ -636,6 +841,9 @@ static int rcar_gen4_pcie_probe(struct platform_device *pdev)
|
|||
err_unprepare:
|
||||
rcar_gen4_pcie_unprepare(rcar);
|
||||
|
||||
err_prepare:
|
||||
reset_control_put(rcar->perst);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -659,6 +867,7 @@ static void rcar_gen4_pcie_remove(struct platform_device *pdev)
|
|||
|
||||
rcar_gen4_remove_dw_pcie(rcar);
|
||||
rcar_gen4_pcie_unprepare(rcar);
|
||||
reset_control_put(rcar->perst);
|
||||
}
|
||||
|
||||
static int r8a779f0_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable)
|
||||
|
|
@ -683,20 +892,26 @@ static int r8a779f0_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_additional_common_init(struct rcar_gen4_pcie *rcar)
|
||||
static int rcar_gen5_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable)
|
||||
{
|
||||
struct dw_pcie *dw = &rcar->dw;
|
||||
u32 val;
|
||||
|
||||
val = dw_pcie_readl_dbi(dw, PCIE_PORT_LANE_SKEW);
|
||||
val &= ~PORT_LANE_SKEW_INSERT_MASK;
|
||||
if (dw->num_lanes < 4)
|
||||
val |= BIT(6);
|
||||
dw_pcie_writel_dbi(dw, PCIE_PORT_LANE_SKEW, val);
|
||||
val = readl(rcar->base + PCIERSTCTRL1);
|
||||
if (enable) {
|
||||
val |= APP_LTSSM_ENABLE;
|
||||
val &= ~APP_HOLD_PHY_RST;
|
||||
} else {
|
||||
val &= ~APP_LTSSM_ENABLE;
|
||||
val |= APP_HOLD_PHY_RST;
|
||||
}
|
||||
writel(val, rcar->base + PCIERSTCTRL1);
|
||||
|
||||
val = readl(rcar->base + PCIEPWRMNGCTRL);
|
||||
val |= APP_CLK_REQ_N | APP_CLK_PM_EN;
|
||||
writel(val, rcar->base + PCIEPWRMNGCTRL);
|
||||
if (enable)
|
||||
phy_power_on(rcar->phy);
|
||||
else
|
||||
phy_power_off(rcar->phy);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcar_gen4_pcie_phy_reg_update_bits(struct rcar_gen4_pcie *rcar,
|
||||
|
|
@ -803,9 +1018,9 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable
|
|||
return 0;
|
||||
}
|
||||
|
||||
val = dw_pcie_readl_dbi(dw, PCIE_PORT_FORCE);
|
||||
val |= PORT_FORCE_DO_DESKEW_FOR_SRIS;
|
||||
dw_pcie_writel_dbi(dw, PCIE_PORT_FORCE, val);
|
||||
val = dw_pcie_readl_dbi(dw, PORT_FORCE_LINK);
|
||||
val |= PORT_FORCE_LINK_DDFS;
|
||||
dw_pcie_writel_dbi(dw, PORT_FORCE_LINK, val);
|
||||
|
||||
val = readl(rcar->base + PCIEMSR0);
|
||||
val |= APP_SRIS_MODE;
|
||||
|
|
@ -849,28 +1064,66 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_suspend_noirq(struct device *dev)
|
||||
{
|
||||
struct rcar_gen4_pcie *rcar = dev_get_drvdata(dev);
|
||||
|
||||
if (rcar->drvdata->mode != DW_PCIE_RC_TYPE)
|
||||
return 0;
|
||||
|
||||
return dw_pcie_suspend_noirq(&rcar->dw);
|
||||
}
|
||||
|
||||
static int rcar_gen4_pcie_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct rcar_gen4_pcie *rcar = dev_get_drvdata(dev);
|
||||
|
||||
if (rcar->drvdata->mode != DW_PCIE_RC_TYPE)
|
||||
return 0;
|
||||
|
||||
return dw_pcie_resume_noirq(&rcar->dw);
|
||||
}
|
||||
|
||||
static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie = {
|
||||
.init = rcar_gen4_pcie_common_init,
|
||||
.deinit = rcar_gen4_pcie_common_deinit,
|
||||
.ltssm_control = r8a779f0_pcie_ltssm_control,
|
||||
.speed_control = rcar_gen4_pcie_speed_control,
|
||||
.mode = DW_PCIE_RC_TYPE,
|
||||
};
|
||||
|
||||
static struct rcar_gen4_pcie_drvdata drvdata_r8a779f0_pcie_ep = {
|
||||
.init = rcar_gen4_pcie_common_init,
|
||||
.deinit = rcar_gen4_pcie_common_deinit,
|
||||
.ltssm_control = r8a779f0_pcie_ltssm_control,
|
||||
.speed_control = rcar_gen4_pcie_speed_control,
|
||||
.mode = DW_PCIE_EP_TYPE,
|
||||
};
|
||||
|
||||
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie = {
|
||||
.additional_common_init = rcar_gen4_pcie_additional_common_init,
|
||||
.init = rcar_gen4_v4h_v4m_pcie_init,
|
||||
.deinit = rcar_gen4_pcie_common_deinit,
|
||||
.ltssm_control = rcar_gen4_pcie_ltssm_control,
|
||||
.speed_control = rcar_gen4_pcie_speed_control,
|
||||
.mode = DW_PCIE_RC_TYPE,
|
||||
};
|
||||
|
||||
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen4_pcie_ep = {
|
||||
.additional_common_init = rcar_gen4_pcie_additional_common_init,
|
||||
.init = rcar_gen4_v4h_v4m_pcie_init,
|
||||
.deinit = rcar_gen4_pcie_common_deinit,
|
||||
.ltssm_control = rcar_gen4_pcie_ltssm_control,
|
||||
.speed_control = rcar_gen4_pcie_speed_control,
|
||||
.mode = DW_PCIE_EP_TYPE,
|
||||
};
|
||||
|
||||
static struct rcar_gen4_pcie_drvdata drvdata_rcar_gen5_pcie = {
|
||||
.init = rcar_gen5_pcie_init,
|
||||
.deinit = rcar_gen5_pcie_deinit,
|
||||
.ltssm_control = rcar_gen5_pcie_ltssm_control,
|
||||
.speed_control = rcar_gen5_pcie_speed_control,
|
||||
.mode = DW_PCIE_RC_TYPE,
|
||||
};
|
||||
|
||||
static const struct of_device_id rcar_gen4_pcie_of_match[] = {
|
||||
{
|
||||
.compatible = "renesas,r8a779f0-pcie",
|
||||
|
|
@ -888,14 +1141,23 @@ static const struct of_device_id rcar_gen4_pcie_of_match[] = {
|
|||
.compatible = "renesas,rcar-gen4-pcie-ep",
|
||||
.data = &drvdata_rcar_gen4_pcie_ep,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,rcar-gen5-pcie4",
|
||||
.data = &drvdata_rcar_gen5_pcie,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_gen4_pcie_of_match);
|
||||
|
||||
static DEFINE_NOIRQ_DEV_PM_OPS(rcar_gen4_pcie_pm_ops,
|
||||
rcar_gen4_pcie_suspend_noirq,
|
||||
rcar_gen4_pcie_resume_noirq);
|
||||
|
||||
static struct platform_driver rcar_gen4_pcie_driver = {
|
||||
.driver = {
|
||||
.name = "pcie-rcar-gen4",
|
||||
.of_match_table = rcar_gen4_pcie_of_match,
|
||||
.pm = pm_sleep_ptr(&rcar_gen4_pcie_pm_ops),
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
.probe = rcar_gen4_pcie_probe,
|
||||
|
|
|
|||
|
|
@ -12,9 +12,12 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ static int tegra194_acpi_init(struct pci_config_window *cfg)
|
|||
static void atu_reg_write(struct tegra194_pcie_ecam *pcie_ecam, int index,
|
||||
u32 val, u32 reg)
|
||||
{
|
||||
u32 offset = PCIE_ATU_UNROLL_BASE(PCIE_ATU_REGION_DIR_OB, index) +
|
||||
PCIE_ATU_VIEWPORT_BASE;
|
||||
u32 offset = ATU_UNROLL_BASE(ATU_REGION_DIR_OB, index) +
|
||||
ATU_VIEWPORT_BASE;
|
||||
|
||||
writel(val, pcie_ecam->iatu_base + offset + reg);
|
||||
}
|
||||
|
|
@ -50,17 +50,17 @@ static void program_outbound_atu(struct tegra194_pcie_ecam *pcie_ecam,
|
|||
u64 pci_addr, u64 size)
|
||||
{
|
||||
atu_reg_write(pcie_ecam, index, lower_32_bits(cpu_addr),
|
||||
PCIE_ATU_LOWER_BASE);
|
||||
ATU_LOWER_BASE);
|
||||
atu_reg_write(pcie_ecam, index, upper_32_bits(cpu_addr),
|
||||
PCIE_ATU_UPPER_BASE);
|
||||
ATU_UPPER_BASE);
|
||||
atu_reg_write(pcie_ecam, index, lower_32_bits(pci_addr),
|
||||
PCIE_ATU_LOWER_TARGET);
|
||||
ATU_LOWER_TARGET);
|
||||
atu_reg_write(pcie_ecam, index, lower_32_bits(cpu_addr + size - 1),
|
||||
PCIE_ATU_LIMIT);
|
||||
ATU_LIMIT);
|
||||
atu_reg_write(pcie_ecam, index, upper_32_bits(pci_addr),
|
||||
PCIE_ATU_UPPER_TARGET);
|
||||
atu_reg_write(pcie_ecam, index, type, PCIE_ATU_REGION_CTRL1);
|
||||
atu_reg_write(pcie_ecam, index, PCIE_ATU_ENABLE, PCIE_ATU_REGION_CTRL2);
|
||||
ATU_UPPER_TARGET);
|
||||
atu_reg_write(pcie_ecam, index, type, ATU_REGION_CTRL1);
|
||||
atu_reg_write(pcie_ecam, index, ATU_ENABLE, ATU_REGION_CTRL2);
|
||||
}
|
||||
|
||||
static void __iomem *tegra194_map_bus(struct pci_bus *bus,
|
||||
|
|
@ -81,8 +81,8 @@ static void __iomem *tegra194_map_bus(struct pci_bus *bus,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
|
||||
PCIE_ATU_FUNC(PCI_FUNC(devfn));
|
||||
busdev = ATU_BUS(bus->number) | ATU_DEV(PCI_SLOT(devfn)) |
|
||||
ATU_FUNC(PCI_FUNC(devfn));
|
||||
|
||||
if (bus->parent->number == cfg->busr.start) {
|
||||
if (PCI_SLOT(devfn) == 0)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/resource.h>
|
||||
#include <linux/types.h>
|
||||
|
|
@ -385,9 +386,9 @@ static irqreturn_t tegra_pcie_rp_irq_handler(int irq, void *arg)
|
|||
val |= APPL_CAR_RESET_OVRD_CYA_OVERRIDE_CORE_RST_N;
|
||||
appl_writel(pcie, val, APPL_CAR_RESET_OVRD);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
val |= PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
||||
val = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
|
||||
val |= SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -621,15 +622,15 @@ static inline u32 event_counter_prog(struct tegra_pcie_dw *pcie, u32 event)
|
|||
u32 val;
|
||||
|
||||
val = dw_pcie_readl_dbi(&pcie->pci, pcie->ras_des_cap +
|
||||
PCIE_RAS_DES_EVENT_COUNTER_CONTROL);
|
||||
val &= ~(EVENT_COUNTER_EVENT_SEL_MASK << EVENT_COUNTER_EVENT_SEL_SHIFT);
|
||||
val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT;
|
||||
val |= event << EVENT_COUNTER_EVENT_SEL_SHIFT;
|
||||
val |= EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT;
|
||||
RAS_DES_EVENT_COUNTER_CONTROL);
|
||||
val &= ~EVENT_COUNTER_EVENT_SEL_MASK;
|
||||
val |= FIELD_PREP(EVENT_COUNTER_GROUP_SEL_MASK, EVENT_COUNTER_GROUP_5);
|
||||
val |= FIELD_PREP(EVENT_COUNTER_EVENT_SEL_MASK, event);
|
||||
val |= FIELD_PREP(EVENT_COUNTER_ENABLE_MASK, EVENT_COUNTER_ENABLE_ALL);
|
||||
dw_pcie_writel_dbi(&pcie->pci, pcie->ras_des_cap +
|
||||
PCIE_RAS_DES_EVENT_COUNTER_CONTROL, val);
|
||||
RAS_DES_EVENT_COUNTER_CONTROL, val);
|
||||
val = dw_pcie_readl_dbi(&pcie->pci, pcie->ras_des_cap +
|
||||
PCIE_RAS_DES_EVENT_COUNTER_DATA);
|
||||
RAS_DES_EVENT_COUNTER_DATA);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
|
@ -657,14 +658,14 @@ static int aspm_state_cnt(struct seq_file *s, void *data)
|
|||
|
||||
/* Clear all counters */
|
||||
dw_pcie_writel_dbi(&pcie->pci, pcie->ras_des_cap +
|
||||
PCIE_RAS_DES_EVENT_COUNTER_CONTROL,
|
||||
RAS_DES_EVENT_COUNTER_CONTROL,
|
||||
EVENT_COUNTER_ALL_CLEAR);
|
||||
|
||||
/* Re-enable counting */
|
||||
val = EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT;
|
||||
val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT;
|
||||
val = FIELD_PREP(EVENT_COUNTER_ENABLE_MASK, EVENT_COUNTER_ENABLE_ALL);
|
||||
val |= FIELD_PREP(EVENT_COUNTER_GROUP_SEL_MASK, EVENT_COUNTER_GROUP_5);
|
||||
dw_pcie_writel_dbi(&pcie->pci, pcie->ras_des_cap +
|
||||
PCIE_RAS_DES_EVENT_COUNTER_CONTROL, val);
|
||||
RAS_DES_EVENT_COUNTER_CONTROL, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -680,10 +681,10 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie)
|
|||
PCI_EXT_CAP_ID_VNDR);
|
||||
|
||||
/* Enable ASPM counters */
|
||||
val = EVENT_COUNTER_ENABLE_ALL << EVENT_COUNTER_ENABLE_SHIFT;
|
||||
val |= EVENT_COUNTER_GROUP_5 << EVENT_COUNTER_GROUP_SEL_SHIFT;
|
||||
val = FIELD_PREP(EVENT_COUNTER_ENABLE_MASK, EVENT_COUNTER_ENABLE_ALL);
|
||||
val |= FIELD_PREP(EVENT_COUNTER_GROUP_SEL_MASK, EVENT_COUNTER_GROUP_5);
|
||||
dw_pcie_writel_dbi(pci, pcie->ras_des_cap +
|
||||
PCIE_RAS_DES_EVENT_COUNTER_CONTROL, val);
|
||||
RAS_DES_EVENT_COUNTER_CONTROL, val);
|
||||
|
||||
/* Program T_cmrt and T_pwr_on values */
|
||||
val = dw_pcie_readl_dbi(pci, l1ss + PCI_L1SS_CAP);
|
||||
|
|
@ -713,13 +714,13 @@ static void init_host_aspm(struct tegra_pcie_dw *pcie)
|
|||
}
|
||||
|
||||
/* Program L0s and L1 entrance latencies */
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_PORT_AFR);
|
||||
val &= ~PORT_AFR_L0S_ENTRANCE_LAT_MASK;
|
||||
val |= (pcie->aspm_l0s_enter_lat << PORT_AFR_L0S_ENTRANCE_LAT_SHIFT);
|
||||
val &= ~PORT_AFR_L1_ENTRANCE_LAT_MASK;
|
||||
val |= (pcie->aspm_l1_enter_lat << PORT_AFR_L1_ENTRANCE_LAT_SHIFT);
|
||||
val |= PORT_AFR_ENTER_ASPM;
|
||||
dw_pcie_writel_dbi(pci, PCIE_PORT_AFR, val);
|
||||
val = dw_pcie_readl_dbi(pci, PORT_AFAC);
|
||||
val &= ~PORT_AFAC_L0S_ELM;
|
||||
val |= FIELD_PREP(PORT_AFAC_L0S_ELM, pcie->aspm_l0s_enter_lat);
|
||||
val &= ~PORT_AFAC_L1_ELM;
|
||||
val |= FIELD_PREP(PORT_AFAC_L1_ELM, pcie->aspm_l1_enter_lat);
|
||||
val |= PORT_AFAC_ENTER_ASPM;
|
||||
dw_pcie_writel_dbi(pci, PORT_AFAC, val);
|
||||
}
|
||||
|
||||
static void init_debugfs(struct tegra_pcie_dw *pcie)
|
||||
|
|
@ -870,29 +871,29 @@ static void config_gen3_gen4_eq_presets(struct tegra_pcie_dw *pcie)
|
|||
dw_pcie_writeb_dbi(pci, offset + i, val);
|
||||
}
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
|
||||
val &= ~GEN3_CTRL_RATE_SHADOW_SEL_MASK;
|
||||
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
|
||||
FIELD_MODIFY(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, &val, 0x3ff);
|
||||
val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE;
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val);
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_EQ_CTRL);
|
||||
FIELD_MODIFY(GEN3_EQ_CTRL_PSET_REQ_VEC, &val, 0x3ff);
|
||||
val &= ~GEN3_EQ_CTRL_FB_MODE;
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_CTRL, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
|
||||
val |= (0x1 << GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT);
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
|
||||
val &= ~GEN3_CTRL_RATE_SHADOW_SEL_MASK;
|
||||
val |= FIELD_PREP(GEN3_CTRL_RATE_SHADOW_SEL_MASK, 0x1);
|
||||
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF);
|
||||
FIELD_MODIFY(GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC, &val,
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_EQ_CTRL);
|
||||
FIELD_MODIFY(GEN3_EQ_CTRL_PSET_REQ_VEC, &val,
|
||||
pcie->of_data->gen4_preset_vec);
|
||||
val &= ~GEN3_EQ_CONTROL_OFF_FB_MODE;
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, val);
|
||||
val &= ~GEN3_EQ_CTRL_FB_MODE;
|
||||
dw_pcie_writel_dbi(pci, GEN3_EQ_CTRL, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
val &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK;
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
|
||||
val &= ~GEN3_CTRL_RATE_SHADOW_SEL_MASK;
|
||||
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
|
||||
}
|
||||
|
||||
static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
|
||||
|
|
@ -940,9 +941,9 @@ static int tegra_pcie_dw_host_init(struct dw_pcie_rp *pp)
|
|||
init_host_aspm(pcie);
|
||||
|
||||
if (!pcie->of_data->has_l1ss_exit_fix) {
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
|
||||
val &= ~GEN3_CTRL_GEN3_ZRXDC_NONCOMPL;
|
||||
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
|
||||
}
|
||||
|
||||
if (pcie->update_fc_fixup) {
|
||||
|
|
@ -1869,9 +1870,9 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
|
|||
pci_epc_deinit_notify(pcie->pci.ep.epc);
|
||||
dw_pcie_ep_cleanup(&pcie->pci.ep);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
||||
val &= ~PORT_LOGIC_SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
||||
val = dw_pcie_readl_dbi(pci, LINK_WIDTH_SPEED_CTRL);
|
||||
val &= ~SPEED_CHANGE;
|
||||
dw_pcie_writel_dbi(pci, LINK_WIDTH_SPEED_CTRL, val);
|
||||
|
||||
if (pcie->update_fc_fixup) {
|
||||
val = dw_pcie_readl_dbi(pci, CFG_TIMER_CTRL_MAX_FUNC_NUM_OFF);
|
||||
|
|
@ -1884,9 +1885,9 @@ static void pex_ep_event_pex_rst_deassert(struct tegra_pcie_dw *pcie)
|
|||
init_host_aspm(pcie);
|
||||
|
||||
if (!pcie->of_data->has_l1ss_exit_fix) {
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF);
|
||||
val &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL;
|
||||
dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, val);
|
||||
val = dw_pcie_readl_dbi(pci, GEN3_CTRL);
|
||||
val &= ~GEN3_CTRL_GEN3_ZRXDC_NONCOMPL;
|
||||
dw_pcie_writel_dbi(pci, GEN3_CTRL, val);
|
||||
}
|
||||
|
||||
pcie->pcie_cap_base = dw_pcie_find_capability(&pcie->pci,
|
||||
|
|
|
|||
|
|
@ -52,18 +52,18 @@ static int ultrarisc_pcie_host_init(struct dw_pcie_rp *pp)
|
|||
val &= ~FAST_LINK_MODE;
|
||||
dw_pcie_writel_dbi(pci, PCIE_CUS_CORE, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM);
|
||||
val = dw_pcie_readl_dbi(pci, TIMER_CTRL_MAX_FUNC_NUM);
|
||||
FIELD_MODIFY(PORT_FLT_SF_MASK, &val, PORT_FLT_SF_VAL_64);
|
||||
dw_pcie_writel_dbi(pci, PCIE_TIMER_CTRL_MAX_FUNC_NUM, val);
|
||||
dw_pcie_writel_dbi(pci, TIMER_CTRL_MAX_FUNC_NUM, val);
|
||||
|
||||
cap_exp = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
|
||||
val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_LNKCTL2);
|
||||
FIELD_MODIFY(PCI_EXP_LNKCTL2_TLS, &val, PCI_EXP_LNKCTL2_TLS_16_0GT);
|
||||
dw_pcie_writel_dbi(pci, cap_exp + PCI_EXP_LNKCTL2, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, PCIE_PORT_FORCE);
|
||||
FIELD_MODIFY(PORT_LINK_NUM_MASK, &val, 0);
|
||||
dw_pcie_writel_dbi(pci, PCIE_PORT_FORCE, val);
|
||||
val = dw_pcie_readl_dbi(pci, PORT_FORCE_LINK);
|
||||
FIELD_MODIFY(PORT_FORCE_LINK_NUM_MASK, &val, 0);
|
||||
dw_pcie_writel_dbi(pci, PORT_FORCE_LINK, val);
|
||||
|
||||
val = dw_pcie_readl_dbi(pci, cap_exp + PCI_EXP_DEVCTL2);
|
||||
FIELD_MODIFY(PCI_EXP_DEVCTL2_COMP_TIMEOUT, &val,
|
||||
|
|
@ -154,6 +154,12 @@ static int ultrarisc_pcie_probe(struct platform_device *pdev)
|
|||
|
||||
pp = &pci->pp;
|
||||
|
||||
/*
|
||||
* DP1000 routes MEM transactions without outbound iATU windows, which
|
||||
* avoids the 16-window/4GB-per-window aperture limit.
|
||||
*/
|
||||
pp->bypass_ob_mem_iatu = true;
|
||||
|
||||
platform_set_drvdata(pdev, ultra);
|
||||
|
||||
ret = ultrarisc_pcie_init_clks(ultra);
|
||||
|
|
|
|||
|
|
@ -1864,28 +1864,25 @@ static int advk_pcie_probe(struct platform_device *pdev)
|
|||
ret = advk_sw_pci_bridge_init(pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register emulated root PCI bridge\n");
|
||||
return ret;
|
||||
goto err_disable_phy;
|
||||
}
|
||||
|
||||
ret = advk_pcie_init_irq_domain(pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize irq\n");
|
||||
return ret;
|
||||
goto err_cleanup_bridge;
|
||||
}
|
||||
|
||||
ret = advk_pcie_init_msi_irq_domain(pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize irq\n");
|
||||
advk_pcie_remove_irq_domain(pcie);
|
||||
return ret;
|
||||
goto err_remove_irq_domain;
|
||||
}
|
||||
|
||||
ret = advk_pcie_init_rp_irq_domain(pcie);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize irq\n");
|
||||
advk_pcie_remove_msi_irq_domain(pcie);
|
||||
advk_pcie_remove_irq_domain(pcie);
|
||||
return ret;
|
||||
goto err_remove_msi_irq_domain;
|
||||
}
|
||||
|
||||
bridge->sysdata = pcie;
|
||||
|
|
@ -1893,14 +1890,22 @@ static int advk_pcie_probe(struct platform_device *pdev)
|
|||
bridge->map_irq = advk_pcie_map_irq;
|
||||
|
||||
ret = pci_host_probe(bridge);
|
||||
if (ret < 0) {
|
||||
advk_pcie_remove_rp_irq_domain(pcie);
|
||||
advk_pcie_remove_msi_irq_domain(pcie);
|
||||
advk_pcie_remove_irq_domain(pcie);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto err_remove_rp_irq_domain;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_rp_irq_domain:
|
||||
advk_pcie_remove_rp_irq_domain(pcie);
|
||||
err_remove_msi_irq_domain:
|
||||
advk_pcie_remove_msi_irq_domain(pcie);
|
||||
err_remove_irq_domain:
|
||||
advk_pcie_remove_irq_domain(pcie);
|
||||
err_cleanup_bridge:
|
||||
pci_bridge_emul_cleanup(&pcie->bridge);
|
||||
err_disable_phy:
|
||||
advk_pcie_disable_phy(pcie);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void advk_pcie_remove(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -1235,6 +1235,11 @@ static void mvebu_pcie_port_clk_put(void *data)
|
|||
clk_put(port->clk);
|
||||
}
|
||||
|
||||
static void mvebu_pcie_port_of_node_put(void *data)
|
||||
{
|
||||
of_node_put(data);
|
||||
}
|
||||
|
||||
static int mvebu_pcie_parse_port(struct mvebu_pcie *pcie,
|
||||
struct mvebu_pcie_port *port, struct device_node *child)
|
||||
{
|
||||
|
|
@ -1451,7 +1456,6 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
|
|||
struct mvebu_pcie *pcie;
|
||||
struct pci_host_bridge *bridge;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child;
|
||||
int num, i, ret;
|
||||
|
||||
bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct mvebu_pcie));
|
||||
|
|
@ -1473,18 +1477,20 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
for_each_available_child_of_node(np, child) {
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
struct mvebu_pcie_port *port = &pcie->ports[i];
|
||||
|
||||
ret = mvebu_pcie_parse_port(pcie, port, child);
|
||||
if (ret < 0) {
|
||||
of_node_put(child);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (ret == 0) {
|
||||
else if (ret == 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
port->dn = child;
|
||||
port->dn = of_node_get(child);
|
||||
ret = devm_add_action_or_reset(dev, mvebu_pcie_port_of_node_put,
|
||||
child);
|
||||
if (ret)
|
||||
return ret;
|
||||
i++;
|
||||
}
|
||||
pcie->nports = i;
|
||||
|
|
@ -1492,6 +1498,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
|
|||
for (i = 0; i < pcie->nports; i++) {
|
||||
struct mvebu_pcie_port *port = &pcie->ports[i];
|
||||
int irq = port->intx_irq;
|
||||
struct device_node *child;
|
||||
|
||||
child = port->dn;
|
||||
if (!child)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/phy/phy.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
|||
|
|
@ -320,18 +320,11 @@ static void xgene_pcie_linkup(struct xgene_pcie *port,
|
|||
static int xgene_pcie_init_port(struct xgene_pcie *port)
|
||||
{
|
||||
struct device *dev = port->dev;
|
||||
int rc;
|
||||
|
||||
port->clk = clk_get(dev, NULL);
|
||||
port->clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(port->clk)) {
|
||||
dev_err(dev, "clock not available\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = clk_prepare_enable(port->clk);
|
||||
if (rc) {
|
||||
dev_err(dev, "clock enable failed\n");
|
||||
return rc;
|
||||
return PTR_ERR(port->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -761,16 +761,21 @@ static int aspeed_pcie_port_init(struct aspeed_pcie_port *port)
|
|||
port->slot);
|
||||
|
||||
ret = phy_init(port->phy);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
clk_disable_unprepare(port->clk);
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to init phy pcie for slot (%d)\n",
|
||||
port->slot);
|
||||
}
|
||||
|
||||
ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
phy_exit(port->phy);
|
||||
clk_disable_unprepare(port->clk);
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to set phy mode for slot (%d)\n",
|
||||
port->slot);
|
||||
}
|
||||
|
||||
reset_control_deassert(port->perst);
|
||||
msleep(PCIE_RESET_CONFIG_WAIT_MS);
|
||||
|
|
|
|||
|
|
@ -277,6 +277,9 @@ struct inbound_win {
|
|||
u64 cpu_addr;
|
||||
};
|
||||
|
||||
/* QUIRKS */
|
||||
#define BQUIRK(pcie, quirk) ((pcie)->cfg->quirks & CFG_QUIRK_ ## quirk)
|
||||
|
||||
/*
|
||||
* The RESCAL block is tied to PCIe controller #1, regardless of the number of
|
||||
* controllers, and turning off PCIe controller #1 prevents access to the RESCAL
|
||||
|
|
@ -285,17 +288,38 @@ struct inbound_win {
|
|||
* or a hang (AXI).
|
||||
*/
|
||||
#define CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN BIT(0)
|
||||
/* The outbound windows must be within the 0-4GB region */
|
||||
#define CFG_QUIRK_OB_WIN_32BIT_ADDR BIT(1)
|
||||
/* Each of the outbound windows size must be <= SZ_128M */
|
||||
#define CFG_QUIRK_OB_WIN_MAXSZ_128MB BIT(2)
|
||||
/* PCIe HW can only access config space with 32bit R/W */
|
||||
#define CFG_QUIRK_32BIT_PCI_OPS BIT(3)
|
||||
/* PCIe HW does not have an internal bus timer */
|
||||
#define CFG_QUIRK_NO_RGR1_TIMER BIT(4)
|
||||
/* PCIe PERST# must be asserted before internal bridge turned on */
|
||||
#define CFG_QUIRK_EARLY_PERST_ASSERT BIT(5)
|
||||
/* PCIe SW PERST behavior only works up to a specific HW version */
|
||||
#define CFG_QUIRK_PERST_PCIE_REV_CUTOFF BIT(6)
|
||||
|
||||
/* FLAGS */
|
||||
#define BFLAG(pcie, flag) ((pcie)->cfg->flags & CFG_FLG_ ## flag)
|
||||
|
||||
/* The PCIe HW registers have phy control of a "RESCAL" reset block */
|
||||
#define CFG_FLG_HAS_PHY BIT(0)
|
||||
/* The SoC PCIe HW can dump to the console PCIe error info */
|
||||
#define CFG_FLG_HAS_ERR_REPORT BIT(1)
|
||||
/* SoC uses a Broadcom variant of the MIPS-1 ISA */
|
||||
#define CFG_FLG_IS_BMIPS BIT(2)
|
||||
|
||||
struct pcie_cfg_data {
|
||||
const int *offsets;
|
||||
const enum pcie_soc_base soc_base;
|
||||
const bool has_phy;
|
||||
const u32 quirks;
|
||||
enum pcie_soc_base soc_base;
|
||||
u32 flags;
|
||||
u32 quirks;
|
||||
u8 num_inbound_wins;
|
||||
int (*perst_set)(struct brcm_pcie *pcie, u32 val);
|
||||
int (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
|
||||
int (*post_setup)(struct brcm_pcie *pcie);
|
||||
bool has_err_report;
|
||||
};
|
||||
|
||||
struct subdev_regulators {
|
||||
|
|
@ -346,24 +370,19 @@ struct brcm_pcie {
|
|||
spinlock_t bridge_lock;
|
||||
};
|
||||
|
||||
static inline bool is_bmips(const struct brcm_pcie *pcie)
|
||||
{
|
||||
return pcie->cfg->soc_base == BCM7435 || pcie->cfg->soc_base == BCM7425;
|
||||
}
|
||||
|
||||
static int brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (pcie->cfg->has_err_report)
|
||||
if (BFLAG(pcie, HAS_ERR_REPORT))
|
||||
spin_lock_irqsave(&pcie->bridge_lock, flags);
|
||||
|
||||
ret = pcie->cfg->bridge_sw_init_set(pcie, val);
|
||||
/* If we fail, assume the bridge is in reset (off) */
|
||||
pcie->bridge_in_reset = ret ? true : val;
|
||||
|
||||
if (pcie->cfg->has_err_report)
|
||||
if (BFLAG(pcie, HAS_ERR_REPORT))
|
||||
spin_unlock_irqrestore(&pcie->bridge_lock, flags);
|
||||
|
||||
return ret;
|
||||
|
|
@ -508,7 +527,7 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,
|
|||
PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK);
|
||||
writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win));
|
||||
|
||||
if (is_bmips(pcie))
|
||||
if (BQUIRK(pcie, OB_WIN_32BIT_ADDR))
|
||||
return;
|
||||
|
||||
/* Write the cpu & limit addr upper bits */
|
||||
|
|
@ -1144,8 +1163,8 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Ensure that PERST# is asserted; some bootloaders may deassert it. */
|
||||
if (pcie->cfg->soc_base == BCM2711) {
|
||||
if (BQUIRK(pcie, EARLY_PERST_ASSERT)) {
|
||||
/* Ensure that PERST# is asserted; some bootloaders may deassert it. */
|
||||
ret = pcie->cfg->perst_set(pcie, 1);
|
||||
if (ret) {
|
||||
pcie->cfg->bridge_sw_init_set(pcie, 0);
|
||||
|
|
@ -1161,7 +1180,7 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
|
|||
return ret;
|
||||
|
||||
tmp = readl(base + HARD_DEBUG(pcie));
|
||||
if (is_bmips(pcie))
|
||||
if (BFLAG(pcie, IS_BMIPS))
|
||||
tmp &= ~PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
|
||||
else
|
||||
tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
|
||||
|
|
@ -1174,7 +1193,7 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
|
|||
* is encoded as 0=128, 1=256, 2=512, 3=Rsvd, for BCM7278 it
|
||||
* is encoded as 0=Rsvd, 1=128, 2=256, 3=512.
|
||||
*/
|
||||
if (is_bmips(pcie))
|
||||
if (BFLAG(pcie, IS_BMIPS))
|
||||
burst = 0x1; /* 256 bytes */
|
||||
else if (pcie->cfg->soc_base == BCM2711)
|
||||
burst = 0x0; /* 128 bytes */
|
||||
|
|
@ -1281,11 +1300,11 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (is_bmips(pcie)) {
|
||||
if (BQUIRK(pcie, OB_WIN_MAXSZ_128MB)) {
|
||||
u64 start = res->start;
|
||||
unsigned int j, nwins = resource_size(res) / SZ_128M;
|
||||
|
||||
/* bmips PCIe outbound windows have a 128MB max size */
|
||||
/* PCIe outbound windows have a 128MB max size */
|
||||
if (nwins > BRCM_NUM_PCIE_OUT_WINS)
|
||||
nwins = BRCM_NUM_PCIE_OUT_WINS;
|
||||
for (j = 0; j < nwins; j++, start += SZ_128M)
|
||||
|
|
@ -1326,8 +1345,8 @@ static void brcm_extend_rbus_timeout(struct brcm_pcie *pcie)
|
|||
const unsigned int REG_OFFSET = PCIE_RGR1_SW_INIT_1(pcie) - 8;
|
||||
u32 timeout_us = 4000000; /* 4 seconds, our setting for L1SS */
|
||||
|
||||
/* 7712 does not have this (RGR1) timer */
|
||||
if (pcie->cfg->soc_base == BCM7712)
|
||||
/* Don't do access if there is no timer */
|
||||
if (BQUIRK(pcie, NO_RGR1_TIMER))
|
||||
return;
|
||||
|
||||
/* Each unit in timeout register is 1/216,000,000 seconds */
|
||||
|
|
@ -1589,12 +1608,12 @@ static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start)
|
|||
|
||||
static inline int brcm_phy_start(struct brcm_pcie *pcie)
|
||||
{
|
||||
return pcie->cfg->has_phy ? brcm_phy_cntl(pcie, 1) : 0;
|
||||
return BFLAG(pcie, HAS_PHY) ? brcm_phy_cntl(pcie, 1) : 0;
|
||||
}
|
||||
|
||||
static inline int brcm_phy_stop(struct brcm_pcie *pcie)
|
||||
{
|
||||
return pcie->cfg->has_phy ? brcm_phy_cntl(pcie, 0) : 0;
|
||||
return BFLAG(pcie, HAS_PHY) ? brcm_phy_cntl(pcie, 0) : 0;
|
||||
}
|
||||
|
||||
static int brcm_pcie_turn_off(struct brcm_pcie *pcie)
|
||||
|
|
@ -1619,7 +1638,7 @@ static int brcm_pcie_turn_off(struct brcm_pcie *pcie)
|
|||
u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
|
||||
writel(tmp, base + HARD_DEBUG(pcie));
|
||||
|
||||
if (!(pcie->cfg->quirks & CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN))
|
||||
if (!(BQUIRK(pcie, AVOID_BRIDGE_SHUTDOWN)))
|
||||
/* Shutdown PCIe bridge */
|
||||
ret = brcm_pcie_bridge_sw_init_set(pcie, 1);
|
||||
|
||||
|
|
@ -1898,7 +1917,7 @@ static void brcm_pcie_remove(struct platform_device *pdev)
|
|||
pci_stop_root_bus(bridge->bus);
|
||||
pci_remove_root_bus(bridge->bus);
|
||||
pci_unlock_rescan_remove();
|
||||
if (pcie->cfg->has_err_report)
|
||||
if (BFLAG(pcie, HAS_ERR_REPORT))
|
||||
brcm_unregister_die_notifiers(pcie);
|
||||
|
||||
__brcm_pcie_remove(pcie);
|
||||
|
|
@ -1950,6 +1969,7 @@ static const struct pcie_cfg_data bcm2711_cfg = {
|
|||
.perst_set = brcm_pcie_perst_set_generic,
|
||||
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
|
||||
.num_inbound_wins = 3,
|
||||
.quirks = CFG_QUIRK_EARLY_PERST_ASSERT,
|
||||
};
|
||||
|
||||
static const struct pcie_cfg_data bcm2712_cfg = {
|
||||
|
|
@ -1958,7 +1978,8 @@ static const struct pcie_cfg_data bcm2712_cfg = {
|
|||
.perst_set = brcm_pcie_perst_set_7278,
|
||||
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
|
||||
.post_setup = brcm_pcie_post_setup_bcm2712,
|
||||
.quirks = CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN,
|
||||
.quirks = CFG_QUIRK_AVOID_BRIDGE_SHUTDOWN |
|
||||
CFG_QUIRK_NO_RGR1_TIMER,
|
||||
.num_inbound_wins = 10,
|
||||
};
|
||||
|
||||
|
|
@ -1968,6 +1989,7 @@ static const struct pcie_cfg_data bcm4908_cfg = {
|
|||
.perst_set = brcm_pcie_perst_set_4908,
|
||||
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
|
||||
.num_inbound_wins = 3,
|
||||
.quirks = CFG_QUIRK_PERST_PCIE_REV_CUTOFF,
|
||||
};
|
||||
|
||||
static const struct pcie_cfg_data bcm7278_cfg = {
|
||||
|
|
@ -1984,6 +2006,9 @@ static const struct pcie_cfg_data bcm7425_cfg = {
|
|||
.perst_set = brcm_pcie_perst_set_generic,
|
||||
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
|
||||
.num_inbound_wins = 3,
|
||||
.quirks = CFG_QUIRK_OB_WIN_32BIT_ADDR
|
||||
| CFG_QUIRK_OB_WIN_MAXSZ_128MB | CFG_QUIRK_32BIT_PCI_OPS,
|
||||
.flags = CFG_FLG_IS_BMIPS,
|
||||
};
|
||||
|
||||
static const struct pcie_cfg_data bcm7435_cfg = {
|
||||
|
|
@ -1992,6 +2017,9 @@ static const struct pcie_cfg_data bcm7435_cfg = {
|
|||
.perst_set = brcm_pcie_perst_set_generic,
|
||||
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
|
||||
.num_inbound_wins = 3,
|
||||
.quirks = CFG_QUIRK_OB_WIN_32BIT_ADDR
|
||||
| CFG_QUIRK_OB_WIN_MAXSZ_128MB,
|
||||
.flags = CFG_FLG_IS_BMIPS,
|
||||
};
|
||||
|
||||
static const struct pcie_cfg_data bcm7216_cfg = {
|
||||
|
|
@ -1999,9 +2027,8 @@ static const struct pcie_cfg_data bcm7216_cfg = {
|
|||
.soc_base = BCM7278,
|
||||
.perst_set = brcm_pcie_perst_set_7278,
|
||||
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_7278,
|
||||
.has_phy = true,
|
||||
.flags = CFG_FLG_HAS_PHY | CFG_FLG_HAS_ERR_REPORT,
|
||||
.num_inbound_wins = 3,
|
||||
.has_err_report = true,
|
||||
};
|
||||
|
||||
static const struct pcie_cfg_data bcm7712_cfg = {
|
||||
|
|
@ -2010,6 +2037,7 @@ static const struct pcie_cfg_data bcm7712_cfg = {
|
|||
.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
|
||||
.soc_base = BCM7712,
|
||||
.num_inbound_wins = 10,
|
||||
.quirks = CFG_QUIRK_NO_RGR1_TIMER,
|
||||
};
|
||||
|
||||
static const struct of_device_id brcm_pcie_match[] = {
|
||||
|
|
@ -2034,7 +2062,7 @@ static struct pci_ops brcm_pcie_ops = {
|
|||
.remove_bus = brcm_pcie_remove_bus,
|
||||
};
|
||||
|
||||
static struct pci_ops brcm7425_pcie_ops = {
|
||||
static struct pci_ops brcm_pcie_ops32 = {
|
||||
.map_bus = brcm7425_pcie_map_bus,
|
||||
.read = pci_generic_config_read32,
|
||||
.write = pci_generic_config_write32,
|
||||
|
|
@ -2143,7 +2171,7 @@ static int brcm_pcie_probe(struct platform_device *pdev)
|
|||
goto fail;
|
||||
|
||||
pcie->hw_rev = readl(pcie->base + PCIE_MISC_REVISION);
|
||||
if (pcie->cfg->soc_base == BCM4908 &&
|
||||
if (BQUIRK(pcie, PERST_PCIE_REV_CUTOFF) &&
|
||||
pcie->hw_rev >= BRCM_PCIE_HW_REV_3_20) {
|
||||
dev_err(pcie->dev, "hardware revision with unsupported PERST# setup\n");
|
||||
ret = -ENODEV;
|
||||
|
|
@ -2164,8 +2192,8 @@ static int brcm_pcie_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
bridge->ops = pcie->cfg->soc_base == BCM7425 ?
|
||||
&brcm7425_pcie_ops : &brcm_pcie_ops;
|
||||
bridge->ops = BQUIRK(pcie, 32BIT_PCI_OPS)
|
||||
? &brcm_pcie_ops32 : &brcm_pcie_ops;
|
||||
bridge->sysdata = pcie;
|
||||
|
||||
platform_set_drvdata(pdev, pcie);
|
||||
|
|
@ -2179,7 +2207,7 @@ static int brcm_pcie_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (pcie->cfg->has_err_report) {
|
||||
if (BFLAG(pcie, HAS_ERR_REPORT)) {
|
||||
spin_lock_init(&pcie->bridge_lock);
|
||||
brcm_register_die_notifiers(pcie);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@
|
|||
#include <linux/phy/phy.h>
|
||||
#include <linux/bcma/bcma.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include "../pci.h"
|
||||
#include "pcie-iproc.h"
|
||||
|
||||
|
||||
|
|
@ -31,6 +33,29 @@ static int iproc_bcma_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
|
|||
return bcma_core_irq(bdev, 5);
|
||||
}
|
||||
|
||||
static void iproc_bcma_pcie_check_dt_window(struct iproc_pcie *pcie)
|
||||
{
|
||||
struct device_node *np = pcie->dev->of_node;
|
||||
struct of_pci_range_parser parser;
|
||||
struct of_pci_range range;
|
||||
struct resource res;
|
||||
|
||||
if (!np || of_pci_range_parser_init(&parser, np))
|
||||
return;
|
||||
|
||||
for_each_of_pci_range(&parser, &range) {
|
||||
if ((range.flags & IORESOURCE_TYPE_BITS) != IORESOURCE_MEM)
|
||||
continue;
|
||||
|
||||
if (of_pci_range_to_resource(&range, np, &res))
|
||||
continue;
|
||||
|
||||
if (res.start != pcie->mem.start || res.end != pcie->mem.end)
|
||||
dev_warn(pcie->dev, "DT window %pR does not match EROM window %pR, using EROM\n",
|
||||
&res, &pcie->mem);
|
||||
}
|
||||
}
|
||||
|
||||
static int iproc_bcma_pcie_probe(struct bcma_device *bdev)
|
||||
{
|
||||
struct device *dev = &bdev->dev;
|
||||
|
|
@ -38,10 +63,20 @@ static int iproc_bcma_pcie_probe(struct bcma_device *bdev)
|
|||
struct pci_host_bridge *bridge;
|
||||
int ret;
|
||||
|
||||
bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie));
|
||||
bridge = pci_alloc_host_bridge(sizeof(*pcie));
|
||||
if (!bridge)
|
||||
return -ENOMEM;
|
||||
|
||||
bridge->dev.parent = dev;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, devm_pci_alloc_host_bridge_release,
|
||||
bridge);
|
||||
if (ret)
|
||||
return -ENOMEM;
|
||||
|
||||
bridge->swizzle_irq = pci_common_swizzle;
|
||||
bridge->map_irq = iproc_bcma_pcie_map_irq;
|
||||
|
||||
pcie = pci_host_bridge_priv(bridge);
|
||||
|
||||
pcie->dev = dev;
|
||||
|
|
@ -59,13 +94,12 @@ static int iproc_bcma_pcie_probe(struct bcma_device *bdev)
|
|||
pcie->mem.end = bdev->addr_s[0] + SZ_128M - 1;
|
||||
pcie->mem.name = "PCIe MEM space";
|
||||
pcie->mem.flags = IORESOURCE_MEM;
|
||||
iproc_bcma_pcie_check_dt_window(pcie);
|
||||
pci_add_resource(&bridge->windows, &pcie->mem);
|
||||
ret = devm_request_pci_bus_resources(dev, &bridge->windows);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
bridge->map_irq = iproc_bcma_pcie_map_irq;
|
||||
|
||||
bcma_set_drvdata(bdev, pcie);
|
||||
|
||||
return iproc_pcie_setup(pcie, &bridge->windows);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
|
|
@ -334,10 +336,10 @@ static int mtk_pcie_set_trans_table(struct mtk_gen3_pcie *pcie,
|
|||
|
||||
while (remaining && (*num < PCIE_MAX_TRANS_TABLES)) {
|
||||
/* Table size needs to be a power of 2 */
|
||||
table_size = BIT(fls(remaining) - 1);
|
||||
table_size = BIT_ULL(fls64(remaining) - 1);
|
||||
|
||||
if (cpu_addr > 0) {
|
||||
addr_align = BIT(ffs(cpu_addr) - 1);
|
||||
addr_align = BIT_ULL(__ffs64(cpu_addr));
|
||||
table_size = min(table_size, addr_align);
|
||||
}
|
||||
|
||||
|
|
@ -349,7 +351,8 @@ static int mtk_pcie_set_trans_table(struct mtk_gen3_pcie *pcie,
|
|||
}
|
||||
|
||||
table = pcie->base + PCIE_TRANS_TABLE_BASE_REG + *num * PCIE_ATR_TLB_SET_OFFSET;
|
||||
writel_relaxed(lower_32_bits(cpu_addr) | PCIE_ATR_SIZE(fls(table_size) - 1), table);
|
||||
writel_relaxed(lower_32_bits(cpu_addr) |
|
||||
PCIE_ATR_SIZE(fls64(table_size) - 1), table);
|
||||
writel_relaxed(upper_32_bits(cpu_addr), table + PCIE_ATR_SRC_ADDR_MSB_OFFSET);
|
||||
writel_relaxed(lower_32_bits(pci_addr), table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET);
|
||||
writel_relaxed(upper_32_bits(pci_addr), table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET);
|
||||
|
|
@ -375,9 +378,11 @@ static int mtk_pcie_set_trans_table(struct mtk_gen3_pcie *pcie,
|
|||
(*num)++;
|
||||
}
|
||||
|
||||
if (remaining)
|
||||
dev_warn(pcie->dev, "not enough translate table for addr: %#llx, limited to [%d]\n",
|
||||
if (remaining) {
|
||||
dev_err(pcie->dev, "not enough translate table for addr: %#llx, limited to [%d]\n",
|
||||
(unsigned long long)cpu_addr, PCIE_MAX_TRANS_TABLES);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -583,9 +583,9 @@ static int mtk_pcie_init_irq_domain(struct mtk_pcie_port *port,
|
|||
int ret;
|
||||
|
||||
/* Setup INTx */
|
||||
pcie_intc_node = of_get_next_child(node, NULL);
|
||||
pcie_intc_node = of_get_child_by_name(node, "interrupt-controller");
|
||||
if (!pcie_intc_node) {
|
||||
dev_err(dev, "no PCIe Intc node found\n");
|
||||
dev_err(dev, "no PCIe INTx interrupt-controller node found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -553,19 +553,6 @@ static void rockchip_pcie_ep_link_training(struct work_struct *work)
|
|||
if (ret)
|
||||
goto again;
|
||||
|
||||
/*
|
||||
* Check the current speed: if gen2 speed was requested and we are not
|
||||
* at gen2 speed yet, retrain again for gen2.
|
||||
*/
|
||||
val = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
|
||||
if (!PCIE_LINK_IS_GEN2(val) && rockchip->link_gen == 2) {
|
||||
/* Enable retrain for gen2 */
|
||||
rockchip_pcie_ep_retrain_link(rockchip);
|
||||
readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL,
|
||||
val, PCIE_LINK_IS_GEN2(val), 50,
|
||||
LINK_TRAIN_TIMEOUT);
|
||||
}
|
||||
|
||||
/* Check again that the link is up */
|
||||
if (!rockchip_pcie_ep_link_up(rockchip))
|
||||
goto again;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/of_pci.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "../pci.h"
|
||||
#include "pcie-rockchip.h"
|
||||
|
|
@ -328,26 +329,6 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip)
|
|||
goto err_power_off_phy;
|
||||
}
|
||||
|
||||
if (rockchip->link_gen == 2) {
|
||||
/*
|
||||
* Enable retrain for gen2. This should be configured only after
|
||||
* gen1 finished.
|
||||
*/
|
||||
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL2);
|
||||
status &= ~PCI_EXP_LNKCTL2_TLS;
|
||||
status |= PCI_EXP_LNKCTL2_TLS_5_0GT;
|
||||
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL2);
|
||||
status = rockchip_pcie_read(rockchip, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
|
||||
status |= PCI_EXP_LNKCTL_RL;
|
||||
rockchip_pcie_write(rockchip, status, PCIE_RC_CONFIG_CR + PCI_EXP_LNKCTL);
|
||||
|
||||
err = readl_poll_timeout(rockchip->apb_base + PCIE_CORE_CTRL,
|
||||
status, PCIE_LINK_IS_GEN2(status), 20,
|
||||
500 * USEC_PER_MSEC);
|
||||
if (err)
|
||||
dev_dbg(dev, "PCIe link training gen2 timeout, fall back to gen1!\n");
|
||||
}
|
||||
|
||||
/* Check the final link width from negotiated lane counter from MGMT */
|
||||
status = rockchip_pcie_read(rockchip, PCIE_CORE_CTRL);
|
||||
status = 0x1 << ((status & PCIE_CORE_PL_CONF_LANE_MASK) >>
|
||||
|
|
|
|||
|
|
@ -66,8 +66,10 @@ int rockchip_pcie_parse_dt(struct rockchip_pcie *rockchip)
|
|||
}
|
||||
|
||||
rockchip->link_gen = of_pci_get_max_link_speed(node);
|
||||
if (rockchip->link_gen < 0 || rockchip->link_gen > 2)
|
||||
rockchip->link_gen = 2;
|
||||
if (rockchip->link_gen < 0 || rockchip->link_gen >= 2) {
|
||||
rockchip->link_gen = 1;
|
||||
dev_warn(dev, "invalid max-link-speed, limited to 2.5 GT/s\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < ROCKCHIP_NUM_PM_RSTS; i++)
|
||||
rockchip->pm_rsts[i].id = rockchip_pci_pm_rsts[i];
|
||||
|
|
@ -147,12 +149,8 @@ int rockchip_pcie_init_port(struct rockchip_pcie *rockchip)
|
|||
goto err_exit_phy;
|
||||
}
|
||||
|
||||
if (rockchip->link_gen == 2)
|
||||
rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_2,
|
||||
PCIE_CLIENT_CONFIG);
|
||||
else
|
||||
rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1,
|
||||
PCIE_CLIENT_CONFIG);
|
||||
rockchip_pcie_write(rockchip, PCIE_CLIENT_GEN_SEL_1,
|
||||
PCIE_CLIENT_CONFIG);
|
||||
|
||||
regs = PCIE_CLIENT_ARI_ENABLE |
|
||||
PCIE_CLIENT_CONF_LANE_NUM(rockchip->lanes);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@
|
|||
#define PCIE_CLIENT_MODE_RC HWORD_SET_BIT(0x0040)
|
||||
#define PCIE_CLIENT_MODE_EP HWORD_CLR_BIT(0x0040)
|
||||
#define PCIE_CLIENT_GEN_SEL_1 HWORD_CLR_BIT(0x0080)
|
||||
#define PCIE_CLIENT_GEN_SEL_2 HWORD_SET_BIT(0x0080)
|
||||
#define PCIE_CLIENT_LEGACY_INT_CTRL (PCIE_CLIENT_BASE + 0x0c)
|
||||
#define PCIE_CLIENT_INT_IN_ASSERT HWORD_SET_BIT(0x0002)
|
||||
#define PCIE_CLIENT_INT_IN_DEASSERT HWORD_CLR_BIT(0x0002)
|
||||
|
|
@ -197,8 +196,6 @@
|
|||
(((x) & PCIE_CORE_PL_CONF_LS_MASK) == PCIE_CORE_PL_CONF_LS_READY)
|
||||
#define PCIE_LINK_UP(x) \
|
||||
(((x) & PCIE_CLIENT_LINK_STATUS_MASK) == PCIE_CLIENT_LINK_STATUS_UP)
|
||||
#define PCIE_LINK_IS_GEN2(x) \
|
||||
(((x) & PCIE_CORE_PL_CONF_SPEED_MASK) == PCIE_CORE_PL_CONF_SPEED_5G)
|
||||
|
||||
#define RC_REGION_0_ADDR_TRANS_H 0x00000000
|
||||
#define RC_REGION_0_ADDR_TRANS_L 0x00000000
|
||||
|
|
|
|||
|
|
@ -1006,22 +1006,38 @@ static const struct irq_domain_ops rzg3s_pcie_intx_domain_ops = {
|
|||
.xlate = irq_domain_xlate_onetwocell,
|
||||
};
|
||||
|
||||
static void rzg3s_pcie_teardown_intx(struct rzg3s_pcie_host *host,
|
||||
int count)
|
||||
{
|
||||
while (--count >= 0) {
|
||||
irq_set_chained_handler_and_data(host->intx_irqs[count], NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (host->intx_domain)
|
||||
irq_domain_remove(host->intx_domain);
|
||||
}
|
||||
|
||||
static int rzg3s_pcie_init_irqdomain(struct rzg3s_pcie_host *host)
|
||||
{
|
||||
struct device *dev = host->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
int i, ret;
|
||||
|
||||
for (int i = 0; i < PCI_NUM_INTX; i++) {
|
||||
for (i = 0; i < PCI_NUM_INTX; i++) {
|
||||
char irq_name[5] = {0};
|
||||
int irq;
|
||||
|
||||
scnprintf(irq_name, ARRAY_SIZE(irq_name), "int%c", 'a' + i);
|
||||
|
||||
irq = platform_get_irq_byname(pdev, irq_name);
|
||||
if (irq < 0)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Failed to parse and map INT%c IRQ\n",
|
||||
'A' + i);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
dev_err_probe(dev, ret,
|
||||
"Failed to parse and map INT%c IRQ\n",
|
||||
'A' + i);
|
||||
goto teardown_intx;
|
||||
}
|
||||
|
||||
host->intx_irqs[i] = irq;
|
||||
irq_set_chained_handler_and_data(irq,
|
||||
|
|
@ -1033,21 +1049,27 @@ static int rzg3s_pcie_init_irqdomain(struct rzg3s_pcie_host *host)
|
|||
PCI_NUM_INTX,
|
||||
&rzg3s_pcie_intx_domain_ops,
|
||||
host);
|
||||
if (!host->intx_domain)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Failed to add irq domain for INTx IRQs\n");
|
||||
if (!host->intx_domain) {
|
||||
ret = -EINVAL;
|
||||
dev_err_probe(dev, ret,
|
||||
"Failed to add irq domain for INTx IRQs\n");
|
||||
goto teardown_intx;
|
||||
}
|
||||
irq_domain_update_bus_token(host->intx_domain, DOMAIN_BUS_WIRED);
|
||||
|
||||
if (IS_ENABLED(CONFIG_PCI_MSI)) {
|
||||
int ret = rzg3s_pcie_init_msi(host);
|
||||
ret = rzg3s_pcie_init_msi(host);
|
||||
|
||||
if (ret) {
|
||||
irq_domain_remove(host->intx_domain);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto teardown_intx;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
teardown_intx:
|
||||
rzg3s_pcie_teardown_intx(host, i);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rzg3s_pcie_teardown_irqdomain(struct rzg3s_pcie_host *host)
|
||||
|
|
@ -1055,7 +1077,7 @@ static void rzg3s_pcie_teardown_irqdomain(struct rzg3s_pcie_host *host)
|
|||
if (IS_ENABLED(CONFIG_PCI_MSI))
|
||||
rzg3s_pcie_teardown_msi(host);
|
||||
|
||||
irq_domain_remove(host->intx_domain);
|
||||
rzg3s_pcie_teardown_intx(host, PCI_NUM_INTX);
|
||||
}
|
||||
|
||||
static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
|
||||
|
|
@ -1077,7 +1099,7 @@ static int rzg3s_pcie_set_max_link_speed(struct rzg3s_pcie_host *host)
|
|||
FIELD_GET(RZG3S_PCI_PCSTAT1_LTSSM_STATE, tmp) == ltssm_state_l0,
|
||||
PCIE_LINK_WAIT_SLEEP_MS * MILLI,
|
||||
PCIE_LINK_WAIT_SLEEP_MS * MILLI *
|
||||
PCIE_LINK_WAIT_MAX_RETRIES);
|
||||
PCIE_LINK_WAIT_MAX_RETRIES * 10);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1897,6 +1919,7 @@ static int rzg3s_pcie_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
host_probe_teardown:
|
||||
clk_disable_unprepare(host->port.refclk);
|
||||
rzg3s_pcie_teardown_irqdomain(host);
|
||||
host->data->config_deinit(host);
|
||||
rpm_put:
|
||||
|
|
@ -2074,6 +2097,25 @@ static const struct rzg3s_pcie_soc_data rzg3e_soc_data = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct rzg3s_pcie_soc_data rzg3l_soc_data = {
|
||||
.power_resets = rzg3e_soc_power_resets,
|
||||
.num_power_resets = ARRAY_SIZE(rzg3e_soc_power_resets),
|
||||
.num_pcie_controllers = 1,
|
||||
.config_pre_init = rzg3e_pcie_config_pre_init,
|
||||
.config_post_init = rzg3e_pcie_config_post_init,
|
||||
.config_deinit = rzg3e_pcie_config_deinit,
|
||||
.sysc_info = {
|
||||
[RZG3S_PCIE_CONTROLLER_ID_0] = {
|
||||
.functions = {
|
||||
[RZG3S_SYSC_FUNC_ID_L1_ALLOW] = {
|
||||
.offset = 0x3a0,
|
||||
.mask = BIT(8),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct rzg3s_pcie_soc_data rzv2h_soc_data = {
|
||||
.power_resets = rzg3e_soc_power_resets,
|
||||
.num_power_resets = ARRAY_SIZE(rzg3e_soc_power_resets),
|
||||
|
|
@ -2123,6 +2165,10 @@ static const struct of_device_id rzg3s_pcie_of_match[] = {
|
|||
.compatible = "renesas,r9a08g045-pcie",
|
||||
.data = &rzg3s_soc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,r9a08g046-pcie",
|
||||
.data = &rzg3l_soc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "renesas,r9a09g047-pcie",
|
||||
.data = &rzg3e_soc_data,
|
||||
|
|
|
|||
|
|
@ -49,8 +49,7 @@
|
|||
#define XAL_RC_BAR_CNTL_STANDARD_64B_BAR_EN BIT(2)
|
||||
|
||||
/* XTL registers */
|
||||
#define XTL_RC_PCIE_CFG_LINK_CAPS 0x56
|
||||
#define XTL_RC_PCIE_CFG_LINK_STATUS 0x5a
|
||||
#define XTL_RC_PCIE_CAP 0x48 /* PCIe Capability */
|
||||
|
||||
#define XTL_RC_MGMT_PERST_CONTROL 0x218
|
||||
#define XTL_RC_MGMT_PERST_CONTROL_PERST_O_N BIT(0)
|
||||
|
|
@ -118,11 +117,11 @@ static void tegra264_pcie_icc_set(struct tegra264_pcie *pcie)
|
|||
* possible, so this is as good as it gets for now.
|
||||
*/
|
||||
if (pcie->link_up) {
|
||||
value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_STATUS);
|
||||
value = readw(pcie->ecam + XTL_RC_PCIE_CAP + PCI_EXP_LNKSTA);
|
||||
speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, value);
|
||||
width = FIELD_GET(PCI_EXP_LNKSTA_NLW, value);
|
||||
} else {
|
||||
value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_CAPS);
|
||||
value = readl(pcie->ecam + XTL_RC_PCIE_CAP + PCI_EXP_LNKCAP);
|
||||
speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, value);
|
||||
width = FIELD_GET(PCI_EXP_LNKCAP_MLW, value);
|
||||
}
|
||||
|
|
@ -263,7 +262,7 @@ static bool tegra264_pcie_supports_hotplug(struct tegra264_pcie *pcie)
|
|||
static bool tegra264_pcie_link_up(struct tegra264_pcie *pcie,
|
||||
enum pci_bus_speed *speed)
|
||||
{
|
||||
u16 value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_STATUS);
|
||||
u16 value = readw(pcie->ecam + XTL_RC_PCIE_CAP + PCI_EXP_LNKSTA);
|
||||
|
||||
if (value & PCI_EXP_LNKSTA_DLLLA) {
|
||||
if (speed)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip.h>
|
||||
|
|
@ -16,6 +18,7 @@
|
|||
#include <linux/of_address.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "../pci.h"
|
||||
#include "pcie-xilinx-common.h"
|
||||
|
|
@ -113,6 +116,8 @@ struct xilinx_cpm_variant {
|
|||
* @irq: Error interrupt number
|
||||
* @lock: lock protecting shared register access
|
||||
* @variant: CPM version check pointer
|
||||
* @perst_gpio: GPIO descriptor for PERST# signal handling
|
||||
* @rstc: Pointer to the PCIe controller reset
|
||||
*/
|
||||
struct xilinx_cpm_pcie {
|
||||
struct device *dev;
|
||||
|
|
@ -125,6 +130,8 @@ struct xilinx_cpm_pcie {
|
|||
int irq;
|
||||
raw_spinlock_t lock;
|
||||
const struct xilinx_cpm_variant *variant;
|
||||
struct gpio_desc *perst_gpio;
|
||||
struct reset_control *rstc;
|
||||
};
|
||||
|
||||
static u32 pcie_read(struct xilinx_cpm_pcie *port, u32 reg)
|
||||
|
|
@ -389,7 +396,7 @@ static int xilinx_cpm_pcie_init_irq_domain(struct xilinx_cpm_pcie *port)
|
|||
struct device_node *pcie_intc_node;
|
||||
|
||||
/* Setup INTx */
|
||||
pcie_intc_node = of_get_next_child(node, NULL);
|
||||
pcie_intc_node = of_get_child_by_name(node, "interrupt-controller");
|
||||
if (!pcie_intc_node) {
|
||||
dev_err(dev, "No PCIe Intc node found\n");
|
||||
return -EINVAL;
|
||||
|
|
@ -469,6 +476,22 @@ static int xilinx_cpm_setup_irq(struct xilinx_cpm_pcie *port)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_cpm_pcie_reset - Reset the PCIe controller and deassert PERST#
|
||||
* @port: PCIe port information
|
||||
*
|
||||
* Reset the PCIe controller and then release the PERST# signal so that the
|
||||
* link can train once the bridge is enabled.
|
||||
*/
|
||||
static void xilinx_cpm_pcie_reset(struct xilinx_cpm_pcie *port)
|
||||
{
|
||||
reset_control_assert(port->rstc);
|
||||
udelay(PCIE_T_PERST_US);
|
||||
reset_control_deassert(port->rstc);
|
||||
gpiod_set_value_cansleep(port->perst_gpio, 0);
|
||||
msleep(PCIE_RESET_CONFIG_WAIT_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_cpm_pcie_init_port - Initialize hardware
|
||||
* @port: PCIe port information
|
||||
|
|
@ -480,6 +503,9 @@ static void xilinx_cpm_pcie_init_port(struct xilinx_cpm_pcie *port)
|
|||
if (variant->version == CPM5NC_HOST)
|
||||
return;
|
||||
|
||||
if (port->perst_gpio || port->rstc)
|
||||
xilinx_cpm_pcie_reset(port);
|
||||
|
||||
if (cpm_pcie_link_up(port))
|
||||
dev_info(port->dev, "PCIe Link is UP\n");
|
||||
else
|
||||
|
|
@ -512,6 +538,42 @@ static void xilinx_cpm_pcie_init_port(struct xilinx_cpm_pcie *port)
|
|||
XILINX_CPM_PCIE_REG_RPSC);
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_cpm_pcie_parse_port - Parse the PCIe Root Port child node
|
||||
* @port: PCIe port information
|
||||
*
|
||||
* Read the PERST# GPIO from the Root Port child node.
|
||||
*
|
||||
* Return: '0' on success and error value on failure
|
||||
*/
|
||||
static int xilinx_cpm_pcie_parse_port(struct xilinx_cpm_pcie *port)
|
||||
{
|
||||
struct device *dev = port->dev;
|
||||
|
||||
/*
|
||||
* This platform currently supports only one Root Port, so the loop
|
||||
* will execute only once.
|
||||
* TODO: Enhance the driver to handle multiple Root Ports in the future.
|
||||
*/
|
||||
for_each_child_of_node_with_prefix(dev->of_node, pcie_port_node, "pcie") {
|
||||
port->perst_gpio = devm_fwnode_gpiod_get(dev,
|
||||
of_fwnode_handle(pcie_port_node),
|
||||
"reset", GPIOD_OUT_HIGH,
|
||||
NULL);
|
||||
if (IS_ERR(port->perst_gpio)) {
|
||||
if (PTR_ERR(port->perst_gpio) == -ENOENT) {
|
||||
port->perst_gpio = NULL;
|
||||
return 0;
|
||||
}
|
||||
return dev_err_probe(dev, PTR_ERR(port->perst_gpio),
|
||||
"Failed to request reset GPIO\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* xilinx_cpm_pcie_parse_dt - Parse Device tree
|
||||
* @port: PCIe port information
|
||||
|
|
@ -525,6 +587,19 @@ static int xilinx_cpm_pcie_parse_dt(struct xilinx_cpm_pcie *port,
|
|||
struct device *dev = port->dev;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
/* CPM5NC does not support PERST# handling yet */
|
||||
if (port->variant->version != CPM5NC_HOST) {
|
||||
port->rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
|
||||
if (IS_ERR(port->rstc))
|
||||
return dev_err_probe(dev, PTR_ERR(port->rstc),
|
||||
"Failed to request reset\n");
|
||||
|
||||
ret = xilinx_cpm_pcie_parse_port(port);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
port->cpm_base = devm_platform_ioremap_resource_byname(pdev,
|
||||
"cpm_slcr");
|
||||
|
|
|
|||
|
|
@ -581,7 +581,7 @@ static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port)
|
|||
port->pldma_domain = irq_domain_create_linear(of_fwnode_handle(pcie_intc_node), 32,
|
||||
&event_domain_ops, port);
|
||||
if (!port->pldma_domain)
|
||||
return -ENOMEM;
|
||||
goto err_put_node;
|
||||
|
||||
irq_domain_update_bus_token(port->pldma_domain, DOMAIN_BUS_NEXUS);
|
||||
|
||||
|
|
@ -589,7 +589,7 @@ static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port)
|
|||
&intx_domain_ops, port);
|
||||
if (!port->intx_domain) {
|
||||
dev_err(dev, "Failed to get a INTx IRQ domain\n");
|
||||
return -ENOMEM;
|
||||
goto err_put_node;
|
||||
}
|
||||
|
||||
irq_domain_update_bus_token(port->intx_domain, DOMAIN_BUS_WIRED);
|
||||
|
|
@ -597,13 +597,17 @@ static int xilinx_pl_dma_pcie_init_irq_domain(struct pl_dma_pcie *port)
|
|||
ret = xilinx_pl_dma_pcie_init_msi_irq_domain(port);
|
||||
if (ret != 0) {
|
||||
irq_domain_remove(port->intx_domain);
|
||||
return -ENOMEM;
|
||||
goto err_put_node;
|
||||
}
|
||||
|
||||
of_node_put(pcie_intc_node);
|
||||
raw_spin_lock_init(&port->lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_node:
|
||||
of_node_put(pcie_intc_node);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int xilinx_pl_dma_pcie_setup_irq(struct pl_dma_pcie *port)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include "../../pci.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,23 @@ enum vmd_features {
|
|||
* referred to as MEMBAR2 or MSI-X BAR.
|
||||
*/
|
||||
VMD_FEAT_USE_BIOS_INFO = (1 << 6),
|
||||
|
||||
/*
|
||||
* Some Intel VMD devices are affected by interrupt ordering errata,
|
||||
* including MTL016 (0x7d0b) and ARL004 (0xad0b):
|
||||
* the VMD may signal its MSI before the posted writes that carry the
|
||||
* child device's DMA data have landed in memory. The demuxed handler
|
||||
* then runs against a not-yet-coherent completion queue and misses the
|
||||
* completion entirely, so the I/O is only recovered when the block
|
||||
* layer timeout fires and polls the queue ("timeout, completion
|
||||
* polled"). Intel's documented workaround is to issue a dummy read
|
||||
* to the MSI initiator (the child device) before handling the
|
||||
* interrupt: the read completion cannot pass the device's earlier
|
||||
* posted writes, so it pulls them into memory per PCIe ordering
|
||||
* rules. A read that terminates at the VMD itself does not order
|
||||
* against the child's writes and is not sufficient (measured).
|
||||
*/
|
||||
VMD_FEAT_INTERRUPT_QUIRK = (1 << 7),
|
||||
};
|
||||
|
||||
#define VMD_BIOS_PM_QUIRK_LTR 0x1003 /* 3145728 ns */
|
||||
|
|
@ -114,6 +131,9 @@ static DEFINE_RAW_SPINLOCK(list_lock);
|
|||
* @irq: back pointer to parent.
|
||||
* @enabled: true if driver enabled IRQ
|
||||
* @virq: the virtual IRQ value provided to the requesting driver.
|
||||
* @flush_addr: config space address of the initiating device, read before
|
||||
* demuxing to flush its posted writes; NULL if the
|
||||
* VMD is not affected.
|
||||
*
|
||||
* Every MSI/MSI-X IRQ requested for a device in a VMD domain will be mapped to
|
||||
* a VMD IRQ using this structure.
|
||||
|
|
@ -123,8 +143,14 @@ struct vmd_irq {
|
|||
struct vmd_irq_list *irq;
|
||||
bool enabled;
|
||||
unsigned int virq;
|
||||
void __iomem *flush_addr;
|
||||
};
|
||||
|
||||
struct vmd_dev;
|
||||
|
||||
static void __iomem *vmd_cfg_addr(struct vmd_dev *vmd, struct pci_bus *bus,
|
||||
unsigned int devfn, int reg, int len);
|
||||
|
||||
/**
|
||||
* struct vmd_irq_list - list of driver requested IRQs mapping to a VMD vector
|
||||
* @irq_list: the list of irq's the VMD one demuxes to.
|
||||
|
|
@ -132,12 +158,15 @@ struct vmd_irq {
|
|||
* @count: number of child IRQs assigned to this vector; used to track
|
||||
* sharing.
|
||||
* @virq: The underlying VMD Linux interrupt number
|
||||
* @vmd: back pointer to the owning VMD device; serializes the MTL016
|
||||
* flush read against other config space access.
|
||||
*/
|
||||
struct vmd_irq_list {
|
||||
struct list_head irq_list;
|
||||
struct srcu_struct srcu;
|
||||
unsigned int count;
|
||||
unsigned int virq;
|
||||
struct vmd_dev *vmd;
|
||||
};
|
||||
|
||||
struct vmd_dev {
|
||||
|
|
@ -295,6 +324,13 @@ static int vmd_msi_alloc(struct irq_domain *domain, unsigned int virq,
|
|||
INIT_LIST_HEAD(&vmdirq->node);
|
||||
vmdirq->irq = vmd_next_irq(vmd, desc);
|
||||
vmdirq->virq = virq + i;
|
||||
if (vmd->features & VMD_FEAT_INTERRUPT_QUIRK) {
|
||||
struct pci_dev *pdev = msi_desc_to_pci_dev(desc);
|
||||
|
||||
vmdirq->flush_addr = vmd_cfg_addr(vmd, pdev->bus,
|
||||
pdev->devfn,
|
||||
PCI_VENDOR_ID, 2);
|
||||
}
|
||||
|
||||
irq_domain_set_info(domain, virq + i, vmdirq->irq->virq,
|
||||
&vmd_msi_controller, vmdirq,
|
||||
|
|
@ -756,8 +792,20 @@ static irqreturn_t vmd_irq(int irq, void *data)
|
|||
int idx;
|
||||
|
||||
idx = srcu_read_lock(&irqs->srcu);
|
||||
list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node)
|
||||
list_for_each_entry_rcu(vmdirq, &irqs->irq_list, node) {
|
||||
/*
|
||||
* MTL016: the MSI may have outrun the initiating device's
|
||||
* posted writes (e.g. its NVMe completion entry). A read
|
||||
* that completes at the initiator flushes them, so the
|
||||
* demuxed handler observes a coherent completion queue.
|
||||
* The value is discarded; only the ordering matters.
|
||||
*/
|
||||
if (vmdirq->flush_addr) {
|
||||
guard(raw_spinlock)(&irqs->vmd->cfg_lock);
|
||||
readw(vmdirq->flush_addr);
|
||||
}
|
||||
generic_handle_irq(vmdirq->virq);
|
||||
}
|
||||
srcu_read_unlock(&irqs->srcu, idx);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
|
@ -788,6 +836,7 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd)
|
|||
return err;
|
||||
|
||||
INIT_LIST_HEAD(&vmd->irqs[i].irq_list);
|
||||
vmd->irqs[i].vmd = vmd;
|
||||
vmd->irqs[i].virq = pci_irq_vector(dev, i);
|
||||
err = devm_request_irq(&dev->dev, vmd->irqs[i].virq,
|
||||
vmd_irq, IRQF_NO_THREAD,
|
||||
|
|
@ -1250,9 +1299,9 @@ static const struct pci_device_id vmd_ids[] = {
|
|||
{PCI_VDEVICE(INTEL, 0xa77f),
|
||||
.driver_data = VMD_FEATS_CLIENT,},
|
||||
{PCI_VDEVICE(INTEL, 0x7d0b),
|
||||
.driver_data = VMD_FEATS_CLIENT,},
|
||||
.driver_data = VMD_FEATS_CLIENT | VMD_FEAT_INTERRUPT_QUIRK,},
|
||||
{PCI_VDEVICE(INTEL, 0xad0b),
|
||||
.driver_data = VMD_FEATS_CLIENT,},
|
||||
.driver_data = VMD_FEATS_CLIENT | VMD_FEAT_INTERRUPT_QUIRK,},
|
||||
{PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_VMD_9A0B),
|
||||
.driver_data = VMD_FEATS_CLIENT,},
|
||||
{PCI_VDEVICE(INTEL, 0xb60b),
|
||||
|
|
|
|||
|
|
@ -208,8 +208,9 @@ static int pci_doe_sysfs_feature_populate(struct pci_dev *pdev,
|
|||
pci_warn(pdev, "Failed adding %s to sysfs group\n",
|
||||
attrs[i].attr.name);
|
||||
goto fail;
|
||||
} else
|
||||
kfree(attrs[i].attr.name);
|
||||
}
|
||||
kfree(attrs[i].attr.name);
|
||||
attrs[i].attr.name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/pci-ep-msi.h>
|
||||
|
|
@ -132,7 +133,7 @@ struct epf_ntb_ctrl {
|
|||
} __packed;
|
||||
|
||||
struct epf_ntb {
|
||||
struct ntb_dev ntb;
|
||||
struct ntb_dev *ntb;
|
||||
struct pci_epf *epf;
|
||||
struct config_group group;
|
||||
|
||||
|
|
@ -147,7 +148,11 @@ struct epf_ntb {
|
|||
u16 vntb_pid;
|
||||
u16 vntb_vid;
|
||||
|
||||
bool linkup;
|
||||
/* Serialize HOST and VHOST link state changes. */
|
||||
struct mutex link_lock;
|
||||
bool host_link_enabled;
|
||||
bool vhost_link_enabled;
|
||||
bool peer_msix;
|
||||
|
||||
/*
|
||||
* True when doorbells are interrupt-driven (MSI or embedded), false
|
||||
|
|
@ -155,6 +160,7 @@ struct epf_ntb {
|
|||
*/
|
||||
bool msi_doorbell;
|
||||
u32 spad_size;
|
||||
u32 msix_table_offset;
|
||||
|
||||
enum pci_barno epf_ntb_bar[VNTB_BAR_NUM];
|
||||
|
||||
|
|
@ -166,10 +172,15 @@ struct epf_ntb {
|
|||
void __iomem *vpci_mw_addr[MAX_MW];
|
||||
|
||||
struct delayed_work cmd_handler;
|
||||
struct pci_bus *vpci_bus;
|
||||
};
|
||||
|
||||
#define to_epf_ntb(epf_group) container_of((epf_group), struct epf_ntb, group)
|
||||
#define ntb_ndev(__ntb) container_of(__ntb, struct epf_ntb, ntb)
|
||||
|
||||
static struct epf_ntb *ntb_ndev(struct ntb_dev *ntb)
|
||||
{
|
||||
return ntb->pdev->sysdata;
|
||||
}
|
||||
|
||||
static struct pci_epf_header epf_ntb_header = {
|
||||
.vendorid = PCI_ANY_ID,
|
||||
|
|
@ -178,24 +189,58 @@ static struct pci_epf_header epf_ntb_header = {
|
|||
.interrupt_pin = PCI_INTERRUPT_INTA,
|
||||
};
|
||||
|
||||
static void epf_ntb_update_link(struct epf_ntb *ntb)
|
||||
{
|
||||
u16 link_status = READ_ONCE(ntb->reg->link_status);
|
||||
|
||||
/* The link is usable only after both sides have enabled it. */
|
||||
if (ntb->host_link_enabled && ntb->vhost_link_enabled)
|
||||
link_status |= LINK_STATUS_UP;
|
||||
else
|
||||
link_status &= ~LINK_STATUS_UP;
|
||||
|
||||
WRITE_ONCE(ntb->reg->link_status, link_status);
|
||||
}
|
||||
|
||||
static int epf_ntb_notify_link(struct ntb_dev *ndev)
|
||||
{
|
||||
struct epf_ntb *ntb = ntb_ndev(ndev);
|
||||
struct pci_epf *epf = ntb->epf;
|
||||
unsigned int irq_type;
|
||||
int ret;
|
||||
|
||||
ntb_link_event(ndev);
|
||||
|
||||
irq_type = ntb->peer_msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI;
|
||||
ret = pci_epc_raise_irq(epf->epc, epf->func_no, epf->vfunc_no,
|
||||
irq_type, EPF_IRQ_LINK + 1);
|
||||
if (ret)
|
||||
dev_err(&epf->dev, "Failed to raise link event IRQ: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* epf_ntb_link_up() - Raise link_up interrupt to Virtual Host (VHOST)
|
||||
* epf_ntb_set_link_enabled() - Set HOST link enablement
|
||||
* @ntb: NTB device that facilitates communication between HOST and VHOST
|
||||
* @link_up: true or false indicating Link is UP or Down
|
||||
*
|
||||
* Once NTB function in HOST invoke ntb_link_enable(),
|
||||
* this NTB function driver will trigger a link event to VHOST.
|
||||
* @enabled: true when the HOST has enabled the link
|
||||
*
|
||||
* Returns: Zero for success, or an error code in case of failure
|
||||
*/
|
||||
static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up)
|
||||
static int epf_ntb_set_link_enabled(struct epf_ntb *ntb, bool enabled)
|
||||
{
|
||||
if (link_up)
|
||||
ntb->reg->link_status |= LINK_STATUS_UP;
|
||||
else
|
||||
ntb->reg->link_status &= ~LINK_STATUS_UP;
|
||||
bool notify;
|
||||
|
||||
scoped_guard(mutex, &ntb->link_lock) {
|
||||
notify = ntb->host_link_enabled != enabled &&
|
||||
ntb->vhost_link_enabled;
|
||||
ntb->host_link_enabled = enabled;
|
||||
epf_ntb_update_link(ntb);
|
||||
}
|
||||
|
||||
if (notify)
|
||||
return epf_ntb_notify_link(ntb->ntb);
|
||||
|
||||
ntb_link_event(&ntb->ntb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -271,6 +316,7 @@ static void epf_ntb_teardown_mw(struct epf_ntb *ntb, u32 mw)
|
|||
*/
|
||||
static void epf_ntb_cmd_handler(struct work_struct *work)
|
||||
{
|
||||
const struct pci_epc_features *epc_features;
|
||||
struct epf_ntb_ctrl *ctrl;
|
||||
u32 command, argument;
|
||||
struct epf_ntb *ntb;
|
||||
|
|
@ -284,7 +330,7 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
|
|||
i++) {
|
||||
if (ntb->epf_db[i]) {
|
||||
atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db);
|
||||
ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START);
|
||||
ntb_db_event(ntb->ntb, i - EPF_IRQ_DB_START);
|
||||
ntb->epf_db[i] = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -303,6 +349,15 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
|
|||
|
||||
switch (command) {
|
||||
case COMMAND_CONFIGURE_DOORBELL:
|
||||
epc_features = pci_epc_get_features(ntb->epf->epc,
|
||||
ntb->epf->func_no,
|
||||
ntb->epf->vfunc_no);
|
||||
if (((argument & MSIX_ENABLE) && !epc_features->msix_capable) ||
|
||||
(!(argument & MSIX_ENABLE) && !epc_features->msi_capable)) {
|
||||
ctrl->command_status = COMMAND_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
ntb->peer_msix = argument & MSIX_ENABLE;
|
||||
ctrl->command_status = COMMAND_STATUS_OK;
|
||||
break;
|
||||
case COMMAND_TEARDOWN_DOORBELL:
|
||||
|
|
@ -320,16 +375,14 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
|
|||
ctrl->command_status = COMMAND_STATUS_OK;
|
||||
break;
|
||||
case COMMAND_LINK_UP:
|
||||
ntb->linkup = true;
|
||||
ret = epf_ntb_link_up(ntb, true);
|
||||
ret = epf_ntb_set_link_enabled(ntb, true);
|
||||
if (ret < 0)
|
||||
ctrl->command_status = COMMAND_STATUS_ERROR;
|
||||
else
|
||||
ctrl->command_status = COMMAND_STATUS_OK;
|
||||
goto reset_handler;
|
||||
case COMMAND_LINK_DOWN:
|
||||
ntb->linkup = false;
|
||||
ret = epf_ntb_link_up(ntb, false);
|
||||
ret = epf_ntb_set_link_enabled(ntb, false);
|
||||
if (ret < 0)
|
||||
ctrl->command_status = COMMAND_STATUS_ERROR;
|
||||
else
|
||||
|
|
@ -348,12 +401,18 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
|
|||
static irqreturn_t epf_ntb_doorbell_handler(int irq, void *data)
|
||||
{
|
||||
struct epf_ntb *ntb = data;
|
||||
struct ntb_dev *ndev;
|
||||
int i;
|
||||
|
||||
/* Pair with smp_store_release() in pci_vntb_probe() */
|
||||
ndev = smp_load_acquire(&ntb->ntb);
|
||||
if (!ndev)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
for (i = EPF_IRQ_DB_START; i < ntb->db_count; i++)
|
||||
if (irq == ntb->epf->db_msg[i].virq) {
|
||||
atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db);
|
||||
ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START);
|
||||
ntb_db_event(ndev, i - EPF_IRQ_DB_START);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
|
@ -440,8 +499,9 @@ static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
|
|||
* @ntb: NTB device that facilitates communication between HOST and VHOST
|
||||
*
|
||||
* Allocate the Local Memory mentioned in the above diagram. The size of
|
||||
* CONFIG REGION is sizeof(struct epf_ntb_ctrl) and size of SCRATCHPAD REGION
|
||||
* is obtained from "spad-count" configfs entry.
|
||||
* CONFIG REGION is sizeof(struct epf_ntb_ctrl), plus space for the MSI-X
|
||||
* Table and PBA if supported. The size of SCRATCHPAD REGION is obtained
|
||||
* from "spad-count" configfs entry.
|
||||
*
|
||||
* Returns: Zero for success, or an error code in case of failure
|
||||
*/
|
||||
|
|
@ -462,6 +522,12 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
|
|||
spad_count = ntb->spad_count;
|
||||
|
||||
ctrl_size = ALIGN(sizeof(struct epf_ntb_ctrl), sizeof(u32));
|
||||
if (epc_features->msix_capable) {
|
||||
ntb->msix_table_offset = ALIGN(ctrl_size, 8);
|
||||
ctrl_size = ntb->msix_table_offset +
|
||||
ntb->db_count * PCI_MSIX_ENTRY_SIZE +
|
||||
BITS_TO_U64(ntb->db_count) * sizeof(u64);
|
||||
}
|
||||
spad_size = 2 * spad_count * sizeof(u32);
|
||||
|
||||
base = pci_epf_alloc_space(epf, ctrl_size + spad_size,
|
||||
|
|
@ -502,12 +568,14 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
|
|||
static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
|
||||
{
|
||||
const struct pci_epc_features *epc_features;
|
||||
struct pci_epf *epf = ntb->epf;
|
||||
struct device *dev;
|
||||
int ret;
|
||||
|
||||
dev = &ntb->epf->dev;
|
||||
dev = &epf->dev;
|
||||
|
||||
epc_features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no);
|
||||
epc_features = pci_epc_get_features(epf->epc, epf->func_no,
|
||||
epf->vfunc_no);
|
||||
|
||||
if (!(epc_features->msix_capable || epc_features->msi_capable)) {
|
||||
dev_err(dev, "MSI or MSI-X is required for doorbell\n");
|
||||
|
|
@ -521,16 +589,24 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
|
|||
}
|
||||
|
||||
if (epc_features->msi_capable) {
|
||||
ret = pci_epc_set_msi(ntb->epf->epc,
|
||||
ntb->epf->func_no,
|
||||
ntb->epf->vfunc_no,
|
||||
16);
|
||||
ret = pci_epc_set_msi(epf->epc, epf->func_no, epf->vfunc_no, 16);
|
||||
if (ret) {
|
||||
dev_err(dev, "MSI configuration failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (epc_features->msix_capable) {
|
||||
ret = pci_epc_set_msix(epf->epc, epf->func_no, epf->vfunc_no,
|
||||
ntb->db_count,
|
||||
ntb->epf_ntb_bar[BAR_CONFIG],
|
||||
ntb->msix_table_offset);
|
||||
if (ret) {
|
||||
dev_err(dev, "MSI-X configuration failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -985,7 +1061,6 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
|
|||
}
|
||||
|
||||
INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler);
|
||||
queue_work(kpcintb_workqueue, &ntb->cmd_handler.work);
|
||||
|
||||
atomic64_set(&ntb->peer_db_pending, 0);
|
||||
enable_work(&ntb->peer_db_work);
|
||||
|
|
@ -1332,14 +1407,20 @@ static int vpci_scan_bus(void *sysdata)
|
|||
struct pci_bus *vpci_bus;
|
||||
struct epf_ntb *ndev = sysdata;
|
||||
|
||||
pci_lock_rescan_remove();
|
||||
|
||||
vpci_bus = pci_scan_bus(ndev->vbus_number, &vpci_ops, sysdata);
|
||||
if (!vpci_bus) {
|
||||
pr_err("create pci bus failed\n");
|
||||
pci_unlock_rescan_remove();
|
||||
return -EINVAL;
|
||||
}
|
||||
ndev->vpci_bus = vpci_bus;
|
||||
|
||||
pci_bus_add_devices(vpci_bus);
|
||||
|
||||
pci_unlock_rescan_remove();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1420,14 +1501,15 @@ static int vntb_epf_mw_set_trans(struct ntb_dev *ndev, int pidx, int idx,
|
|||
int ret;
|
||||
struct device *dev;
|
||||
|
||||
dev = &ntb->ntb.dev;
|
||||
dev = &ndev->dev;
|
||||
barno = ntb->epf_ntb_bar[BAR_MW1 + idx];
|
||||
epf_bar = &ntb->epf->bar[barno];
|
||||
epf_bar->phys_addr = addr;
|
||||
epf_bar->barno = barno;
|
||||
epf_bar->size = size;
|
||||
|
||||
ret = pci_epc_set_bar(ntb->epf->epc, 0, 0, epf_bar);
|
||||
ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
|
||||
ntb->epf->vfunc_no, epf_bar);
|
||||
if (ret) {
|
||||
dev_err(dev, "failure set mw trans\n");
|
||||
return ret;
|
||||
|
|
@ -1455,11 +1537,29 @@ static int vntb_epf_peer_mw_get_addr(struct ntb_dev *ndev, int idx,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int vntb_epf_set_link_enabled(struct ntb_dev *ndev, bool enabled)
|
||||
{
|
||||
struct epf_ntb *ntb = ntb_ndev(ndev);
|
||||
bool notify;
|
||||
|
||||
scoped_guard(mutex, &ntb->link_lock) {
|
||||
notify = ntb->vhost_link_enabled != enabled &&
|
||||
ntb->host_link_enabled;
|
||||
ntb->vhost_link_enabled = enabled;
|
||||
epf_ntb_update_link(ntb);
|
||||
}
|
||||
|
||||
if (notify)
|
||||
return epf_ntb_notify_link(ndev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vntb_epf_link_enable(struct ntb_dev *ntb,
|
||||
enum ntb_speed max_speed,
|
||||
enum ntb_width max_width)
|
||||
{
|
||||
return 0;
|
||||
return vntb_epf_set_link_enabled(ntb, true);
|
||||
}
|
||||
|
||||
static u32 vntb_epf_spad_read(struct ntb_dev *ndev, int idx)
|
||||
|
|
@ -1512,6 +1612,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work)
|
|||
struct epf_ntb *ntb = container_of(work, struct epf_ntb, peer_db_work);
|
||||
struct pci_epf *epf = ntb->epf;
|
||||
unsigned int budget = VNTB_PEER_DB_WORK_BUDGET;
|
||||
unsigned int irq_type;
|
||||
u8 func_no, vfunc_no;
|
||||
unsigned int db_bit;
|
||||
u32 interrupt_num;
|
||||
|
|
@ -1523,6 +1624,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work)
|
|||
|
||||
func_no = epf->func_no;
|
||||
vfunc_no = epf->vfunc_no;
|
||||
irq_type = ntb->peer_msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI;
|
||||
|
||||
/*
|
||||
* Drain doorbells from peer_db_pending in snapshots (atomic64_xchg()).
|
||||
|
|
@ -1536,7 +1638,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work)
|
|||
|
||||
while (db_bits) {
|
||||
/*
|
||||
* pci_epc_raise_irq() for MSI expects a 1-based
|
||||
* pci_epc_raise_irq() for MSI/MSI-X expects a 1-based
|
||||
* interrupt number. The first usable doorbell starts
|
||||
* at EPF_IRQ_DB_START in the legacy slot layout.
|
||||
*
|
||||
|
|
@ -1556,9 +1658,9 @@ static void vntb_epf_peer_db_work(struct work_struct *work)
|
|||
db_bits &= ~BIT_ULL(db_bit);
|
||||
|
||||
ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no,
|
||||
PCI_IRQ_MSI, interrupt_num);
|
||||
irq_type, interrupt_num);
|
||||
if (ret)
|
||||
dev_err(&ntb->ntb.dev,
|
||||
dev_err(&epf->dev,
|
||||
"Failed to raise IRQ for interrupt_num %u: %d\n",
|
||||
interrupt_num, ret);
|
||||
}
|
||||
|
|
@ -1619,7 +1721,7 @@ static u64 vntb_epf_link_is_up(struct ntb_dev *ndev,
|
|||
{
|
||||
struct epf_ntb *ntb = ntb_ndev(ndev);
|
||||
|
||||
return ntb->reg->link_status;
|
||||
return READ_ONCE(ntb->reg->link_status);
|
||||
}
|
||||
|
||||
static int vntb_epf_db_clear_mask(struct ntb_dev *ndev, u64 db_bits)
|
||||
|
|
@ -1637,7 +1739,7 @@ static int vntb_epf_db_clear(struct ntb_dev *ndev, u64 db_bits)
|
|||
|
||||
static int vntb_epf_link_disable(struct ntb_dev *ntb)
|
||||
{
|
||||
return 0;
|
||||
return vntb_epf_set_link_enabled(ntb, false);
|
||||
}
|
||||
|
||||
static struct device *vntb_epf_get_dma_dev(struct ntb_dev *ndev)
|
||||
|
|
@ -1676,13 +1778,18 @@ static const struct ntb_dev_ops vntb_epf_ops = {
|
|||
|
||||
static int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct epf_ntb *ndev = (struct epf_ntb *)pdev->sysdata;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ntb_dev *ntb;
|
||||
int ret;
|
||||
|
||||
ndev->ntb.pdev = pdev;
|
||||
ndev->ntb.topo = NTB_TOPO_NONE;
|
||||
ndev->ntb.ops = &vntb_epf_ops;
|
||||
ntb = devm_kzalloc(dev, sizeof(*ntb), GFP_KERNEL);
|
||||
if (!ntb)
|
||||
return -ENOMEM;
|
||||
|
||||
ntb->pdev = pdev;
|
||||
ntb->topo = NTB_TOPO_NONE;
|
||||
ntb->ops = &vntb_epf_ops;
|
||||
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
|
||||
if (ret) {
|
||||
|
|
@ -1690,16 +1797,41 @@ static int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = ntb_register_device(&ndev->ntb);
|
||||
ret = ntb_register_device(ntb);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register NTB device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Publish after ntb_register_device() succeeds */
|
||||
smp_store_release(&ndev->ntb, ntb);
|
||||
queue_delayed_work(kpcintb_workqueue, &ndev->cmd_handler, 0);
|
||||
|
||||
dev_dbg(dev, "PCI Virtual NTB driver loaded\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_vntb_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct epf_ntb *ndev = pdev->sysdata;
|
||||
struct ntb_dev *ntb;
|
||||
unsigned int i;
|
||||
|
||||
/* Stop the work reader, then close and drain the IRQ gate */
|
||||
cancel_delayed_work_sync(&ndev->cmd_handler);
|
||||
|
||||
ntb = xchg(&ndev->ntb, NULL);
|
||||
|
||||
if (ndev->msi_doorbell)
|
||||
for (i = 0; i < ndev->db_count; i++) {
|
||||
if (epf_ntb_db_irq_is_duplicated(ndev->epf, i))
|
||||
continue;
|
||||
synchronize_irq(ndev->epf->db_msg[i].virq);
|
||||
}
|
||||
|
||||
ntb_unregister_device(ntb);
|
||||
}
|
||||
|
||||
static struct pci_device_id pci_vntb_table[] = {
|
||||
{
|
||||
PCI_DEVICE(0xffff, 0xffff),
|
||||
|
|
@ -1711,6 +1843,7 @@ static struct pci_driver vntb_pci_driver = {
|
|||
.name = "pci-vntb",
|
||||
.id_table = pci_vntb_table,
|
||||
.probe = pci_vntb_probe,
|
||||
.remove = pci_vntb_remove,
|
||||
};
|
||||
|
||||
/* ============ PCIe EPF Driver Bind ====================*/
|
||||
|
|
@ -1749,6 +1882,10 @@ static int epf_ntb_bind(struct pci_epf *epf)
|
|||
goto err_bar_alloc;
|
||||
}
|
||||
|
||||
ntb->host_link_enabled = false;
|
||||
ntb->vhost_link_enabled = false;
|
||||
ntb->reg->link_status = 0;
|
||||
|
||||
ret = epf_ntb_epc_init(ntb);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to initialize EPC\n");
|
||||
|
|
@ -1791,12 +1928,25 @@ static int epf_ntb_bind(struct pci_epf *epf)
|
|||
*/
|
||||
static void epf_ntb_unbind(struct pci_epf *epf)
|
||||
{
|
||||
struct pci_host_bridge *bridge;
|
||||
struct epf_ntb *ntb = epf_get_drvdata(epf);
|
||||
|
||||
pci_unregister_driver(&vntb_pci_driver);
|
||||
|
||||
if (ntb->vpci_bus) {
|
||||
bridge = to_pci_host_bridge(ntb->vpci_bus->bridge);
|
||||
|
||||
pci_lock_rescan_remove();
|
||||
pci_stop_root_bus(ntb->vpci_bus);
|
||||
pci_remove_root_bus(ntb->vpci_bus);
|
||||
ntb->vpci_bus = NULL;
|
||||
pci_unlock_rescan_remove();
|
||||
|
||||
pci_free_host_bridge(bridge);
|
||||
}
|
||||
|
||||
epf_ntb_epc_cleanup(ntb);
|
||||
epf_ntb_config_spad_bar_free(ntb);
|
||||
|
||||
pci_unregister_driver(&vntb_pci_driver);
|
||||
}
|
||||
|
||||
// EPF driver probe
|
||||
|
|
@ -1821,7 +1971,7 @@ static int epf_ntb_probe(struct pci_epf *epf,
|
|||
{
|
||||
struct epf_ntb *ntb;
|
||||
struct device *dev;
|
||||
int i;
|
||||
int ret, i;
|
||||
|
||||
dev = &epf->dev;
|
||||
|
||||
|
|
@ -1832,6 +1982,9 @@ static int epf_ntb_probe(struct pci_epf *epf,
|
|||
epf->header = &epf_ntb_header;
|
||||
ntb->epf = epf;
|
||||
ntb->vbus_number = 0xff;
|
||||
ret = devm_mutex_init(dev, &ntb->link_lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
INIT_WORK(&ntb->peer_db_work, vntb_epf_peer_db_work);
|
||||
disable_work(&ntb->peer_db_work);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/msi.h>
|
||||
|
|
@ -112,8 +111,11 @@ static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db)
|
|||
*
|
||||
* Still, pci_epf_alloc_doorbell() allows requesting multiple doorbells.
|
||||
* For such backends we replicate the same address/data for each entry
|
||||
* and mark the IRQ as shared (IRQF_SHARED). Consumers must treat them
|
||||
* as equivalent "kick" doorbells.
|
||||
* as equivalent "kick" doorbells. Consumers must request each distinct
|
||||
* IRQ only once.
|
||||
*
|
||||
* Keep the IRQ exclusive because the source cannot identify which EPF
|
||||
* rang the doorbell.
|
||||
*/
|
||||
for (i = 0; i < num_db; i++)
|
||||
msg[i] = (struct pci_epf_doorbell_msg) {
|
||||
|
|
@ -121,7 +123,6 @@ static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db)
|
|||
.msg.address_hi = (u32)(addr >> 32),
|
||||
.msg.data = doorbell->u.db_mmio.data,
|
||||
.virq = doorbell->u.db_mmio.irq,
|
||||
.irq_flags = IRQF_SHARED,
|
||||
.type = PCI_EPF_DOORBELL_EMBEDDED,
|
||||
.bar = doorbell->bar,
|
||||
.offset = (doorbell->bar == NO_BAR) ? 0 :
|
||||
|
|
@ -198,28 +199,31 @@ static int pci_epf_alloc_doorbell_msi(struct pci_epf *epf, u16 num_db)
|
|||
int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db)
|
||||
{
|
||||
struct pci_epc *epc = epf->epc;
|
||||
struct pci_epf *first_epf;
|
||||
struct device *dev = &epf->dev;
|
||||
int ret;
|
||||
|
||||
/* TODO: Multi-EPF support */
|
||||
if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) {
|
||||
dev_err(dev, "Doorbell doesn't support multiple EPF\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (epf->db_msg)
|
||||
return -EBUSY;
|
||||
|
||||
ret = pci_epf_alloc_doorbell_msi(epf, num_db);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Fall back to embedded doorbell only when platform MSI is unavailable
|
||||
* for this EPC.
|
||||
* The MSI-backed doorbell path currently targets the first EPF attached
|
||||
* to the EPC. Let non-first EPFs try the embedded doorbell instead.
|
||||
*/
|
||||
if (ret != -ENODEV)
|
||||
return ret;
|
||||
first_epf = list_first_entry_or_null(&epc->pci_epf, struct pci_epf,
|
||||
list);
|
||||
if (first_epf == epf) {
|
||||
ret = pci_epf_alloc_doorbell_msi(epf, num_db);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Fall back to embedded doorbell only when platform MSI is
|
||||
* unavailable for this EPC.
|
||||
*/
|
||||
if (ret != -ENODEV)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pci_epf_alloc_doorbell_embedded(epf, num_db);
|
||||
if (ret) {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ int pciehp_poll_time;
|
|||
* not really modular, but the easiest way to keep compat with existing
|
||||
* bootargs behaviour is to continue using module_param here.
|
||||
*/
|
||||
module_param(pciehp_poll_mode, bool, 0644);
|
||||
module_param(pciehp_poll_mode, bool, 0444);
|
||||
module_param(pciehp_poll_time, int, 0644);
|
||||
MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
|
||||
MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
|
||||
|
|
@ -258,7 +258,7 @@ static bool pme_is_native(struct pcie_device *dev)
|
|||
const struct pci_host_bridge *host;
|
||||
|
||||
host = pci_find_host_bridge(dev->port->bus);
|
||||
return pcie_ports_native || host->native_pme;
|
||||
return host->native_pme;
|
||||
}
|
||||
|
||||
static void pciehp_disable_interrupt(struct pcie_device *dev)
|
||||
|
|
|
|||
|
|
@ -179,29 +179,6 @@ bool pci_iov_is_memory_decoding_enabled(struct pci_dev *dev)
|
|||
return cmd & PCI_SRIOV_CTRL_MSE;
|
||||
}
|
||||
|
||||
static void pci_read_vf_config_common(struct pci_dev *virtfn)
|
||||
{
|
||||
struct pci_dev *physfn = virtfn->physfn;
|
||||
|
||||
/*
|
||||
* Some config registers are the same across all associated VFs.
|
||||
* Read them once from VF0 so we can skip reading them from the
|
||||
* other VFs.
|
||||
*
|
||||
* PCIe r4.0, sec 9.3.4.1, technically doesn't require all VFs to
|
||||
* have the same Revision ID and Subsystem ID, but we assume they
|
||||
* do.
|
||||
*/
|
||||
pci_read_config_dword(virtfn, PCI_CLASS_REVISION,
|
||||
&physfn->sriov->class);
|
||||
pci_read_config_byte(virtfn, PCI_HEADER_TYPE,
|
||||
&physfn->sriov->hdr_type);
|
||||
pci_read_config_word(virtfn, PCI_SUBSYSTEM_VENDOR_ID,
|
||||
&physfn->sriov->subsystem_vendor);
|
||||
pci_read_config_word(virtfn, PCI_SUBSYSTEM_ID,
|
||||
&physfn->sriov->subsystem_device);
|
||||
}
|
||||
|
||||
int pci_iov_sysfs_link(struct pci_dev *dev,
|
||||
struct pci_dev *virtfn, int id)
|
||||
{
|
||||
|
|
@ -329,8 +306,6 @@ static struct pci_dev *pci_iov_scan_device(struct pci_dev *dev, int id,
|
|||
virtfn->physfn = pci_dev_get(dev);
|
||||
virtfn->no_command_memory = 1;
|
||||
|
||||
if (id == 0)
|
||||
pci_read_vf_config_common(virtfn);
|
||||
|
||||
rc = pci_setup_device(virtfn);
|
||||
if (rc) {
|
||||
|
|
|
|||
|
|
@ -236,9 +236,8 @@ static void pci_p2pdma_release(void *data)
|
|||
return;
|
||||
|
||||
/* Flush and disable pci_alloc_p2p_mem() */
|
||||
pdev->p2pdma = NULL;
|
||||
if (p2pdma->pool)
|
||||
synchronize_rcu();
|
||||
RCU_INIT_POINTER(pdev->p2pdma, NULL);
|
||||
synchronize_rcu();
|
||||
xa_destroy(&p2pdma->map_types);
|
||||
|
||||
if (!p2pdma->pool)
|
||||
|
|
@ -440,8 +439,8 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
|
|||
goto pgmap_free;
|
||||
}
|
||||
|
||||
error = devm_add_action_or_reset(&pdev->dev, pci_p2pdma_unmap_mappings,
|
||||
p2p_pgmap);
|
||||
error = devm_add_action(&pdev->dev, pci_p2pdma_unmap_mappings,
|
||||
p2p_pgmap);
|
||||
if (error)
|
||||
goto pages_free;
|
||||
|
||||
|
|
@ -451,13 +450,15 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size,
|
|||
range_len(&pgmap->range), dev_to_node(&pdev->dev),
|
||||
&pgmap->ref);
|
||||
if (error)
|
||||
goto pages_free;
|
||||
goto mappings_remove;
|
||||
|
||||
pci_info(pdev, "added peer-to-peer DMA memory %#llx-%#llx\n",
|
||||
pgmap->range.start, pgmap->range.end);
|
||||
|
||||
return 0;
|
||||
|
||||
mappings_remove:
|
||||
devm_remove_action(&pdev->dev, pci_p2pdma_unmap_mappings, p2p_pgmap);
|
||||
pages_free:
|
||||
devm_memunmap_pages(&pdev->dev, pgmap);
|
||||
pgmap_free:
|
||||
|
|
@ -542,9 +543,13 @@ static const struct pci_p2pdma_whitelist_entry {
|
|||
/* Intel Xeon E5/Core i7 */
|
||||
{PCI_VENDOR_ID_INTEL, 0x3c00, REQ_SAME_HOST_BRIDGE},
|
||||
{PCI_VENDOR_ID_INTEL, 0x3c01, REQ_SAME_HOST_BRIDGE},
|
||||
/* Intel Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D */
|
||||
{PCI_VENDOR_ID_INTEL, 0x6f00, REQ_SAME_HOST_BRIDGE},
|
||||
/* Intel Xeon E7 v3/Xeon E5 v3/Core i7 */
|
||||
{PCI_VENDOR_ID_INTEL, 0x2f00, REQ_SAME_HOST_BRIDGE},
|
||||
{PCI_VENDOR_ID_INTEL, 0x2f01, REQ_SAME_HOST_BRIDGE},
|
||||
/* Intel Haswell (client) */
|
||||
{PCI_VENDOR_ID_INTEL, 0x0c08, REQ_SAME_HOST_BRIDGE},
|
||||
/* Intel Skylake-E */
|
||||
{PCI_VENDOR_ID_INTEL, 0x2030, 0},
|
||||
{PCI_VENDOR_ID_INTEL, 0x2031, 0},
|
||||
|
|
@ -569,6 +574,14 @@ static const struct pci_p2pdma_whitelist_entry {
|
|||
{PCI_VENDOR_ID_NVIDIA, 0x2f96, 0},
|
||||
{PCI_VENDOR_ID_NVIDIA, 0x2f97, 0},
|
||||
{PCI_VENDOR_ID_NVIDIA, 0x2f98, 0},
|
||||
/* Zhaoxin KX-6000/KH-40000/KX-6000G/KX-7000/KH-50000 */
|
||||
{PCI_VENDOR_ID_ZHAOXIN, 0x1003, REQ_SAME_HOST_BRIDGE},
|
||||
{PCI_VENDOR_ID_ZHAOXIN, 0x1005, REQ_SAME_HOST_BRIDGE},
|
||||
{PCI_VENDOR_ID_ZHAOXIN, 0x1006, REQ_SAME_HOST_BRIDGE},
|
||||
{PCI_VENDOR_ID_ZHAOXIN, 0x1007, REQ_SAME_HOST_BRIDGE},
|
||||
{PCI_VENDOR_ID_ZHAOXIN, 0x1008, 0},
|
||||
/* Alibaba T-HEAD Yitian 710 CPU */
|
||||
{PCI_VENDOR_ID_ALIBABA, 0x8000, 0},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
@ -708,7 +721,6 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
|
|||
{
|
||||
enum pci_p2pdma_map_type map_type = PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
|
||||
struct pci_dev *a = provider, *b = client, *bb;
|
||||
bool acs_redirects = false;
|
||||
struct pci_p2pdma *p2pdma;
|
||||
struct seq_buf acs_list;
|
||||
int acs_cnt = 0;
|
||||
|
|
@ -771,17 +783,18 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
|
|||
}
|
||||
|
||||
if (verbose) {
|
||||
acs_list.buffer[acs_list.len-1] = 0; /* drop final semicolon */
|
||||
/* Drop the final semicolon; the list is not empty here */
|
||||
if (!seq_buf_has_overflowed(&acs_list))
|
||||
acs_list.buffer[acs_list.len - 1] = '\0';
|
||||
pci_warn(client, "ACS redirect is set between the client and provider (%s)\n",
|
||||
pci_name(provider));
|
||||
pci_warn(client, "to disable ACS redirect for this path, add the kernel parameter: pci=disable_acs_redir=%s\n",
|
||||
acs_list.buffer);
|
||||
seq_buf_str(&acs_list));
|
||||
}
|
||||
acs_redirects = true;
|
||||
|
||||
map_through_host_bridge:
|
||||
if (!cpu_supports_p2pdma() &&
|
||||
!host_bridge_whitelist(provider, client, acs_redirects)) {
|
||||
!host_bridge_whitelist(provider, client, verbose)) {
|
||||
if (verbose)
|
||||
pci_warn(client, "cannot be used for peer-to-peer DMA as the client and provider (%s) do not share an upstream bridge or whitelisted host bridge\n",
|
||||
pci_name(provider));
|
||||
|
|
@ -867,7 +880,13 @@ static bool pci_has_p2pmem(struct pci_dev *pdev)
|
|||
|
||||
rcu_read_lock();
|
||||
p2pdma = rcu_dereference(pdev->p2pdma);
|
||||
res = p2pdma && p2pdma->p2pmem_published;
|
||||
|
||||
/*
|
||||
* The callers hand the result to pci_alloc_p2pmem(), so only a
|
||||
* provider backed by a pool is of any use here. pcim_p2pdma_init()
|
||||
* creates providers without one.
|
||||
*/
|
||||
res = p2pdma && p2pdma->pool && p2pdma->p2pmem_published;
|
||||
rcu_read_unlock();
|
||||
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -812,9 +812,6 @@ bool pciehp_is_native(struct pci_dev *bridge)
|
|||
if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
|
||||
return false;
|
||||
|
||||
if (pcie_ports_native)
|
||||
return true;
|
||||
|
||||
host = pci_find_host_bridge(bridge->bus);
|
||||
return host->native_pcie_hotplug;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -818,6 +818,52 @@ static void pci_pm_complete(struct device *dev)
|
|||
|
||||
#endif /* !CONFIG_PM_SLEEP */
|
||||
|
||||
#if defined(CONFIG_SUSPEND) || defined(CONFIG_HIBERNATE_CALLBACKS)
|
||||
/**
|
||||
* pci_pm_suspend_noirq_common - prepare a device to enter a low-power state
|
||||
* @pci_dev: pci device
|
||||
*
|
||||
* Save the device state and decide whether bus-level power management should
|
||||
* skipped. Returns true if bus-level power management should be skipped,
|
||||
* false otherwise.
|
||||
*/
|
||||
static bool pci_pm_suspend_noirq_common(struct pci_dev *pci_dev)
|
||||
{
|
||||
if (!pci_dev->state_saved) {
|
||||
pci_save_state(pci_dev);
|
||||
|
||||
/*
|
||||
* If the device is a bridge with a child in D0 below it,
|
||||
* it needs to stay in D0, so check skip_bus_pm to avoid
|
||||
* putting it into a low-power state in that case.
|
||||
*/
|
||||
if (!pci_dev->skip_bus_pm && pci_power_manageable(pci_dev))
|
||||
pci_prepare_to_sleep(pci_dev);
|
||||
}
|
||||
|
||||
pci_dbg(pci_dev, "PCI PM: Sleep power state: %s\n",
|
||||
pci_power_name(pci_dev->current_state));
|
||||
|
||||
if (pci_dev->current_state == PCI_D0) {
|
||||
pci_dev->skip_bus_pm = true;
|
||||
/*
|
||||
* Per PCI PM r1.2, table 6-1, a bridge must be in D0 if any
|
||||
* downstream device is in D0, so avoid changing the power state
|
||||
* of the parent bridge by setting the skip_bus_pm flag for it.
|
||||
*/
|
||||
if (pci_dev->bus->self)
|
||||
pci_dev->bus->self->skip_bus_pm = true;
|
||||
}
|
||||
|
||||
if (pci_dev->skip_bus_pm && pm_suspend_no_platform()) {
|
||||
pci_dbg(pci_dev, "PCI PM: Skipped\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_SUSPEND || CONFIG_HIBERNATE_CALLBACKS */
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
static void pcie_pme_root_status_cleanup(struct pci_dev *pci_dev)
|
||||
{
|
||||
|
|
@ -907,6 +953,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
|
|||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
bool skip_bus_pm;
|
||||
|
||||
if (dev_pm_skip_suspend(dev))
|
||||
return 0;
|
||||
|
|
@ -937,36 +984,10 @@ static int pci_pm_suspend_noirq(struct device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
if (!pci_dev->state_saved) {
|
||||
pci_save_state(pci_dev);
|
||||
skip_bus_pm = pci_pm_suspend_noirq_common(pci_dev);
|
||||
|
||||
/*
|
||||
* If the device is a bridge with a child in D0 below it,
|
||||
* it needs to stay in D0, so check skip_bus_pm to avoid
|
||||
* putting it into a low-power state in that case.
|
||||
*/
|
||||
if (!pci_dev->skip_bus_pm && pci_power_manageable(pci_dev))
|
||||
pci_prepare_to_sleep(pci_dev);
|
||||
}
|
||||
|
||||
pci_dbg(pci_dev, "PCI PM: Suspend power state: %s\n",
|
||||
pci_power_name(pci_dev->current_state));
|
||||
|
||||
if (pci_dev->current_state == PCI_D0) {
|
||||
pci_dev->skip_bus_pm = true;
|
||||
/*
|
||||
* Per PCI PM r1.2, table 6-1, a bridge must be in D0 if any
|
||||
* downstream device is in D0, so avoid changing the power state
|
||||
* of the parent bridge by setting the skip_bus_pm flag for it.
|
||||
*/
|
||||
if (pci_dev->bus->self)
|
||||
pci_dev->bus->self->skip_bus_pm = true;
|
||||
}
|
||||
|
||||
if (pci_dev->skip_bus_pm && pm_suspend_no_platform()) {
|
||||
pci_dbg(pci_dev, "PCI PM: Skipped\n");
|
||||
if (skip_bus_pm)
|
||||
goto Fixup;
|
||||
}
|
||||
|
||||
set_unknown:
|
||||
pci_pm_set_unknown_state(pci_dev);
|
||||
|
|
@ -1196,6 +1217,8 @@ static int pci_pm_poweroff(struct device *dev)
|
|||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
|
||||
pci_dev->skip_bus_pm = false;
|
||||
|
||||
if (pci_has_legacy_pm_support(pci_dev))
|
||||
return pci_legacy_suspend(dev, PMSG_HIBERNATE);
|
||||
|
||||
|
|
@ -1238,6 +1261,7 @@ static int pci_pm_poweroff_noirq(struct device *dev)
|
|||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
bool skip_bus_pm;
|
||||
|
||||
if (dev_pm_skip_suspend(dev))
|
||||
return 0;
|
||||
|
|
@ -1251,16 +1275,26 @@ static int pci_pm_poweroff_noirq(struct device *dev)
|
|||
}
|
||||
|
||||
if (pm->poweroff_noirq) {
|
||||
pci_power_t prev = pci_dev->current_state;
|
||||
int error;
|
||||
|
||||
error = pm->poweroff_noirq(dev);
|
||||
suspend_report_result(dev, pm->poweroff_noirq, error);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (!pci_dev->state_saved && pci_dev->current_state != PCI_D0
|
||||
&& pci_dev->current_state != PCI_UNKNOWN) {
|
||||
pci_WARN_ONCE(pci_dev, pci_dev->current_state != prev,
|
||||
"PCI PM: State of device not saved by %pS\n",
|
||||
pm->poweroff_noirq);
|
||||
goto Fixup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pci_dev->state_saved && !pci_has_subordinate(pci_dev))
|
||||
pci_prepare_to_sleep(pci_dev);
|
||||
skip_bus_pm = pci_pm_suspend_noirq_common(pci_dev);
|
||||
if (skip_bus_pm)
|
||||
goto Fixup;
|
||||
|
||||
/*
|
||||
* The reason for doing this here is the same as for the analogous code
|
||||
|
|
@ -1269,6 +1303,7 @@ static int pci_pm_poweroff_noirq(struct device *dev)
|
|||
if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)
|
||||
pci_write_config_word(pci_dev, PCI_COMMAND, 0);
|
||||
|
||||
Fixup:
|
||||
pci_fixup_device(pci_fixup_suspend_late, pci_dev);
|
||||
|
||||
return 0;
|
||||
|
|
@ -1278,10 +1313,15 @@ static int pci_pm_restore_noirq(struct device *dev)
|
|||
{
|
||||
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
|
||||
pci_power_t prev_state = pci_dev->current_state;
|
||||
bool skip_bus_pm = pci_dev->skip_bus_pm;
|
||||
|
||||
pci_pm_default_resume_early(pci_dev);
|
||||
pci_fixup_device(pci_fixup_resume_early, pci_dev);
|
||||
|
||||
if (!skip_bus_pm && prev_state == PCI_D3cold)
|
||||
pci_pm_bridge_power_up_actions(pci_dev);
|
||||
|
||||
if (pci_has_legacy_pm_support(pci_dev))
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ static int dsm_label_utf16s_to_utf8s(union acpi_object *obj, char *buf)
|
|||
int len;
|
||||
|
||||
len = utf16s_to_utf8s((const wchar_t *)obj->buffer.pointer,
|
||||
obj->buffer.length,
|
||||
obj->buffer.length / sizeof(wchar_t),
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
buf, PAGE_SIZE - 1);
|
||||
buf[len++] = '\n';
|
||||
|
|
@ -157,40 +157,53 @@ static int dsm_get_label(struct device *dev, char *buf,
|
|||
{
|
||||
acpi_handle handle = ACPI_HANDLE(dev);
|
||||
union acpi_object *obj, *tmp;
|
||||
int len = 0;
|
||||
int len;
|
||||
|
||||
if (!handle)
|
||||
return -1;
|
||||
return -ENODEV;
|
||||
|
||||
obj = acpi_evaluate_dsm(handle, &pci_acpi_dsm_guid, 0x2,
|
||||
DSM_PCI_DEVICE_NAME, NULL);
|
||||
if (!obj)
|
||||
return -1;
|
||||
return -EIO;
|
||||
|
||||
tmp = obj->package.elements;
|
||||
if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 2 &&
|
||||
tmp[0].type == ACPI_TYPE_INTEGER &&
|
||||
(tmp[1].type == ACPI_TYPE_STRING ||
|
||||
tmp[1].type == ACPI_TYPE_BUFFER)) {
|
||||
/*
|
||||
* The second string element is optional even when
|
||||
* this _DSM is implemented; when not implemented,
|
||||
* this entry must return a null string.
|
||||
*/
|
||||
if (attr == ACPI_ATTR_INDEX_SHOW) {
|
||||
len = sysfs_emit(buf, "%llu\n", tmp->integer.value);
|
||||
} else if (attr == ACPI_ATTR_LABEL_SHOW) {
|
||||
if (tmp[1].type == ACPI_TYPE_STRING)
|
||||
len = sysfs_emit(buf, "%s\n",
|
||||
tmp[1].string.pointer);
|
||||
else if (tmp[1].type == ACPI_TYPE_BUFFER)
|
||||
len = dsm_label_utf16s_to_utf8s(tmp + 1, buf);
|
||||
}
|
||||
if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 2) {
|
||||
len = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmp = obj->package.elements;
|
||||
if (tmp[0].type != ACPI_TYPE_INTEGER) {
|
||||
len = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (attr == ACPI_ATTR_INDEX_SHOW) {
|
||||
len = sysfs_emit(buf, "%llu\n", tmp[0].integer.value);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Per PCI Firmware r3.3, sec 4.6.7, the device name is optional
|
||||
* even when this _DSM is implemented. When not implemented, this
|
||||
* entry must return a NULL string.
|
||||
*/
|
||||
switch (tmp[1].type) {
|
||||
case ACPI_TYPE_STRING:
|
||||
len = sysfs_emit(buf, "%s\n", tmp[1].string.pointer);
|
||||
break;
|
||||
case ACPI_TYPE_BUFFER:
|
||||
len = dsm_label_utf16s_to_utf8s(&tmp[1], buf);
|
||||
break;
|
||||
default:
|
||||
len = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
ACPI_FREE(obj);
|
||||
|
||||
return len > 0 ? len : -1;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t label_show(struct device *dev, struct device_attribute *attr,
|
||||
|
|
@ -217,11 +230,42 @@ static umode_t acpi_attr_is_visible(struct kobject *kobj, struct attribute *a,
|
|||
int n)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
union acpi_object *obj, *tmp;
|
||||
umode_t mode = 0;
|
||||
|
||||
if (!device_has_acpi_name(dev))
|
||||
return 0;
|
||||
|
||||
return a->mode;
|
||||
/*
|
||||
* The bitmap from _DSM function 0 only advertises function 7,
|
||||
* and whether the returned object can be parsed is a separate
|
||||
* question. Evaluate it and expose each attribute only if the
|
||||
* element it exports has one of the types the read path
|
||||
* accepts, mirroring the checks in dsm_get_label().
|
||||
*/
|
||||
obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &pci_acpi_dsm_guid, 0x2,
|
||||
DSM_PCI_DEVICE_NAME, NULL);
|
||||
if (!obj)
|
||||
return 0;
|
||||
|
||||
if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 2)
|
||||
goto out;
|
||||
|
||||
tmp = obj->package.elements;
|
||||
if (tmp[0].type != ACPI_TYPE_INTEGER)
|
||||
goto out;
|
||||
|
||||
if (a == &dev_attr_acpi_index.attr)
|
||||
mode = a->mode;
|
||||
else if (a == &dev_attr_label.attr &&
|
||||
(tmp[1].type == ACPI_TYPE_STRING ||
|
||||
tmp[1].type == ACPI_TYPE_BUFFER))
|
||||
mode = a->mode;
|
||||
|
||||
out:
|
||||
ACPI_FREE(obj);
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
const struct attribute_group pci_dev_acpi_attr_group = {
|
||||
|
|
|
|||
|
|
@ -1212,6 +1212,9 @@ static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
|
|||
if (port + count - 1 > pci_resource_end(pdev, bar))
|
||||
return -EINVAL;
|
||||
|
||||
if (!IS_ALIGNED(port, count))
|
||||
return -EINVAL;
|
||||
|
||||
switch (count) {
|
||||
case 1:
|
||||
if (write)
|
||||
|
|
|
|||
|
|
@ -1743,6 +1743,24 @@ static void pci_restore_pcie_state(struct pci_dev *dev)
|
|||
pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the saved copy of the Device Control register after changing it, so
|
||||
* that pci_restore_state() doesn't put back a stale value. Device Control is
|
||||
* the first register saved by pci_save_pcie_state().
|
||||
*/
|
||||
static void pcie_update_saved_devctl(struct pci_dev *dev, u16 clear, u16 set)
|
||||
{
|
||||
struct pci_cap_saved_state *save_state;
|
||||
u16 *devctl;
|
||||
|
||||
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
|
||||
if (!save_state)
|
||||
return;
|
||||
|
||||
devctl = (u16 *)&save_state->cap.data[0];
|
||||
*devctl = (*devctl & ~clear) | set;
|
||||
}
|
||||
|
||||
static int pci_save_pcix_state(struct pci_dev *dev)
|
||||
{
|
||||
int pos;
|
||||
|
|
@ -3262,7 +3280,15 @@ void pci_pm_init(struct pci_dev *dev)
|
|||
}
|
||||
|
||||
poweron:
|
||||
pci_pm_power_up_and_verify_state(dev);
|
||||
pci_update_current_state(dev, PCI_UNKNOWN);
|
||||
if (dev->current_state == PCI_D0) {
|
||||
/* No need for full power-up, just sync-up platform state */
|
||||
platform_pci_set_power_state(dev, PCI_D0);
|
||||
} else {
|
||||
/* Power up the device and restore BARs if necessary */
|
||||
pci_set_full_power_state(dev, false);
|
||||
}
|
||||
pci_update_current_state(dev, PCI_D0);
|
||||
pm_runtime_forbid(&dev->dev);
|
||||
|
||||
/*
|
||||
|
|
@ -5990,8 +6016,12 @@ int pcie_set_mps(struct pci_dev *dev, int mps)
|
|||
|
||||
ret = pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
|
||||
PCI_EXP_DEVCTL_PAYLOAD, v);
|
||||
if (ret)
|
||||
return pcibios_err_to_errno(ret);
|
||||
|
||||
return pcibios_err_to_errno(ret);
|
||||
pcie_update_saved_devctl(dev, PCI_EXP_DEVCTL_PAYLOAD, v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(pcie_set_mps);
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,15 @@ struct pcie_tlp_log;
|
|||
*/
|
||||
#define PCIE_T_PERST_CLK_US 100
|
||||
|
||||
/*
|
||||
* PERST# active time.
|
||||
*
|
||||
* See the "Power Sequencing and Reset Signal Timings" table of the PCI Express
|
||||
* Card Electromechanical Specification, Revision 6.0, Section 2.11.2, Symbol
|
||||
* "T_PERST".
|
||||
*/
|
||||
#define PCIE_T_PERST_US 100
|
||||
|
||||
/*
|
||||
* PCIe r6.0, sec 5.3.3.2.1 <PME Synchronization>
|
||||
* Recommends 1ms to 10ms timeout to check L2 ready.
|
||||
|
|
@ -502,6 +511,7 @@ enum pci_bar_type {
|
|||
pci_bar_mem64, /* A 64-bit memory BAR */
|
||||
};
|
||||
|
||||
void devm_pci_alloc_host_bridge_release(void *data);
|
||||
struct device *pci_get_host_bridge_device(struct pci_dev *dev);
|
||||
void pci_put_host_bridge_device(struct device *dev);
|
||||
|
||||
|
|
@ -711,10 +721,6 @@ struct pci_sriov {
|
|||
u16 driver_max_VFs; /* Max num VFs driver supports */
|
||||
struct pci_dev *dev; /* Lowest numbered PF */
|
||||
struct pci_dev *self; /* This PF */
|
||||
u32 class; /* VF device */
|
||||
u8 hdr_type; /* VF header type */
|
||||
u16 subsystem_vendor; /* VF subsystem vendor */
|
||||
u16 subsystem_device; /* VF subsystem device */
|
||||
resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */
|
||||
u16 vf_rebar_cap; /* VF Resizable BAR capability offset */
|
||||
bool drivers_autoprobe; /* Auto probing of VFs by driver */
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ int pcie_aer_is_native(struct pci_dev *dev)
|
|||
if (!dev->aer_cap)
|
||||
return 0;
|
||||
|
||||
return pcie_ports_native || host->native_aer;
|
||||
return host->native_aer;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(pcie_aer_is_native, "CXL");
|
||||
|
||||
|
|
@ -1341,7 +1341,6 @@ static void handle_error_source(struct pci_dev *dev, struct aer_err_info *info)
|
|||
{
|
||||
cxl_rch_handle_error(dev, info);
|
||||
pci_aer_handle_error(dev, info);
|
||||
pci_dev_put(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI_APEI_PCIEAER
|
||||
|
|
@ -1369,14 +1368,13 @@ static void aer_recover_work_func(struct work_struct *work)
|
|||
while (kfifo_get(&aer_recover_ring, &entry)) {
|
||||
pdev = pci_get_domain_bus_and_slot(entry.domain, entry.bus,
|
||||
entry.devfn);
|
||||
if (!pdev) {
|
||||
if (!pdev)
|
||||
pr_err_ratelimited("%04x:%02x:%02x.%x: no pci_dev found\n",
|
||||
entry.domain, entry.bus,
|
||||
PCI_SLOT(entry.devfn),
|
||||
PCI_FUNC(entry.devfn));
|
||||
continue;
|
||||
}
|
||||
pci_print_aer(pdev, entry.severity, entry.regs);
|
||||
else
|
||||
pci_print_aer(pdev, entry.severity, entry.regs);
|
||||
|
||||
regs = entry.regs;
|
||||
if (entry.severity == AER_CORRECTABLE)
|
||||
|
|
@ -1394,6 +1392,9 @@ static void aer_recover_work_func(struct work_struct *work)
|
|||
ghes_estatus_pool_region_free((unsigned long)entry.regs,
|
||||
sizeof(struct aer_capability_regs));
|
||||
|
||||
if (!pdev)
|
||||
continue;
|
||||
|
||||
if (err) {
|
||||
if (entry.severity == AER_NONFATAL)
|
||||
pcie_do_recovery(pdev, pci_channel_io_normal,
|
||||
|
|
@ -1414,6 +1415,24 @@ static void aer_recover_work_func(struct work_struct *work)
|
|||
static DEFINE_SPINLOCK(aer_recover_ring_lock);
|
||||
static DECLARE_WORK(aer_recover_work, aer_recover_work_func);
|
||||
|
||||
/**
|
||||
* aer_recover_queue - queue an AER error record reported by firmware
|
||||
* @domain: PCI domain (segment) of the device that reported the error
|
||||
* @bus: bus number of the device that reported the error
|
||||
* @devfn: encoded device and function number, as returned by PCI_DEVFN()
|
||||
* @severity: AER_CORRECTABLE, AER_NONFATAL or AER_FATAL
|
||||
* @aer_regs: snapshot of the device's AER Capability registers
|
||||
*
|
||||
* Queue an error record received from firmware through APEI GHES. The
|
||||
* record is processed later from a workqueue, which logs the error and,
|
||||
* for uncorrectable errors, attempts recovery of the device.
|
||||
*
|
||||
* Takes ownership of @aer_regs, which must have been allocated from
|
||||
* ghes_estatus_pool with a size of sizeof(struct aer_capability_regs).
|
||||
* The buffer is freed with ghes_estatus_pool_region_free() by the work
|
||||
* item that processes the record, or immediately if the queue is full.
|
||||
* The caller must not access or free @aer_regs after this call.
|
||||
*/
|
||||
void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
|
||||
int severity, struct aer_capability_regs *aer_regs)
|
||||
{
|
||||
|
|
@ -1426,11 +1445,14 @@ void aer_recover_queue(int domain, unsigned int bus, unsigned int devfn,
|
|||
};
|
||||
|
||||
if (kfifo_in_spinlocked(&aer_recover_ring, &entry, 1,
|
||||
&aer_recover_ring_lock))
|
||||
&aer_recover_ring_lock)) {
|
||||
schedule_work(&aer_recover_work);
|
||||
else
|
||||
} else {
|
||||
pr_err("buffer overflow in recovery for %04x:%02x:%02x.%x\n",
|
||||
domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
|
||||
ghes_estatus_pool_region_free((unsigned long)aer_regs,
|
||||
sizeof(struct aer_capability_regs));
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(aer_recover_queue);
|
||||
#endif
|
||||
|
|
@ -1529,6 +1551,7 @@ static inline void aer_process_err_devices(struct aer_err_info *e_info)
|
|||
for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) {
|
||||
if (aer_get_device_error_info(e_info, i))
|
||||
handle_error_source(e_info->dev[i], e_info);
|
||||
pci_dev_put(e_info->dev[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1858,7 +1881,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
|||
*/
|
||||
aer = root ? root->aer_cap : 0;
|
||||
|
||||
if ((host->native_aer || pcie_ports_native) && aer)
|
||||
if (host->native_aer && aer)
|
||||
aer_disable_irq(root);
|
||||
|
||||
if (type == PCI_EXP_TYPE_RC_EC || type == PCI_EXP_TYPE_RC_END) {
|
||||
|
|
@ -1873,7 +1896,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
|
|||
pci_is_root_bus(dev->bus) ? "Root" : "Downstream", rc);
|
||||
}
|
||||
|
||||
if ((host->native_aer || pcie_ports_native) && aer) {
|
||||
if (host->native_aer && aer) {
|
||||
/* Clear Root Error Status */
|
||||
pci_read_config_dword(root, aer + PCI_ERR_ROOT_STATUS, ®32);
|
||||
pci_write_config_dword(root, aer + PCI_ERR_ROOT_STATUS, reg32);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ static bool cxl_error_is_native(struct pci_dev *dev)
|
|||
{
|
||||
struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
|
||||
|
||||
return (pcie_ports_native || host->native_aer);
|
||||
return host->native_aer;
|
||||
}
|
||||
|
||||
static int cxl_rch_handle_error_iter(struct pci_dev *dev, void *data)
|
||||
|
|
|
|||
|
|
@ -1504,16 +1504,23 @@ static int __pci_disable_link_state(struct pci_dev *pdev, int state, bool locked
|
|||
return -EINVAL;
|
||||
/*
|
||||
* A driver requested that ASPM be disabled on this device, but
|
||||
* if we don't have permission to manage ASPM (e.g., on ACPI
|
||||
* if we might not have permission to manage ASPM (e.g., on ACPI
|
||||
* systems we have to observe the FADT ACPI_FADT_NO_ASPM bit and
|
||||
* the _OSC method), we can't honor that request. Windows has
|
||||
* a similar mechanism using "PciASPMOptOut", which is also
|
||||
* ignored in this situation.
|
||||
* the _OSC method), previously we chose to not honor disable
|
||||
* request in that case. Windows has a similar mechanism using
|
||||
* "PciASPMOptOut", which is also ignored in this situation.
|
||||
*
|
||||
* Not honoring the requests to disable ASPM, however, led to
|
||||
* drivers forcing ASPM off on their own. As such changes of ASPM
|
||||
* state are not tracked by this service driver, the state kept here
|
||||
* became out of sync.
|
||||
*
|
||||
* Therefore, honor ASPM disable requests even when OS does not have
|
||||
* ASPM control. Plain disable for ASPM is assumed to be slightly
|
||||
* safer than fully managing it.
|
||||
*/
|
||||
if (aspm_disabled) {
|
||||
pci_warn(pdev, "can't disable ASPM; OS doesn't have ASPM control\n");
|
||||
return -EPERM;
|
||||
}
|
||||
if (aspm_disabled)
|
||||
pci_warn(pdev, "OS doesn't have ASPM control, disabling ASPM anyway\n");
|
||||
|
||||
if (!locked)
|
||||
down_read(&pci_bus_sem);
|
||||
|
|
@ -1540,13 +1547,13 @@ int pci_disable_link_state_locked(struct pci_dev *pdev, int state)
|
|||
EXPORT_SYMBOL(pci_disable_link_state_locked);
|
||||
|
||||
/**
|
||||
* pci_disable_link_state - Disable device's link state, so the link will
|
||||
* never enter specific states. Note that if the BIOS didn't grant ASPM
|
||||
* control to the OS, this does nothing because we can't touch the LNKCTL
|
||||
* register. Returns 0 or a negative errno.
|
||||
*
|
||||
* pci_disable_link_state - Disable device's link state
|
||||
* @pdev: PCI device
|
||||
* @state: ASPM link state to disable
|
||||
*
|
||||
* Disable device's link state so the link will never enter specific states.
|
||||
*
|
||||
* Return: 0 or a negative errno
|
||||
*/
|
||||
int pci_disable_link_state(struct pci_dev *pdev, int state)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev,
|
|||
* it is responsible for clearing this status. In that case, the
|
||||
* signaling device may not even be visible to the OS.
|
||||
*/
|
||||
if (host->native_aer || pcie_ports_native) {
|
||||
if (host->native_aer) {
|
||||
pcie_clear_device_status(dev);
|
||||
pci_aer_clear_nonfatal_status(dev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ static int get_port_device_capability(struct pci_dev *dev)
|
|||
if (dev->is_pciehp &&
|
||||
(pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
|
||||
pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) &&
|
||||
(pcie_ports_native || host->native_pcie_hotplug)) {
|
||||
host->native_pcie_hotplug) {
|
||||
services |= PCIE_PORT_SERVICE_HP;
|
||||
|
||||
/*
|
||||
|
|
@ -240,14 +240,14 @@ static int get_port_device_capability(struct pci_dev *dev)
|
|||
if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
|
||||
pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) &&
|
||||
dev->aer_cap && pci_aer_available() &&
|
||||
(pcie_ports_native || host->native_aer))
|
||||
host->native_aer)
|
||||
services |= PCIE_PORT_SERVICE_AER;
|
||||
#endif
|
||||
|
||||
/* Root Ports and Root Complex Event Collectors may generate PMEs */
|
||||
if ((pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT ||
|
||||
pci_pcie_type(dev) == PCI_EXP_TYPE_RC_EC) &&
|
||||
(pcie_ports_native || host->native_pme)) {
|
||||
host->native_pme) {
|
||||
services |= PCIE_PORT_SERVICE_PME;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@
|
|||
|
||||
#define PCIE_PORT_DEVICE_MAXSERVICES 5
|
||||
|
||||
extern bool pcie_ports_dpc_native;
|
||||
|
||||
#ifdef CONFIG_PCIEAER
|
||||
int pcie_aer_init(void);
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -658,6 +658,8 @@ static const struct device_type pci_host_bridge_type = {
|
|||
|
||||
static void pci_init_host_bridge(struct pci_host_bridge *bridge)
|
||||
{
|
||||
bool port_services = IS_ENABLED(CONFIG_PCIEPORTBUS);
|
||||
|
||||
INIT_LIST_HEAD(&bridge->windows);
|
||||
INIT_LIST_HEAD(&bridge->dma_ranges);
|
||||
INIT_LIST_HEAD(&bridge->ports);
|
||||
|
|
@ -668,12 +670,12 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge)
|
|||
* may implement its own AER handling and use _OSC to prevent the
|
||||
* OS from interfering.
|
||||
*/
|
||||
bridge->native_aer = 1;
|
||||
bridge->native_pcie_hotplug = 1;
|
||||
bridge->native_aer = port_services;
|
||||
bridge->native_pcie_hotplug = port_services;
|
||||
bridge->native_shpc_hotplug = 1;
|
||||
bridge->native_pme = 1;
|
||||
bridge->native_pme = port_services;
|
||||
bridge->native_ltr = 1;
|
||||
bridge->native_dpc = 1;
|
||||
bridge->native_dpc = port_services;
|
||||
bridge->domain_nr = PCI_DOMAIN_NR_NOT_SET;
|
||||
bridge->native_cxl_error = 1;
|
||||
bridge->dev.type = &pci_host_bridge_type;
|
||||
|
|
@ -696,10 +698,11 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv)
|
|||
}
|
||||
EXPORT_SYMBOL(pci_alloc_host_bridge);
|
||||
|
||||
static void devm_pci_alloc_host_bridge_release(void *data)
|
||||
void devm_pci_alloc_host_bridge_release(void *data)
|
||||
{
|
||||
pci_free_host_bridge(data);
|
||||
}
|
||||
EXPORT_SYMBOL(devm_pci_alloc_host_bridge_release);
|
||||
|
||||
struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev,
|
||||
size_t priv)
|
||||
|
|
@ -1651,6 +1654,8 @@ void set_pcie_port_type(struct pci_dev *pdev)
|
|||
pdev->aspm_l1_support = 1;
|
||||
#endif
|
||||
|
||||
pdev->supported_speeds = pcie_get_supported_speeds(pdev);
|
||||
|
||||
parent = pci_upstream_bridge(pdev);
|
||||
if (!parent)
|
||||
return;
|
||||
|
|
@ -1890,8 +1895,13 @@ static u32 pci_class(struct pci_dev *dev)
|
|||
u32 class;
|
||||
|
||||
#ifdef CONFIG_PCI_IOV
|
||||
if (dev->is_virtfn)
|
||||
return dev->physfn->sriov->class;
|
||||
if (dev->is_virtfn) {
|
||||
u8 rev;
|
||||
|
||||
if (pci_read_config_byte(dev, PCI_REVISION_ID, &rev))
|
||||
rev = 0;
|
||||
return (dev->physfn->class << 8) | rev;
|
||||
}
|
||||
#endif
|
||||
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
|
||||
return class;
|
||||
|
|
@ -1901,8 +1911,8 @@ static void pci_subsystem_ids(struct pci_dev *dev, u16 *vendor, u16 *device)
|
|||
{
|
||||
#ifdef CONFIG_PCI_IOV
|
||||
if (dev->is_virtfn) {
|
||||
*vendor = dev->physfn->sriov->subsystem_vendor;
|
||||
*device = dev->physfn->sriov->subsystem_device;
|
||||
*vendor = dev->physfn->subsystem_vendor;
|
||||
pci_read_config_word(dev, PCI_SUBSYSTEM_ID, device);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1916,7 +1926,7 @@ static u8 pci_hdr_type(struct pci_dev *dev)
|
|||
|
||||
#ifdef CONFIG_PCI_IOV
|
||||
if (dev->is_virtfn)
|
||||
return dev->physfn->sriov->hdr_type;
|
||||
return 0;
|
||||
#endif
|
||||
pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type);
|
||||
return hdr_type;
|
||||
|
|
@ -2074,9 +2084,6 @@ int pci_setup_device(struct pci_dev *dev)
|
|||
|
||||
set_pcie_untrusted(dev);
|
||||
|
||||
if (pci_is_pcie(dev))
|
||||
dev->supported_speeds = pcie_get_supported_speeds(dev);
|
||||
|
||||
/* "Unknown power state" */
|
||||
dev->current_state = PCI_UNKNOWN;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ config PCI_PWRCTRL_TC9563
|
|||
select PCI_PWRCTRL
|
||||
default m if ARCH_QCOM
|
||||
depends on I2C
|
||||
select AUXILIARY_BUS
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y here to enable the PCI Power Control driver of TC9563 PCIe
|
||||
switch.
|
||||
|
|
|
|||
|
|
@ -252,14 +252,16 @@ static int __pci_pwrctrl_power_on_device(struct device *dev)
|
|||
static int pci_pwrctrl_power_on_device(struct device_node *np)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *child;
|
||||
int ret = 0;
|
||||
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
for_each_available_child_of_node(np, child) {
|
||||
ret = pci_pwrctrl_power_on_device(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err_power_off;
|
||||
}
|
||||
|
||||
child = NULL;
|
||||
if (!pci_pwrctrl_is_required(np))
|
||||
return 0;
|
||||
|
||||
|
|
@ -279,6 +281,19 @@ static int pci_pwrctrl_power_on_device(struct device_node *np)
|
|||
|
||||
platform_device_put(pdev);
|
||||
|
||||
if (ret)
|
||||
goto err_power_off;
|
||||
|
||||
return 0;
|
||||
|
||||
err_power_off:
|
||||
for_each_available_child_of_node_scoped(np, tmp) {
|
||||
if (tmp == child)
|
||||
break;
|
||||
pci_pwrctrl_power_off_device(tmp);
|
||||
}
|
||||
of_node_put(child);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -388,6 +403,9 @@ static void pci_pwrctrl_destroy_device(struct device_node *np)
|
|||
for_each_available_child_of_node_scoped(np, child)
|
||||
pci_pwrctrl_destroy_device(child);
|
||||
|
||||
if (!pci_pwrctrl_is_required(np))
|
||||
return;
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
if (!pdev)
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ static void devm_slot_pwrctrl_release(void *data)
|
|||
struct slot_pwrctrl *slot = data;
|
||||
|
||||
regulator_bulk_free(slot->num_supplies, slot->supplies);
|
||||
kfree(slot->supplies);
|
||||
}
|
||||
|
||||
static int slot_pwrctrl_probe(struct platform_device *pdev)
|
||||
|
|
|
|||
|
|
@ -4,28 +4,28 @@
|
|||
*/
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-pwrctrl.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/soc/qcom/tc9563.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
#include "../pci.h"
|
||||
|
||||
#define TC9563_GPIO_CONFIG 0x801208
|
||||
#define TC9563_RESET_GPIO 0x801210
|
||||
|
||||
#define TC9563_PORT_L0S_DELAY 0x82496c
|
||||
#define TC9563_PORT_L1_DELAY 0x824970
|
||||
|
||||
|
|
@ -57,9 +57,6 @@
|
|||
#define TC9563_POWER_CONTROL 0x82b09c
|
||||
#define TC9563_POWER_CONTROL_OVREN 0x82b2c8
|
||||
|
||||
#define TC9563_GPIO_MASK 0xfffffff3
|
||||
#define TC9563_GPIO_DEASSERT_BITS 0xc /* Clear to deassert GPIO */
|
||||
|
||||
#define TC9563_TX_MARGIN_MIN_UA 400000
|
||||
|
||||
/*
|
||||
|
|
@ -70,11 +67,6 @@
|
|||
|
||||
#define TC9563_L0S_L1_DELAY_UNIT_NS 256 /* Each unit represents 256 ns */
|
||||
|
||||
struct tc9563_pwrctrl_reg_setting {
|
||||
unsigned int offset;
|
||||
unsigned int val;
|
||||
};
|
||||
|
||||
enum tc9563_pwrctrl_ports {
|
||||
TC9563_USP,
|
||||
TC9563_DSP1,
|
||||
|
|
@ -90,6 +82,7 @@ struct tc9563_pwrctrl_cfg {
|
|||
u8 nfts[2]; /* GEN1 & GEN2 */
|
||||
bool disable_dfe;
|
||||
bool disable_port;
|
||||
struct gpio_desc *reset;
|
||||
};
|
||||
|
||||
#define TC9563_PWRCTL_MAX_SUPPLY 6
|
||||
|
|
@ -110,14 +103,14 @@ struct tc9563_pwrctrl {
|
|||
struct tc9563_pwrctrl_cfg ep_cfg;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct i2c_adapter *adapter;
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
/*
|
||||
* downstream port power off sequence, hardcoding the address
|
||||
* as we don't know register names for these register offsets.
|
||||
*/
|
||||
static const struct tc9563_pwrctrl_reg_setting common_pwroff_seq[] = {
|
||||
static const struct reg_sequence common_pwroff_seq[] = {
|
||||
{0x82900c, 0x1},
|
||||
{0x829010, 0x1},
|
||||
{0x829018, 0x0},
|
||||
|
|
@ -140,7 +133,7 @@ static const struct tc9563_pwrctrl_reg_setting common_pwroff_seq[] = {
|
|||
{0x829114, 0x1},
|
||||
};
|
||||
|
||||
static const struct tc9563_pwrctrl_reg_setting dsp1_pwroff_seq[] = {
|
||||
static const struct reg_sequence dsp1_pwroff_seq[] = {
|
||||
{TC9563_PORT_ACCESS_ENABLE, 0x2},
|
||||
{TC9563_PORT_LANE_ACCESS_ENABLE, 0x3},
|
||||
{TC9563_POWER_CONTROL, 0x014f4804},
|
||||
|
|
@ -148,7 +141,7 @@ static const struct tc9563_pwrctrl_reg_setting dsp1_pwroff_seq[] = {
|
|||
{TC9563_PORT_ACCESS_ENABLE, 0x4},
|
||||
};
|
||||
|
||||
static const struct tc9563_pwrctrl_reg_setting dsp2_pwroff_seq[] = {
|
||||
static const struct reg_sequence dsp2_pwroff_seq[] = {
|
||||
{TC9563_PORT_ACCESS_ENABLE, 0x8},
|
||||
{TC9563_PORT_LANE_ACCESS_ENABLE, 0x1},
|
||||
{TC9563_POWER_CONTROL, 0x014f4804},
|
||||
|
|
@ -156,85 +149,13 @@ static const struct tc9563_pwrctrl_reg_setting dsp2_pwroff_seq[] = {
|
|||
{TC9563_PORT_ACCESS_ENABLE, 0x8},
|
||||
};
|
||||
|
||||
/*
|
||||
* Since all transfers are initiated by the probe, no locks are necessary,
|
||||
* as there are no concurrent calls.
|
||||
*/
|
||||
static int tc9563_pwrctrl_i2c_write(struct i2c_client *client,
|
||||
u32 reg_addr, u32 reg_val)
|
||||
{
|
||||
struct i2c_msg msg;
|
||||
u8 msg_buf[7];
|
||||
int ret;
|
||||
|
||||
msg.addr = client->addr;
|
||||
msg.len = 7;
|
||||
msg.flags = 0;
|
||||
|
||||
/* Big Endian for reg addr */
|
||||
put_unaligned_be24(reg_addr, &msg_buf[0]);
|
||||
|
||||
/* Little Endian for reg val */
|
||||
put_unaligned_le32(reg_val, &msg_buf[3]);
|
||||
|
||||
msg.buf = msg_buf;
|
||||
ret = i2c_transfer(client->adapter, &msg, 1);
|
||||
return ret == 1 ? 0 : ret;
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_i2c_read(struct i2c_client *client,
|
||||
u32 reg_addr, u32 *reg_val)
|
||||
{
|
||||
struct i2c_msg msg[2];
|
||||
u8 wr_data[3];
|
||||
u32 rd_data;
|
||||
int ret;
|
||||
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].len = 3;
|
||||
msg[0].flags = 0;
|
||||
|
||||
/* Big Endian for reg addr */
|
||||
put_unaligned_be24(reg_addr, &wr_data[0]);
|
||||
|
||||
msg[0].buf = wr_data;
|
||||
|
||||
msg[1].addr = client->addr;
|
||||
msg[1].len = 4;
|
||||
msg[1].flags = I2C_M_RD;
|
||||
|
||||
msg[1].buf = (u8 *)&rd_data;
|
||||
|
||||
ret = i2c_transfer(client->adapter, &msg[0], 2);
|
||||
if (ret == 2) {
|
||||
*reg_val = get_unaligned_le32(&rd_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If only one message successfully completed, return -EIO */
|
||||
return ret == 1 ? -EIO : ret;
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client,
|
||||
const struct tc9563_pwrctrl_reg_setting *seq,
|
||||
int len)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = tc9563_pwrctrl_i2c_write(client, seq[i].offset, seq[i].val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static DEFINE_IDA(tc9563_pwrctrl_ida);
|
||||
|
||||
static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563,
|
||||
enum tc9563_pwrctrl_ports port)
|
||||
{
|
||||
struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port];
|
||||
const struct tc9563_pwrctrl_reg_setting *seq;
|
||||
const struct reg_sequence *seq;
|
||||
int ret, len;
|
||||
|
||||
if (!cfg->disable_port)
|
||||
|
|
@ -254,12 +175,12 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563,
|
|||
return 0;
|
||||
}
|
||||
|
||||
ret = tc9563_pwrctrl_i2c_bulk_write(tc9563->client, seq, len);
|
||||
ret = regmap_multi_reg_write(tc9563->regmap, seq, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, common_pwroff_seq,
|
||||
ARRAY_SIZE(common_pwroff_seq));
|
||||
return regmap_multi_reg_write(tc9563->regmap, common_pwroff_seq,
|
||||
ARRAY_SIZE(common_pwroff_seq));
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_set_port_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
|
||||
|
|
@ -275,14 +196,13 @@ static int tc9563_pwrctrl_set_port_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9
|
|||
/* convert to units of 256ns */
|
||||
units = ns / TC9563_L0S_L1_DELAY_UNIT_NS;
|
||||
|
||||
ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT,
|
||||
BIT(port));
|
||||
ret = regmap_write(tc9563->regmap, TC9563_PORT_SELECT, BIT(port));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return tc9563_pwrctrl_i2c_write(tc9563->client,
|
||||
is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY,
|
||||
units);
|
||||
return regmap_write(tc9563->regmap,
|
||||
is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY,
|
||||
units);
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
|
||||
|
|
@ -297,8 +217,7 @@ static int tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc95
|
|||
/* convert to units of 256ns */
|
||||
units = ns / TC9563_L0S_L1_DELAY_UNIT_NS;
|
||||
|
||||
ret = tc9563_pwrctrl_i2c_read(tc9563->client, TC9563_EMBEDDED_ETH_DELAY,
|
||||
&rd_val);
|
||||
ret = regmap_read(tc9563->regmap, TC9563_EMBEDDED_ETH_DELAY, &rd_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -309,8 +228,7 @@ static int tc9563_pwrctrl_set_eth_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc95
|
|||
rd_val = u32_replace_bits(rd_val, units,
|
||||
TC9563_ETH_L0S_DELAY_MASK);
|
||||
|
||||
return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_EMBEDDED_ETH_DELAY,
|
||||
rd_val);
|
||||
return regmap_write(tc9563->regmap, TC9563_EMBEDDED_ETH_DELAY, rd_val);
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563,
|
||||
|
|
@ -343,14 +261,14 @@ static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct tc9563_pwrctrl_reg_setting tx_amp_seq[] = {
|
||||
struct reg_sequence tx_amp_seq[] = {
|
||||
{TC9563_PORT_ACCESS_ENABLE, port_access},
|
||||
{TC9563_PORT_LANE_ACCESS_ENABLE, 0x3},
|
||||
{TC9563_TX_MARGIN, amp},
|
||||
};
|
||||
|
||||
return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, tx_amp_seq,
|
||||
ARRAY_SIZE(tx_amp_seq));
|
||||
return regmap_multi_reg_write(tc9563->regmap, tx_amp_seq,
|
||||
ARRAY_SIZE(tx_amp_seq));
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563,
|
||||
|
|
@ -383,7 +301,7 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct tc9563_pwrctrl_reg_setting disable_dfe_seq[] = {
|
||||
struct reg_sequence disable_dfe_seq[] = {
|
||||
{TC9563_PORT_ACCESS_ENABLE, port_access},
|
||||
{TC9563_PORT_LANE_ACCESS_ENABLE, lane_access},
|
||||
{TC9563_DFE_ENABLE, 0x0},
|
||||
|
|
@ -397,15 +315,15 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563,
|
|||
{TC9563_PHY_RATE_CHANGE_OVERRIDE, 0x0},
|
||||
};
|
||||
|
||||
return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, disable_dfe_seq,
|
||||
ARRAY_SIZE(disable_dfe_seq));
|
||||
return regmap_multi_reg_write(tc9563->regmap, disable_dfe_seq,
|
||||
ARRAY_SIZE(disable_dfe_seq));
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl *tc9563,
|
||||
enum tc9563_pwrctrl_ports port)
|
||||
{
|
||||
u8 *nfts = tc9563->cfg[port].nfts;
|
||||
struct tc9563_pwrctrl_reg_setting nfts_seq[] = {
|
||||
struct reg_sequence nfts_seq[] = {
|
||||
{TC9563_NFTS_2_5_GT, nfts[0]},
|
||||
{TC9563_NFTS_5_GT, nfts[1]},
|
||||
};
|
||||
|
|
@ -420,28 +338,51 @@ static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl *tc9563,
|
|||
return 0;
|
||||
}
|
||||
|
||||
ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT,
|
||||
BIT(port));
|
||||
ret = regmap_write(tc9563->regmap, TC9563_PORT_SELECT, BIT(port));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, nfts_seq,
|
||||
ARRAY_SIZE(nfts_seq));
|
||||
return regmap_multi_reg_write(tc9563->regmap, nfts_seq,
|
||||
ARRAY_SIZE(nfts_seq));
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl *tc9563,
|
||||
bool deassert)
|
||||
{
|
||||
int ret, val;
|
||||
int i;
|
||||
|
||||
ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_GPIO_CONFIG,
|
||||
TC9563_GPIO_MASK);
|
||||
if (ret)
|
||||
return ret;
|
||||
for (i = 0; i < ARRAY_SIZE(tc9563->cfg); i++) {
|
||||
int err;
|
||||
|
||||
val = deassert ? TC9563_GPIO_DEASSERT_BITS : 0;
|
||||
if (tc9563->cfg[i].reset) {
|
||||
err = gpiod_direction_output(tc9563->cfg[i].reset,
|
||||
!deassert);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
/* Fallback: legacy DTS without reset-gpios */
|
||||
switch (i) {
|
||||
case TC9563_DSP1:
|
||||
case TC9563_DSP2:
|
||||
err = regmap_clear_bits(tc9563->regmap,
|
||||
TC9563_GPIO_CONFIG,
|
||||
BIT(i + 1));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_RESET_GPIO, val);
|
||||
err = regmap_assign_bits(tc9563->regmap,
|
||||
TC9563_RESET_GPIO,
|
||||
BIT(i + 1), deassert);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_parse_device_dt(struct device_node *node,
|
||||
|
|
@ -476,12 +417,115 @@ static int tc9563_pwrctrl_parse_device_dt(struct device_node *node,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_parse_reset_line(struct tc9563_pwrctrl *tc9563)
|
||||
{
|
||||
enum tc9563_pwrctrl_ports port = TC9563_USP;
|
||||
struct device *dev = tc9563->pwrctrl.dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
|
||||
for_each_child_of_node_scoped(node, child) {
|
||||
struct tc9563_pwrctrl_cfg *cfg;
|
||||
|
||||
if (++port >= TC9563_MAX)
|
||||
break;
|
||||
|
||||
cfg = &tc9563->cfg[port];
|
||||
if (cfg->reset) /* Already discovered */
|
||||
continue;
|
||||
|
||||
cfg->reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(child),
|
||||
"reset", GPIOD_ASIS,
|
||||
NULL);
|
||||
if (IS_ERR(cfg->reset)) {
|
||||
int err = PTR_ERR(cfg->reset);
|
||||
|
||||
cfg->reset = NULL;
|
||||
if (err != -ENOENT)
|
||||
return dev_err_probe(dev, err,
|
||||
"failed to get reset\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc9563_pwrctrl_adev_release(struct device *dev)
|
||||
{
|
||||
struct auxiliary_device *adev = to_auxiliary_dev(dev);
|
||||
|
||||
ida_free(&tc9563_pwrctrl_ida, adev->id);
|
||||
of_node_put(adev->dev.of_node);
|
||||
kfree(adev);
|
||||
}
|
||||
|
||||
static void tc9563_pwrctrl_adev_remove(void *data)
|
||||
{
|
||||
struct auxiliary_device *adev = data;
|
||||
|
||||
auxiliary_device_delete(adev);
|
||||
auxiliary_device_uninit(adev);
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_adev_add(struct device *dev, const char *name,
|
||||
struct device_node *of_node,
|
||||
void *priv_data)
|
||||
{
|
||||
struct auxiliary_device *adev;
|
||||
int id, ret;
|
||||
|
||||
adev = kzalloc_obj(*adev);
|
||||
if (!adev)
|
||||
return -ENOMEM;
|
||||
|
||||
id = ida_alloc(&tc9563_pwrctrl_ida, GFP_KERNEL);
|
||||
if (id < 0) {
|
||||
kfree(adev);
|
||||
return id;
|
||||
}
|
||||
|
||||
adev->id = id;
|
||||
adev->name = name;
|
||||
adev->dev.parent = dev;
|
||||
adev->dev.platform_data = priv_data;
|
||||
adev->dev.release = tc9563_pwrctrl_adev_release;
|
||||
adev->dev.of_node = of_node_get(of_node);
|
||||
dev_set_of_node_reused(&adev->dev);
|
||||
|
||||
ret = auxiliary_device_init(adev);
|
||||
if (ret) {
|
||||
ida_free(&tc9563_pwrctrl_ida, id);
|
||||
of_node_put(adev->dev.of_node);
|
||||
kfree(adev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = auxiliary_device_add(adev);
|
||||
if (ret) {
|
||||
auxiliary_device_uninit(adev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_add_action_or_reset(dev, tc9563_pwrctrl_adev_remove, adev);
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_add_gpio_adev(struct tc9563_pwrctrl *tc9563)
|
||||
{
|
||||
struct device *dev = tc9563->pwrctrl.dev;
|
||||
|
||||
if (!of_property_read_bool(dev->of_node, "gpio-controller") ||
|
||||
!of_property_present(dev->of_node, "#gpio-cells"))
|
||||
return 0;
|
||||
|
||||
return tc9563_pwrctrl_adev_add(dev, TC9563_GPIO_DEV_NAME, dev->of_node,
|
||||
tc9563->regmap);
|
||||
}
|
||||
|
||||
static int tc9563_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl)
|
||||
{
|
||||
struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl,
|
||||
struct tc9563_pwrctrl, pwrctrl);
|
||||
|
||||
gpiod_set_value(tc9563->reset_gpio, 1);
|
||||
gpiod_set_value_cansleep(tc9563->reset_gpio, 1);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(tc9563->supplies), tc9563->supplies);
|
||||
|
||||
|
|
@ -496,12 +540,16 @@ static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
|
|||
struct tc9563_pwrctrl_cfg *cfg;
|
||||
int ret, i;
|
||||
|
||||
ret = tc9563_pwrctrl_parse_reset_line(tc9563);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(tc9563->supplies),
|
||||
tc9563->supplies);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "cannot enable regulators\n");
|
||||
|
||||
gpiod_set_value(tc9563->reset_gpio, 0);
|
||||
gpiod_set_value_cansleep(tc9563->reset_gpio, 0);
|
||||
|
||||
fsleep(TC9563_OSC_STAB_DELAY_US);
|
||||
|
||||
|
|
@ -572,6 +620,13 @@ static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct regmap_config tc9563_regmap_config = {
|
||||
.reg_bits = 24,
|
||||
.val_bits = 32,
|
||||
.reg_format_endian = REGMAP_ENDIAN_BIG,
|
||||
.val_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
static int tc9563_pwrctrl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
|
@ -579,6 +634,7 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
|
|||
enum tc9563_pwrctrl_ports port;
|
||||
struct tc9563_pwrctrl *tc9563;
|
||||
struct device_node *i2c_node;
|
||||
struct i2c_client *client;
|
||||
int ret, addr;
|
||||
|
||||
tc9563 = devm_kzalloc(dev, sizeof(*tc9563), GFP_KERNEL);
|
||||
|
|
@ -595,11 +651,18 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
|
|||
if (!tc9563->adapter)
|
||||
return dev_err_probe(dev, -EPROBE_DEFER, "Failed to find I2C adapter\n");
|
||||
|
||||
tc9563->client = i2c_new_dummy_device(tc9563->adapter, addr);
|
||||
if (IS_ERR(tc9563->client)) {
|
||||
client = devm_i2c_new_dummy_device(dev, tc9563->adapter, addr);
|
||||
if (IS_ERR(client)) {
|
||||
dev_err(dev, "Failed to create I2C client\n");
|
||||
i2c_put_adapter(tc9563->adapter);
|
||||
return PTR_ERR(tc9563->client);
|
||||
ret = PTR_ERR(client);
|
||||
goto remove_i2c;
|
||||
}
|
||||
|
||||
tc9563->regmap = devm_regmap_init_i2c(client, &tc9563_regmap_config);
|
||||
if (IS_ERR(tc9563->regmap)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(tc9563->regmap),
|
||||
"Failed to allocate register map\n");
|
||||
goto remove_i2c;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(tc9563_supply_names); i++)
|
||||
|
|
@ -664,6 +727,10 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
|
|||
tc9563->pwrctrl.power_on = tc9563_pwrctrl_power_on;
|
||||
tc9563->pwrctrl.power_off = tc9563_pwrctrl_power_off;
|
||||
|
||||
ret = tc9563_pwrctrl_add_gpio_adev(tc9563);
|
||||
if (ret)
|
||||
goto remove_i2c;
|
||||
|
||||
ret = devm_pci_pwrctrl_device_set_ready(dev, &tc9563->pwrctrl);
|
||||
if (ret)
|
||||
goto power_off;
|
||||
|
|
@ -673,7 +740,6 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
|
|||
power_off:
|
||||
tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
|
||||
remove_i2c:
|
||||
i2c_unregister_device(tc9563->client);
|
||||
i2c_put_adapter(tc9563->adapter);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -685,7 +751,6 @@ static void tc9563_pwrctrl_remove(struct platform_device *pdev)
|
|||
struct tc9563_pwrctrl, pwrctrl);
|
||||
|
||||
tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
|
||||
i2c_unregister_device(tc9563->client);
|
||||
i2c_put_adapter(tc9563->adapter);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include <linux/isa-dma.h> /* isa_dma_bridge_buggy */
|
||||
#include <linux/init.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dmi.h>
|
||||
|
|
@ -108,7 +109,11 @@ int pcie_failed_link_retrain(struct pci_dev *dev)
|
|||
|
||||
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta);
|
||||
pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &oldlnkctl2);
|
||||
if (!(lnksta & PCI_EXP_LNKSTA_DLLLA) && pcie_lbms_seen(dev, lnksta)) {
|
||||
if (lnksta & PCI_EXP_LNKSTA_DLLLA) {
|
||||
;
|
||||
} else if (PCIE_LNKCTL2_TLS2SPEED(oldlnkctl2) == PCIE_SPEED_2_5GT) {
|
||||
return ret;
|
||||
} else if (pcie_lbms_seen(dev, lnksta)) {
|
||||
pci_info(dev, "broken device, retraining non-functional downstream link at 2.5GT/s\n");
|
||||
ret = pcie_set_target_speed(dev, PCIE_SPEED_2_5GT, false);
|
||||
if (ret)
|
||||
|
|
@ -1941,6 +1946,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4388, quirk_no_msi);
|
|||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4389, quirk_no_msi);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x438a, quirk_no_msi);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x438b, quirk_no_msi);
|
||||
/* ULi M1575 EHCI (10b9:5239): MSI stalls the async schedule */
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, 0x5239, quirk_no_msi);
|
||||
|
||||
static void quirk_pcie_mch(struct pci_dev *pdev)
|
||||
{
|
||||
|
|
@ -2381,6 +2388,24 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
|
|||
DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2000, quirk_plx_pci9050);
|
||||
DECLARE_PCI_FIXUP_HEADER(0x1402, 0x2600, quirk_plx_pci9050);
|
||||
|
||||
/*
|
||||
* An idle downstream port of a PLX/Broadcom PEX 8532/8533/8548 switch
|
||||
* autosuspends into D3hot, and that takes the whole switch down with
|
||||
* it -- any device behind another port of the same switch then sees a
|
||||
* fatal, non-recoverable bus fault on its next access. Keep these
|
||||
* ports out of D3 entirely.
|
||||
*/
|
||||
static void quirk_plx_pex_no_d3(struct pci_dev *dev)
|
||||
{
|
||||
if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
|
||||
dev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
|
||||
pci_info(dev, "avoiding D3 on this downstream port\n");
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PLX, 0x8532, quirk_plx_pex_no_d3);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PLX, 0x8533, quirk_plx_pex_no_d3);
|
||||
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_PLX, 0x8548, quirk_plx_pex_no_d3);
|
||||
|
||||
static void quirk_netmos(struct pci_dev *dev)
|
||||
{
|
||||
unsigned int num_parallel = (dev->subsystem_device & 0xf0) >> 4;
|
||||
|
|
@ -3822,6 +3847,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TI, 0xb005, quirk_no_bus_reset);
|
|||
*/
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ASMEDIA, 0x1164, quirk_no_bus_reset);
|
||||
|
||||
/* Intel DG1 0x4905 may not recover from bus reset */
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x4905, quirk_no_bus_reset);
|
||||
|
||||
static void quirk_no_pm_reset(struct pci_dev *dev)
|
||||
{
|
||||
/*
|
||||
|
|
@ -4104,7 +4132,7 @@ static int nvme_disable_and_flr(struct pci_dev *dev, bool probe)
|
|||
if (probe)
|
||||
return 0;
|
||||
|
||||
bar = pci_iomap(dev, 0, NVME_REG_CC + sizeof(cfg));
|
||||
bar = pci_iomap(dev, 0, NVME_REG_CSTS + sizeof(u32));
|
||||
if (!bar)
|
||||
return -ENOTTY;
|
||||
|
||||
|
|
@ -4241,6 +4269,117 @@ static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET 0x3008
|
||||
#define QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V BIT(0)
|
||||
#define QUALCOMM_WLAN_MHICTRL 0x38
|
||||
#define QUALCOMM_WLAN_MHICTRL_RESET_MASK 0x2
|
||||
|
||||
/*
|
||||
* Qualcomm WLAN device-specific reset using SoC global reset via BAR0
|
||||
* registers.
|
||||
*/
|
||||
static int reset_qualcomm_wlan(struct pci_dev *pdev, bool probe)
|
||||
{
|
||||
void __iomem *bar;
|
||||
u32 val;
|
||||
u16 cmd;
|
||||
int ret;
|
||||
|
||||
if (probe)
|
||||
return 0;
|
||||
|
||||
if (pdev->current_state != PCI_D0)
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
|
||||
pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
|
||||
|
||||
bar = pci_iomap(pdev, 0, 0);
|
||||
if (!bar) {
|
||||
pci_write_config_word(pdev, PCI_COMMAND, cmd);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
val = ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
|
||||
if (PCI_POSSIBLE_ERROR(val)) {
|
||||
ret = -ENODEV;
|
||||
goto out_restore;
|
||||
}
|
||||
val |= QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V;
|
||||
iowrite32(val, bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
|
||||
ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
|
||||
|
||||
msleep(10);
|
||||
|
||||
val &= ~QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET_V;
|
||||
iowrite32(val, bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
|
||||
ioread32(bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
|
||||
|
||||
msleep(10);
|
||||
|
||||
ret = read_poll_timeout(ioread32, val, !PCI_POSSIBLE_ERROR(val),
|
||||
20 * USEC_PER_MSEC, 5 * USEC_PER_SEC, false,
|
||||
bar + QUALCOMM_WLAN_PCIE_SOC_GLOBAL_RESET);
|
||||
if (ret) {
|
||||
pci_err(pdev, "PCIe link failed to recover after reset\n");
|
||||
goto out_restore;
|
||||
}
|
||||
|
||||
/*
|
||||
* After SOC_GLOBAL_RESET, MHISTATUS may still have SYSERR bit set
|
||||
* and thus need to set MHICTRL_RESET to clear SYSERR.
|
||||
*/
|
||||
iowrite32(QUALCOMM_WLAN_MHICTRL_RESET_MASK, bar + QUALCOMM_WLAN_MHICTRL);
|
||||
ioread32(bar + QUALCOMM_WLAN_MHICTRL);
|
||||
|
||||
msleep(10);
|
||||
|
||||
out_restore:
|
||||
pci_iounmap(pdev, bar);
|
||||
pci_write_config_word(pdev, PCI_COMMAND, cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MHI_SOC_RESET_REQ_OFFSET 0xb0
|
||||
#define MHI_SOC_RESET_REQ BIT(0)
|
||||
|
||||
/*
|
||||
* Qualcomm modem device-specific reset using MHI SoC reset via BAR0
|
||||
* register.
|
||||
*/
|
||||
static int reset_qualcomm_modem(struct pci_dev *pdev, bool probe)
|
||||
{
|
||||
void __iomem *bar;
|
||||
u16 cmd;
|
||||
|
||||
if (probe)
|
||||
return 0;
|
||||
|
||||
if (pdev->current_state != PCI_D0)
|
||||
return -EINVAL;
|
||||
|
||||
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
|
||||
pci_write_config_word(pdev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY);
|
||||
|
||||
bar = pci_iomap(pdev, 0, 0);
|
||||
if (!bar) {
|
||||
pci_write_config_word(pdev, PCI_COMMAND, cmd);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
iowrite32(MHI_SOC_RESET_REQ, bar + MHI_SOC_RESET_REQ_OFFSET);
|
||||
ioread32(bar + MHI_SOC_RESET_REQ_OFFSET);
|
||||
|
||||
/* Be sure device reset has been executed */
|
||||
msleep(2000);
|
||||
|
||||
pci_iounmap(pdev, bar);
|
||||
pci_write_config_word(pdev, PCI_COMMAND, cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82599_SFP_VF,
|
||||
reset_intel_82599_sfp_virtfn },
|
||||
|
|
@ -4256,6 +4395,9 @@ static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
|
|||
reset_chelsio_generic_dev },
|
||||
{ PCI_VENDOR_ID_HUAWEI, PCI_DEVICE_ID_HINIC_VF,
|
||||
reset_hinic_vf_dev },
|
||||
{ PCI_VENDOR_ID_QCOM, 0x0308, reset_qualcomm_modem }, /* SDX62/SDX65 modems */
|
||||
{ PCI_VENDOR_ID_QCOM, 0x1103, reset_qualcomm_wlan }, /* WCN6855 WLAN */
|
||||
{ PCI_VENDOR_ID_QCOM, 0x1107, reset_qualcomm_wlan }, /* WCN7850 WLAN */
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
|
|
@ -5608,6 +5750,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x443, quirk_intel_qat_vf_cap);
|
|||
* AMD Neural Processing Unit 0x1502 0x17f0
|
||||
* Intel 82579LM Gigabit Ethernet Controller 0x1502
|
||||
* Intel 82579V Gigabit Ethernet Controller 0x1503
|
||||
* Intel DG1 Discrete Graphics Adapter 0x4905
|
||||
* Mediatek MT7922 802.11ax PCI Express Wireless Network Adapter
|
||||
* Mediatek MT7925 802.11be PCI Express Wireless Network Adapter
|
||||
*/
|
||||
|
|
@ -5623,6 +5766,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1502, quirk_no_flr);
|
|||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x17f0, quirk_no_flr);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_no_flr);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_no_flr);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x4905, quirk_no_flr);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x0616, quirk_no_flr);
|
||||
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_MEDIATEK, 0x7925, quirk_no_flr);
|
||||
|
||||
|
|
@ -6286,6 +6430,10 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0x2303,
|
|||
pci_fixup_pericom_acs_store_forward);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0x2303,
|
||||
pci_fixup_pericom_acs_store_forward);
|
||||
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0xb304,
|
||||
pci_fixup_pericom_acs_store_forward);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb304,
|
||||
pci_fixup_pericom_acs_store_forward);
|
||||
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_PERICOM, 0xb404,
|
||||
pci_fixup_pericom_acs_store_forward);
|
||||
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_PERICOM, 0xb404,
|
||||
|
|
|
|||
|
|
@ -119,12 +119,14 @@ static acpi_status tph_invoke_dsm(acpi_handle handle, u32 cpu_uid,
|
|||
if (!out_obj)
|
||||
return AE_ERROR;
|
||||
|
||||
if (out_obj->type != ACPI_TYPE_BUFFER) {
|
||||
if (out_obj->type != ACPI_TYPE_BUFFER ||
|
||||
out_obj->buffer.length < sizeof(st_out->value) ||
|
||||
!out_obj->buffer.pointer) {
|
||||
ACPI_FREE(out_obj);
|
||||
return AE_ERROR;
|
||||
}
|
||||
|
||||
st_out->value = *((u64 *)(out_obj->buffer.pointer));
|
||||
memcpy(&st_out->value, out_obj->buffer.pointer, sizeof(st_out->value));
|
||||
|
||||
ACPI_FREE(out_obj);
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,8 @@ enum pci_epf_doorbell_type {
|
|||
* For MSI-backed doorbells this is the MSI message, while for
|
||||
* "embedded" doorbells this represents an MMIO write that asserts
|
||||
* an interrupt on the EP side.
|
||||
* @virq: IRQ number of this doorbell message
|
||||
* @virq: IRQ number of this doorbell message. Multiple messages may use the
|
||||
* same IRQ; consumers must request each distinct IRQ only once.
|
||||
* @irq_flags: Required flags for request_irq()/request_threaded_irq().
|
||||
* Callers may OR-in additional flags (e.g. IRQF_ONESHOT).
|
||||
* @type: Doorbell type.
|
||||
|
|
|
|||
|
|
@ -1916,12 +1916,14 @@ static inline int pci_irqd_intx_xlate(struct irq_domain *d,
|
|||
#ifdef CONFIG_PCIEPORTBUS
|
||||
extern bool pcie_ports_disabled;
|
||||
extern bool pcie_ports_native;
|
||||
extern bool pcie_ports_dpc_native;
|
||||
|
||||
int pcie_set_target_speed(struct pci_dev *port, enum pci_bus_speed speed_req,
|
||||
bool use_lt);
|
||||
#else
|
||||
#define pcie_ports_disabled true
|
||||
#define pcie_ports_native false
|
||||
#define pcie_ports_dpc_native false
|
||||
|
||||
static inline int pcie_set_target_speed(struct pci_dev *port,
|
||||
enum pci_bus_speed speed_req,
|
||||
|
|
|
|||
19
include/linux/soc/qcom/tc9563.h
Normal file
19
include/linux/soc/qcom/tc9563.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2026 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Author: Lorenzo Bianconi <lorenzo.bianconi@oss.qualcomm.com>
|
||||
*/
|
||||
|
||||
#ifndef __QCOM_TC9563_H
|
||||
#define __QCOM_TC9563_H
|
||||
|
||||
#define TC9563_GPIO_DEV_NAME "tc9563-gpio"
|
||||
|
||||
#define TC9563_GPIO_IN0_OFFSET 0x801200
|
||||
#define TC9563_GPIO_EN0_OFFSET 0x801208
|
||||
#define TC9563_GPIO_OUT0_OFFSET 0x801210
|
||||
|
||||
#define TC9563_GPIO_CONFIG TC9563_GPIO_EN0_OFFSET
|
||||
#define TC9563_RESET_GPIO TC9563_GPIO_OUT0_OFFSET
|
||||
|
||||
#endif /* __QCOM_TC9563_H */
|
||||
Loading…
Reference in New Issue
Block a user